zoukankan      html  css  js  c++  java
  • Qt音视频开发40-人脸识别离线版

    一、前言

    上一篇文章写了在线调用人脸识别api进行处理,其实很多的客户需求是要求离线使用的,尤其是一些事业单位,严禁这些刷脸数据外泄上传到服务器,尽管各个厂家号称严格保密这些数据,但要阻止这些担心,唯一的解决办法就是设备离线使用,连个屁的网,不联网看你怎么上传,于是离线的人脸识别应用应运而生,比如我们手机上的识别就是本地库在运算,至于本地模型库估计会联网更新,以保持最新的状态。百度的离线人脸识别做的还行,看官网的sdk开发包,更新也是蛮快的,提供了windows、linux、android等版本。

    本篇文章采用的百度离线SDK作为解决方案。可以去官网申请,默认有6个免费的密钥使用三个月,需要与本地设备的指纹信息匹配,感兴趣的同学可以自行去官网下载SDK。百度离线人脸识别SDK文件比较大,光模型文件就645MB,估计这也许是识别率比较高的一方面原因吧,不断训练得出的模型库,本篇文章只放出Qt封装部分源码。官网对应的使用说明还是非常详细的,只要是学过编程的人就可以看懂。

    处理流程:

    1. 实例化BaiduFaceApi类,调用sdk_init初始化。
    2. 调用is_auth判断授权是否成功,成功了才能继续。
    3. 设置最小人脸比例(set_min_face_size)、光照阈值(set_illum_thr)等参数。
    4. 调用track_max_face函数获取人脸区域。
    5. 调用rgb_liveness_check函数进行活体检测。
    6. 调用get_face_feature函数提取特征值。
    7. 调用compare_feature函数进行人脸比对。

    百度人脸识别在线版和离线版SDK的封装:

    1. 离线版要求支持C++11的编译器,而且必须为MSVC。不支持mingw编译器。
    2. 在线版中的密钥等信息,务必记得换成自己申请的。
    3. 离线版本只能在windows上使用。
    4. 离线版本需要自己申请密钥。找到facebaidusdk文件夹下的LicenseTool.exe,填写后台离线SDK管理中申请到的序列号,单击激活按钮。
    5. 离线版本对应的动态库和模型文件自行从官网下载。
    6. 如果源码包中有facebaidusdk+face-resource文件夹则说明带了动态库和模型库文件夹,只需要将facebaidusdk文件夹下的所有文件复制到可执行文件同一目录,face-resource文件夹复制到可执行文件夹目录同等级目录即可。目录位置见snap文件夹下的示例图。
    7. facebaidusdk目录下的TestFaceApi.exe为百度提供的测试程序,先要将USB摄像头插到电脑上,会实时找人脸框。

    二、功能特点

    1. 支持的功能包括人脸识别、人脸比对、人脸搜索、活体检测等。
    2. 在线版还支持身份证、驾驶证、行驶证、银行卡等识别。
    3. 在线版的协议支持百度、旷视,离线版的支持百度,可定制。
    4. 除了支持X86架构,还支持嵌入式linux比如contex-A9、树莓派等。
    5. 每个功能的执行除了返回结果还返回执行用时时间。
    6. 多线程处理,通过type控制当前处理类型。
    7. 支持单张图片检索相似度最高的图片。
    8. 支持指定目录图片用来生成人脸特征值文件。
    9. 可设置等待处理图片队列中的数量。
    10. 每次执行都有成功或者失败的信号返回。
    11. 人脸搜索的返回结果包含了原图+最大相似度图+相似度等。
    12. 人脸比对同时支持两张图片和两个特征值比对。
    13. 相关功能自定义一套协议用于客户端和服务端,可以通过TCP通信进行交互。
    14. 自定义人脸识别协议非常适用于中心一台服务器,现场若干设备请求的场景。
    15. 每个模块全部是独立的一个类,代码整洁、注释完善。

    三、效果图

    四、相关站点

    1. 国内站点:https://gitee.com/feiyangqingyun/QWidgetDemo
    2. 国际站点:https://github.com/feiyangqingyun/QWidgetDemo
    3. 个人主页:https://blog.csdn.net/feiyangqingyun
    4. 知乎主页:https://www.zhihu.com/people/feiyangqingyun/
    5. 体验地址:https://blog.csdn.net/feiyangqingyun/article/details/97565652

    五、核心代码

    void FaceLocalBaiDu::init()
    {
        //如果已经正常则无需初始化
        if (isOk) {
            return;
        }
    
        int res = api->sdk_init();
        res = api->is_auth();
        if (res != 1) {
            qDebug() << TIMEMS << QString("init sdk error: %1").arg(res);
        } else {
            //设置最小人脸,默认30
            api->set_min_face_size(percent);
            //设置光照阈值,默认40
            api->set_illum_thr(20);
            //设置角度阈值,默认15
            //api->set_eulur_angle_thr(30, 30, 30);
            isOk = true;
            qDebug() << TIMEMS << "init sdk ok";
        }
    
        emit sdkInitFinsh(isOk);
    }
    
    bool FaceLocalBaiDu::getFaceRect(const QString &flag, const QImage &img, QRect &rect, int &msec)
    {
        //qDebug() << TIMEMS << flag << "getFaceRect";
    
        QTime time;
        if (countTime) {
            time.start();
        }
    
        faces->clear();
        QByteArray imageData = FaceHelper::getImageData(img);
        int result = api->track_max_face(faces, imageData.constData(), 1);
    
        if (result == 1) {
            TrackFaceInfo info = faces->at(0);
            FaceInfo ibox = info.box;
            float width = ibox.mWidth;
            float x = ibox.mCenter_x;
            float y = ibox.mCenter_y;
            rect = QRect(x - width / 2, y - width / 2, width, width);
            msec = getTime(time);
            return true;
        }
    
        return false;
    }
    
    bool FaceLocalBaiDu::getFaceLive(const QString &flag, const QImage &img, float &result, int &msec)
    {
        //qDebug() << TIMEMS << flag << "getFaceLive";
    
        QTime time;
        if (countTime) {
            time.start();
        }
    
        result = 0;
        QByteArray imageData = FaceHelper::getImageData(img);
        std::string value = api->rgb_liveness_check(imageData.constData(), 1);
    
        QString data = value.c_str();
        data = data.replace("	", "");
        data = data.replace(""", "");
        data = data.replace(" ", "");
    
        int index = -1;
        QStringList list = data.split("
    ");
        foreach (QString str, list) {
            index = str.indexOf("score:");
            if (index >= 0) {
                result = str.mid(6, 4).toFloat();
                break;
            }
        }
    
        if (index >= 0) {
            msec = getTime(time);
            return true;
        }
    
        return false;
    }
    
    bool FaceLocalBaiDu::getFaceFeature(const QString &flag, const QImage &img, QList<float> &feature, int &msec)
    {
        //qDebug() << TIMEMS << flag << "getFaceFeature" << img.width() << img.height() << img.size();
    
        QTime time;
        if (countTime) {
            time.start();
        }
    
        const float *fea = nullptr;
        QByteArray imageData = FaceHelper::getImageData(img);
        int result = api->get_face_feature(imageData.constData(), 1, fea);
    
        if (result == 512) {
            feature.clear();
            for (int i = 0; i < 512; i++) {
                feature.append(fea[i]);
            }
    
            msec = getTime(time);
            return true;
        }
    
        return false;
    }
    
    float FaceLocalBaiDu::getFaceCompare(const QString &flag, const QList<float> &feature1, const QList<float> &feature2)
    {
        //qDebug() << TIMEMS << flag << "getFaceCompareXXX";
    
        std::vector<float> fea1, fea2;
        for (int i = 0; i < 512; i++) {
            fea1.push_back(feature1.at(i));
            fea2.push_back(feature2.at(i));
        }
    
        float result = api->compare_feature(fea1, fea2);
        //过滤非法的值
        result = result > 100 ? 0 : result;
        return result;
    }
    
    bool FaceLocalBaiDu::getFaceCompare(const QString &flag, const QImage &img1, const QImage &img2, float &result, int &msec)
    {
        //qDebug() << TIMEMS << flag << "getFaceCompare";
    
        result = 0;
        bool ok1, ok2;
        QList<float> feature1, feature2;
        int msec1, msec2;
        QString flag1, flag2;
        if (flag.contains("|")) {
            QStringList list = flag.split("|");
            flag1 = list.at(0);
            flag2 = list.at(1);
        } else {
            flag1 = flag;
            flag2 = flag;
        }
    
        QTime time;
        if (countTime) {
            time.start();
        }
    
        ok1 = getFaceFeature(flag1, img1, feature1, msec1);
        if (ok1) {
            emit receiveFaceFeature(flag1, feature1, msec1);
        }
    
        ok2 = getFaceFeature(flag2, img2, feature2, msec2);
        if (ok2) {
            emit receiveFaceFeature(flag2, feature2, msec2);
        }
    
        if (ok1 && ok2) {
            result = getFaceCompare(flag, feature1, feature2);
            msec = getTime(time);
            return true;
        }
    
        return false;
    }
    
  • 相关阅读:
    IM设计思考:XMPP多用户文本聊天协议(MUC:Multi User Chat)
    软件设计中的“三视图”
    [C++] 应该如何应对OOM?
    Android 上Camera分析
    android的语言切换应用程序 根据 不同的语言 加载不同的 res
    布局
    布局
    如何解决"Launching New_configuration"问题
    android的语言切换应用程序 根据 不同的语言 加载不同的 res
    如何解决"Launching New_configuration"问题
  • 原文地址:https://www.cnblogs.com/feiyangqingyun/p/13883090.html
Copyright © 2011-2022 走看看