Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Qt音视频开发40-人脸识别离线版

Qt音视频开发40-人脸识别离线版

原创
作者头像
feiyangqingyun
修改于 2020-10-27 02:31:39
修改于 2020-10-27 02:31:39
1.1K0
举报
文章被收录于专栏:Qt项目实战Qt项目实战

一、前言

上一篇文章写了在线调用人脸识别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. 每个模块全部是独立的一个类,代码整洁、注释完善。

三、效果图

QQ截图20200504195249.jpg
QQ截图20200504195249.jpg

四、相关站点

  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

五、核心代码

代码语言:txt
AI代码解释
复制
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("\t", "");
    data = data.replace("\"", "");
    data = data.replace(" ", "");

    int index = -1;
    QStringList list = data.split("\n");
    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;
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
M3U8在线播放
当我们在网上愉快观影的时候,难免会遇到“M3U8格式”的视频。聪明的你应该也发现了,它是没办法直接播放的。它其实只是一个索引文件,根据它找到相应的.ts文件再进行播放。而这样做的好处,大概就是做多码率适配,保证视频播放的流畅性。有感兴趣的小伙伴可以参看这里—>M3U8文件格式。
全栈程序员站长
2022/07/02
10.3K0
M3U8在线播放
JQ俄罗斯方块儿
jquery方便简单,下载后受益无穷,今天翻了翻官方网站,发现 Download 页面没有一个能打开的,我想上传一个 jquery 文件,没想到也被人捷足先登了,要31块钱才能下一次。我找了两天,终于找到了一个v3.5.1 的网络地址,大家老方法,拷贝下直接打开就能玩了。 不会拷贝点这里
我不是费圆
2020/10/09
4680
听说最近HTML5很火~~!---贪吃蛇小游戏
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>贪吃蛇</title> <style type="text/css"> *{margin:0;padding: 0;font-family: "Microsoft YaHei";} #page{margin-right: auto;margin-left: auto; margin-top: 20px;height: 600px; width:
赵腰静
2018/03/09
8390
三级侧边栏-树形菜单案例效果
效果图: 本次制作要点: html: 结构上,要把li内部的文字和待展开的元素分开来,便于后期样式上的把控和逻辑上的处理。 1 <div class="sidebar-list"> 2
xing.org1^
2018/05/17
5K0
jQuery 实现图片下载代码jQuery 实现图片下载代码
jQuery 实现图片下载代码 function downloadImage(src) { var $a = $("<a></a>").attr("href", src).attr("download", "meitu.png"); $a[0].click(); } 关键调用downloadImage函数代码 onclick=downloadImage(url) 完整 js 代码 $(function () { $.extend($.fn.bootstrapTable.defaul
一个会写诗的程序员
2018/08/17
7K0
键盘事件 和键码
<script type="text/javascript" language=JavaScript charset="UTF-8"> document.onkeydown=function(event){ var e=event||window.event||arguments.callee.caller.arguments[0]; if(e && e.keyCode==27){ // 按 Esc //要做的事情
连小壮
2018/08/31
1.7K0
键盘事件 和键码
JS打砖块,童年的回忆
打砖块 —— 敲碎屏幕奖励一百块 睿智的程序员,你有想过自己写一个H5小游戏吗? 打砖块大家都不陌生吧,写一个给孩子玩吧! <!DOCTYPE html> <html> <head> <
我不是费圆
2020/09/21
1.4K0
JS打砖块,童年的回忆
js+jquery实现贪吃蛇经典小游戏
恩爸编程
2024/11/27
2570
js+jquery实现贪吃蛇经典小游戏
第61天:json遍历和封装运动框架(多个属性)
 var json = {width:200,height:300,left:50} console.log(json.width); for(var k in json) { console.log(k);   // k 遍历的是json  可以得到的是  属性 console.log(json[k]);  // json[k]  得到 是属性的  值 }
半指温柔乐
2018/09/11
6790
第61天:json遍历和封装运动框架(多个属性)
JavaScript(进阶)
正则用来定义一些字符串的规则,程序可以根据这些规则来判断一个字符串是否符合规则, 也可以将一个字符串中符合规则的内容提取出来。
神葳
2021/01/22
1.6K0
无聊的人用JS实现了一个简单的打地鼠游戏
如果点击颜色比较深的那个(俗称坏老鼠),将扣分50;如果点击颜色比较浅的那个(俗称好老鼠),将得分100
书童小二
2018/09/03
1.9K0
无聊的人用JS实现了一个简单的打地鼠游戏
第53天:鼠标事件、event事件对象
-->鼠标事件 -->event事件对象 -->默认事件 -->键盘事件(keyCode) -->拖拽效果
半指温柔乐
2018/09/11
1.1K0
EasyUI----EasyUI-Tree联想加模糊查询
最近做的项目中用到了EasyUI的Tree,树的结构是这样的,有22个车站,每一个车站相当于一个逻辑域(虚拟域),每一个域下有许多的设备类型,拿我现在做的门禁系统来说,设备类型有门禁主控制器和门禁就地控制器等设备类型,每一种设备类型下面会有多个设备,每一个车站可能会有好几十个设备,22个车站就可能会有上百个或者上千个设备,这样在加载成Tree后,比如说想要查看文锦站的A站口的门禁就地控制器的信息,要是让你一个节点一个节点的打开去找,肯定会烦死的,所以我们就做了一个(联想+模糊)查询。
令仔很忙
2018/09/14
2.5K0
c# winform快捷键实现
你会注意到keyup和kedown事件的修饰符是protected,为什么要用protected呢?是因为窗体的一些输入控件的按键事件是不会传递到窗体的,比如textbox这些,所有需要手动指定一下
冰封一夏
2019/09/10
1.1K0
JavaScript 中提供的所有键盘按键和按键代码
现代浏览器标准中已不再使用keyCode属性,而改用event.key,但是掌握常用按键及其传统keyCode值对我们的日常开发仍会有很大的帮助,本文分享了JavaScript 中提供的所有键盘按键和 keyCode值。
Power
2025/03/31
2720
键盘code表
键盘code表,供自己查询,免去console.log出code,然后再写的麻烦。
老猫-Leo
2023/12/11
2350
js中的事件(event)
我们可以简单的把事件理解为浏览器的感知系统。比如说:他可以感觉到用户是否点击(click)了页面、鼠标是否进入了页面的某个元素上面(mouseover或mouseenter)、鼠标是否离开了网页(mouseout或mouseleave)、浏览器是都加载完了页面上的资源(window.onload)、文档树是否生成(DOMContentLoaded)、键盘上的某个键是否按下(keydown)、鼠标的滚轮是否滚动了等等。
山河木马
2019/03/05
7.3K0
简书搜索自动匹配功能
今天周六,我又来分享知识啦。最近一直在忙项目,所以趁着这个周末,喝着咖啡,听着音乐,敲着代码就把做项目的知识点总结给大家,简直不要太惬意,哈哈。
wblearn
2018/08/27
1.8K0
简书搜索自动匹配功能
前端学习(53)~键盘事件
onmousewheel:鼠标滚轮滚动的事件,会在滚轮滚动时触发。但是火狐不支持该属性。
Vincent-yuan
2020/03/19
1.1K0
增强型的 <input type=number>
input 标签的 number 类型提供了一种处理数字的好方法。 我们可以使用min和max属性设置界限,并且可以通过向上和向下键来添加或减少1,如果设置step属性,则向上或向下键来添加或减少对应的 step 值。 但是,如果我们想让用户以不同的step上下移动,该怎么办?
前端小智@大迁世界
2020/08/25
6900
相关推荐
M3U8在线播放
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档