Qt音视频开发39-人脸识别在线版
一、前言
关于人脸识别这块,前些年不要太火,哪怕是到了今天依然火的一塌糊涂,什么玩意都要跟人脸识别搭个边,这东西应该只是人工智能的一个很小的部分,人脸识别光从字面上理解就是识别出人脸区域,其实背后真正的处理是拿到人脸区域图片,提取人脸特征值,再用这些特征值去做比对分析处理,识别出到底是谁,国内厂家也不少,比拼的就是准确度误报率,速度无非就是靠堆硬件来,什么VPU各种并行运算都堆上去,速度杠杠的,好多厂家都做到了几个毫秒的级别,估计很多厂家都是在开源的基础上加上了自家的算法,一直跑呀跑的整出了符合自家算法的人脸模型文件,比如百度的人脸识别模型文件,经过好几年的发展,越来越大越来越细越来越准。
听某个大神说过,很多时候人工智能其实并不是完全的智能,绝大部分都停留在半智能阶段,而且这种半智能阶段还需要借助很多辅助的硬件甚至人为的判断,很多模型库文件的生成就是靠一小小姑娘在那边流水线上类似的不停的点呀点,号称深度学习算法,就是让他识别更多的数据,使得更准确。关于人脸识别或者人工智能,外行一般觉得很科幻,内行一般觉得很绝望,业界领袖和领袖各种打鸡血。
国内的厂家大部分都提供了官网对应的api来进行处理,注册个账号,搞个key,直接就可以撸起来,关于这块技术上没有任何难点,初学者都可以搞定,无非就是先post数据,拿到返回的数据进行解析,要搞清楚的就是如何填充要post的数据,比如带上key,组织其他数据比如图片是base64字符串上传还是二进制文件上传等,返回的数据都是json啦,直接用现成的json库进行解析就ok。
百度人脸识别在线版和离线版SDK的封装:
- 离线版要求支持C++11的编译器,而且必须为MSVC。不支持mingw编译器。
- 在线版中的密钥等信息,务必记得换成自己申请的。
- 离线版本只能在windows上使用。
- 离线版本需要自己申请密钥。找到facebaidusdk文件夹下的LicenseTool.exe,填写后台离线SDK管理中申请到的序列号,单击激活按钮。
- 离线版本对应的动态库和模型文件自行从官网下载。
- 如果源码包中有facebaidusdk+face-resource文件夹则说明带了动态库和模型库文件夹,只需要将facebaidusdk文件夹下的所有文件复制到可执行文件同一目录,face-resource文件夹复制到可执行文件夹目录同等级目录即可。目录位置见snap文件夹下的示例图。
- facebaidusdk目录下的TestFaceApi.exe为百度提供的测试程序,先要将USB摄像头插到电脑上,会实时找人脸框。
二、功能特点
- 支持的功能包括人脸识别、人脸比对、人脸搜索、活体检测等。
- 在线版还支持身份证、驾驶证、行驶证、银行卡等识别。
- 在线版的协议支持百度、旷视,离线版的支持百度,可定制。
- 除了支持X86架构,还支持嵌入式linux比如contex-A9、树莓派等。
- 每个功能的执行除了返回结果还返回执行用时时间。
- 多线程处理,通过type控制当前处理类型。
- 支持单张图片检索相似度最高的图片。
- 支持指定目录图片用来生成人脸特征值文件。
- 可设置等待处理图片队列中的数量。
- 每次执行都有成功或者失败的信号返回。
- 人脸搜索的返回结果包含了原图+最大相似度图+相似度等。
- 人脸比对同时支持两张图片和两个特征值比对。
- 相关功能自定义一套协议用于客户端和服务端,可以通过TCP通信进行交互。
- 自定义人脸识别协议非常适用于中心一台服务器,现场若干设备请求的场景。
- 每个模块全部是独立的一个类,代码整洁、注释完善。
三、效果图
四、相关站点
- 国内站点: https:// gitee.com/feiyangqingyu n/QWidgetDemo
- 国际站点: https:// github.com/feiyangqingy un/QWidgetDemo
- 个人主页: https:// blog.csdn.net/feiyangqi ngyun
- 知乎主页: https://www. zhihu.com/people/feiyan gqingyun/
- 体验地址: https:// blog.csdn.net/feiyangqi ngyun/article/details/97565652
五、核心代码
void FaceWebBaiDu::finished(QNetworkReply *reply)
QString error = reply->errorString();
if (!error.isEmpty() && error != "Unknown error") {
emit receiveError(error);
if (reply->bytesAvailable() > 0 && reply->error() == QNetworkReply::NoError) {
QString data = reply->readAll();
reply->deleteLater();
//发送接收数据信号
emit receiveData(data);
//初始化脚本引擎
QScriptEngine engine;
//构建解析对象
QScriptValue script = engine.evaluate("value=" + data);
//获取鉴权标识符
QString token = script.property("access_token").toString();
if (!token.isEmpty()) {
this->token = token;
emit receiveResult(0, "鉴权标识返回成功");
return;
//通用返回结果字段
int code = script.property("error_code").toInt32();
QString msg = script.property("error_msg").toString();
emit receiveResult(code, msg);
//图片识别部分
QScriptValue result = script.property("result");
if (!result.isNull()) {
//人脸识别
if (data.contains("location")) {
QScriptValue face_list = result.property("face_list");
checkFaceList(face_list);
//人脸比对
if (data.contains("score") && !data.contains("location")) {
QScriptValue score = result.property("score");
float result = score.toString().toFloat();
if (result > 0) {
emit receiveFaceCompare(QRect(), QRect(), result);
} else {
emit receiveFaceCompareFail();
//活体检测
if (data.contains("face_liveness")) {
QScriptValue face_liveness = result.property("face_liveness");
float result = face_liveness.toString().toFloat();
if (result >= 0) {
emit receiveLive(result);
void FaceWebBaiDu::checkFaceList(const QScriptValue &scriptValue)
//创建迭代器逐个解析具体值
QScriptValueIterator it(scriptValue);
while (it.hasNext()) {
it.next();
if (it.flags() & QScriptValue::SkipInEnumeration) {
continue;
QRect rect;
QString face_token = it.value().property("face_token").toString();
if (!face_token.isEmpty()) {
QScriptValue value = it.value().property("location");
rect = FaceHelper::getRect(value);
if (rect.width() > 0) {
emit receiveFaceRect(rect);
break;
void FaceWebBaiDu::getToken()
//具体参见 http://ai.baidu.com/ai-doc/REFERENCE/Ck3dwjhhu
QStringList list;
list.append(QString("grant_type=%1").arg("client_credentials"));
list.append(QString("client_id=%1").arg(key));
list.append(QString("client_secret=%1").arg(secret));
QString data = list.join("&");
QString url = "https://aip.baidubce.com/oauth/2.0/token";
FaceHelper::sendData(manager, url, data);
void FaceWebBaiDu::detect(const QImage &img)
QString imgData = FaceHelper::getImageData2(img);
QStringList list;
list.append(QString("{\"image\":\"%1\",\"image_type\":\"BASE64\"}").arg(imgData));
QString data = list.join("");
QString url = QString("https://aip.baidubce.com/rest/2.0/face/v3/detect?access_token=%1").arg(token);
FaceHelper::sendData(manager, url, data);
void FaceWebBaiDu::compare(const QImage &img1, const QImage &img2)
QString imgData1 = FaceHelper::getImageData2(img1);
QString imgData2 = FaceHelper::getImageData2(img2);
//如果需要活体检测则NONE改为LOW NORMAL HIGH
QStringList list;
list.append("[");
list.append(QString("{\"image\":\"%1\",\"image_type\":\"BASE64\",\"liveness_control\":\"NONE\"}").arg(imgData1));
list.append(",");
list.append(QString("{\"image\":\"%1\",\"image_type\":\"BASE64\",\"liveness_control\":\"NONE\"}").arg(imgData2));
list.append("]");
QString data = list.join("");
QString url = QString("https://aip.baidubce.com/rest/2.0/face/v3/match?access_token=%1").arg(token);
FaceHelper::sendData(manager, url, data);
void FaceWebBaiDu::live(const QImage &img)
QList<QImage> imgs;
if (!img.isNull()) {
imgs << img;
live(imgs);
void FaceWebBaiDu::live(const QList<QImage> &imgs)
//记住最后一次处理的时间,限制频繁的调用
QDateTime now = QDateTime::currentDateTime();
if (lastTime.msecsTo(now) < 500) {
return;
lastTime = now;
QStringList list;
list.append("[");
int count = imgs.count();
for (int i = 0; i < count; i++) {
QString imgData = FaceHelper::getImageData2(imgs.at(i));
list.append(QString("{\"image\":\"%1\",\"image_type\":\"BASE64\"}").arg(imgData));
if (i < count - 1) {
list.append(",");
list.append("]");