Qt音视频开发42-人脸识别客户端
一、前言
人脸识别客户端程序,不需要和人脸识别相关的库在一起,而是通过协议通信来和人脸识别服务端通信交互,人脸识别客户端和服务端程序框架,主要是为了提供一套通用的框架,按照定好的协议,实现人脸识别的相关处理,很多厂家都会有也都会做类似的机制,以便第三方厂家或者自家的其他设备按照这个通信协议来处理,比如客户端程序可以在PC机上,也可以是网页,还可以是安卓客户端,前端设备比如人工访客机,访客机本地是不需要做人脸识别等处理的,而是发送到服务端处理完以后再拿到结果进行展示,这样就可以利用服务端强大的运算能力。
自定义人脸识别协议功能:
- 离线使用,同时支持百度的离线包和嵌入式linux人脸识别静态库。
- 支持多个连接并发,自动排队处理,返回的时候带上唯一标识区分。
- 传入单张图片返回人脸区域。
- 传入单张图片返回人脸特征值。
- 传入单张图片或者多张图片返回是否是活体。
- 传入两张图片返回比对结果。
- 传入两个特征值返回比对结果。
- 传入单张图片添加人脸。
- 指定唯一标识符删除人脸。
- 传入单张照片返回相似度最大的人脸信息。
- 修改人脸服务的配置参数比如是否快速查找、人脸占比等。
二、功能特点
- 支持的功能包括人脸识别、人脸比对、人脸搜索、活体检测等。
- 在线版还支持身份证、驾驶证、行驶证、银行卡等识别。
- 在线版的协议支持百度、旷视,离线版的支持百度,可定制。
- 除了支持X86架构,还支持嵌入式linux比如contex-A9、树莓派等。
- 每个功能的执行除了返回结果还返回执行用时时间。
- 多线程处理,通过type控制当前处理类型。
- 支持单张图片检索相似度最高的图片。
- 支持指定目录图片用来生成人脸特征值文件。
- 可设置等待处理图片队列中的数量。
- 每次执行都有成功或者失败的信号返回。
- 人脸搜索的返回结果包含了原图+最大相似度图+相似度等。
- 人脸比对同时支持两张图片和两个特征值比对。
- 相关功能自定义一套协议用于客户端和服务端,可以通过TCP通信进行交互。
- 自定义人脸识别协议非常适用于中心一台服务器,现场若干设备请求的场景。
- 每个模块全部是独立的一个类,代码整洁、注释完善。
三、效果图

四、相关站点
- 国内站点:https://gitee.com/feiyangqingyun/QWidgetDemo
- 国际站点:https://github.com/feiyangqingyun/QWidgetDemo
- 个人主页:https://blog.csdn.net/feiyangqingyun
- 知乎主页:https://www.zhihu.com/people/feiyangqingyun/
- 体验地址:https://blog.csdn.net/feiyangqingyun/article/details/97565652
五、核心代码
FaceSdkClient::FaceSdkClient(QObject *parent) : QObject(parent)
{
isOk = false;
timeout = 10;
lastTime = QDateTime::currentDateTime();
//随机生成IP用于测试
clientIP = QString("192.168.1.%1").arg(qrand() % 255);
serverIP = "192.168.1.239";
serverPort = 6662;
//实例化连接对象
tcpSocket = new QTcpSocket(this);
connect(tcpSocket, SIGNAL(connected()), this, SLOT(connected()));
connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(disconnected()));
connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readData()));
//定时器处理数据
timerData = new QTimer(this);
connect(timerData, SIGNAL(timeout()), this, SLOT(checkData()));
timerData->setInterval(100);
//定时器处理重连
timerConn = new QTimer(this);
connect(timerConn, SIGNAL(timeout()), this, SLOT(checkConn()));
timerConn->setInterval(3000);
//定时器发送心跳
timerHeart = new QTimer(this);
connect(timerHeart, SIGNAL(timeout()), this, SLOT(sendHeart()));
timerHeart->setInterval(2000);
}
void FaceSdkClient::connected()
{
isOk = true;
sendHeart();
}
void FaceSdkClient::disconnected()
{
isOk = false;
emit receiveAnaly(serverIP, "服务器断开");
}
void FaceSdkClient::checkConn()
{
QDateTime now = QDateTime::currentDateTime();
if (lastTime.secsTo(now) >= timeout) {
isOk = false;
start();
emit receiveAnaly(serverIP, "重连服务器");
}
}
void FaceSdkClient::readData()
{
lastTime = QDateTime::currentDateTime();
QMutexLocker locker(&mutex);
buffer.append(tcpSocket->readAll());
}
void FaceSdkClient::checkData()
{
QMutexLocker locker(&mutex);
QDomDocument dom;
if (!DeviceFun::getReceiveXmlData(buffer, dom, "IFACE:", 10)) {
return;
}
//逐个取出节点判断数据
emit receiveData(serverIP, dom.toString());
QDomElement element = dom.documentElement();
if (element.tagName() == "FaceServer") {
//查找时间字段属性
QString nowTime = element.attribute("NowTime");
QString year, month, day, hour, min, sec;
DeviceFun::getNowTime(nowTime, year, month, day, hour, min, sec);
//QUIHelper::setSystemDateTime(year, month, day, hour, min, sec);
//根据子节点处理
QDomNode childNode = element.firstChild();
QString name = childNode.nodeName();
QString value = element.text();
//qDebug() << TIMEMS << name << value;
//取出faceID和耗时
element = childNode.toElement();
QString faceID = element.attribute("FaceID");
int msec = element.attribute("TimeUsed").toInt();
if (name == "Msg") {
if (value.startsWith("Init Face Finsh")) {
emit receiveFaceFeatureFinsh();
} else if (value.startsWith("Find Face Rect Fail")) {
emit receiveFaceRectFail(faceID);
} else if (value.startsWith("Find Face Feature Fail")) {
emit receiveFaceFeatureFail(faceID);
} else if (value.startsWith("Find Face Compare Fail")) {
emit receiveFaceCompareFail(faceID);
} else if (value.startsWith("Find Face Live Fail")) {
emit receiveFaceLiveFail(faceID);
} else if (value.startsWith("Image Size Is Too Large")) {
emit receiveAnaly(serverIP, "图片尺寸过大");
} else {
emit receiveAnaly(serverIP, value);
}
} else if (name == "FaceRect") {
QStringList list = value.split(",");
QRect faceRect(list.at(0).toInt(), list.at(1).toInt(), list.at(2).toInt(), list.at(3).toInt());
emit receiveFaceRect(faceID, faceRect, msec);
} else if (name == "FaceFeature") {
QStringList list = value.split("|");
QList<float> feature;
for (int i = 0; i < 256; i++) {
feature << list.at(i).toFloat();
}
emit receiveFaceFeature(faceID, feature, msec);
} else if (name == "FaceLive") {
float result = element.attribute("Result").toFloat();
emit receiveFaceLive(faceID, result, msec);
} else if (name == "FaceCompare") {
float result = element.attribute("Result").toFloat();
emit receiveFaceCompare(faceID, result, msec);
} else if (name == "FaceCompareOne") {
QStringList list = value.split("|");
QImage image1 = DeviceFun::getImage(list.at(0));
QImage image2 = DeviceFun::getImage(list.at(1));
QString targetName = element.attribute("TargetName");
float result = element.attribute("Result").toFloat();
emit receiveFaceCompareOne(faceID, image1, image2, targetName, result, msec);
} else if (name == "FaceConfig") {
QString findFast = element.attribute("FindFast");
QString facePercent = element.attribute("FacePercent");
QString info = QString("模式: %1 比例: %2").arg(findFast == "true" ? "快速模式" : "正常模式").arg(facePercent);
emit receiveAnaly(serverIP, "人脸配置 " + info);
} else {
emit receiveAnaly(serverIP, "心跳回复");
}
}
}
void FaceSdkClient::setClientIP(const QString &clientIP)
{
this->clientIP = clientIP;
}
void FaceSdkClient::setServerIP(const QString &serverIP)
{
this->serverIP = serverIP;
}
void FaceSdkClient::setServerPort(int serverPort)
{
this->serverPort = serverPort;
}
bool FaceSdkClient::start()
{
tcpSocket->abort();
tcpSocket->connectToHost(serverIP, serverPort);
timerData->start();
timerConn->start();
timerHeart->start();
return true;
}
void FaceSdkClient::stop()
{
tcpSocket->disconnectFromHost();
timerData->stop();
timerConn->stop();
timerHeart->stop();
}
void FaceSdkClient::sendData(const QString &body)
{
if (!isOk) {
return;
}
//构建xml字符串
QStringList list;
list.append(QString("<FaceClient DeviceIP=\"%1\">").arg(clientIP));
list.append(body);
list.append("</FaceClient>");
QString data = DeviceFun::getSendXmlData(list.join(""), "IFACE:");
tcpSocket->write(data.toUtf8());
emit sendData(clientIP, data);
}
void FaceSdkClient::sendHeart()
{
if (!isOk) {
return;
}
sendData("<DeviceHeart/>");
emit sendAnaly(clientIP, "发送心跳");
}
void FaceSdkClient::sendFindFace(const QString &faceID, const QImage &image)
{
if (!isOk) {
return;
}
QString data = QString("<FindFace FaceID=\"%1\">%2</FindFace>").arg(faceID).arg(DeviceFun::getImageData(image));
sendData(data);
emit sendAnaly(clientIP, "请求人脸区域");
}
void FaceSdkClient::sendFindFeature(const QString &faceID, const QImage &image)
{
if (!isOk) {
return;
}
QString data = QString("<FindFeature FaceID=\"%1\">%2</FindFeature>").arg(faceID).arg(DeviceFun::getImageData(image));
sendData(data);
emit sendAnaly(clientIP, "请求人脸特征");
}
void FaceSdkClient::sendFindLive(const QString &faceID, const QImage &image)
{
if (!isOk) {
return;
}
QString data = QString("<FindLive FaceID=\"%1\">%2</FindLive>").arg(faceID).arg(DeviceFun::getImageData(image));
sendData(data);
emit sendAnaly(clientIP, "请求活体检测");
}
void FaceSdkClient::sendCompareByImage(const QString &faceID1, const QImage &image1,
const QString &faceID2, const QImage &image2)
{
if (!isOk) {
return;
}
QStringList list;
list.append("<CompareByImage>");
list.append(QString("<FaceImage1 FaceID=\"%1\">%2</FaceImage1>").arg(faceID1).arg(DeviceFun::getImageData(image1)));
list.append(QString("<FaceImage2 FaceID=\"%1\">%2</FaceImage2>").arg(faceID2).arg(DeviceFun::getImageData(image2)));
list.append("</CompareByImage>");
sendData(list.join(""));
emit sendAnaly(clientIP, "请求人脸比对");
}
void FaceSdkClient::sendCompareByFeature(const QString &faceID1, const QString &feature1,
const QString &faceID2, const QString &feature2)
{
if (!isOk) {
return;
}
QStringList list;
list.append("<CompareByFeature>");
list.append(QString("<FaceFeature1 FaceID=\"%1\">%2</FaceFeature1>").arg(faceID1).arg(feature1));
list.append(QString("<FaceFeature2 FaceID=\"%1\">%2</FaceFeature2>").arg(faceID2).arg(feature2));
list.append("</CompareByFeature>");
sendData(list.join(""));
emit sendAnaly(clientIP, "请求人脸比对");
}
void FaceSdkClient::sendAppendFace(const QString &faceID, const QImage &image)
{
if (!isOk) {
return;
}
QString data = QString("<AppendFace FaceID=\"%1\">%2</AppendFace>").arg(faceID).arg(DeviceFun::getImageData(image));
sendData(data);
emit sendAnaly(clientIP, "添加人脸");
}
void FaceSdkClient::sendDeleteFace(const QString &faceID)
{
if (!isOk) {
return;
}
QString data = QString("<DeleteFace FaceID=\"%1\" />").arg(faceID);
sendData(data);
emit sendAnaly(clientIP, "删除人脸");
}
void FaceSdkClient::sendFindByImage(const QString &faceID, const QImage &image)
{
if (!isOk) {
return;
}
QString data = QString("<FindByImage FaceID=\"%1\">%2</FindByImage>").arg(faceID).arg(DeviceFun::getImageData(image));
sendData(data);
emit sendAnaly(clientIP, "查找最大人脸");
}
void FaceSdkClient::sendFindByFeature(const QString &faceID, const QString &feature)
{
if (!isOk) {
return;
}
QString data = QString("<FindByFeature FaceID=\"%1\">%2</FindByFeature>").arg(faceID).arg(feature);
sendData(data);
emit sendAnaly(clientIP, "查找最大人脸");
}
Qt音视频开发42-人脸识别客户端的更多相关文章
- Android IOS WebRTC 音视频开发总结(八十五)-- 使用WebRTC广播网络摄像头视频(下)
本文主要介绍WebRTC (我们翻译和整理的,译者:weizhenwei,校验:blacker),最早发表在[编风网] 支持原创,转载必须注明出处,欢迎关注我的微信公众号blacker(微信ID:bl ...
- WebRTC 音视频开发
WebRTC 音视频开发 webrtc Android IOS WebRTC 音视频开发总结(七八)-- 为什么WebRTC端到端监控很关键? 摘要: 本文主要介绍WebRTC端到端监控(我们翻译 ...
- 转:Android IOS WebRTC 音视频开发总结 (系列文章集合)
随笔分类 - webrtc Android IOS WebRTC 音视频开发总结(七八)-- 为什么WebRTC端到端监控很关键? 摘要: 本文主要介绍WebRTC端到端监控(我们翻译和整理的,译 ...
- Android开发 音视频开发需要了解的专业术语知识
前言 在摸索一段时间的音视频开发后,越来越发现这个坑的深度真是特别的深. 除了了解Android自带的音视频处理API以外,还得了解一些视频与音频方面的知识.这篇博客就是主要讲解这方面的专业术语.内容 ...
- Python音视频开发:消除抖音短视频Logo的图形化工具实现
☞ ░ 前往老猿Python博文目录 ░ 一.引言 在<Python音视频开发:消除抖音短视频Logo和去电视台标的实现详解>节介绍了怎么通过Python+Moviepy+OpenCV实现 ...
- Python音视频开发:消除抖音短视频Logo和去电视台标
☞ ░ 前往老猿Python博文目录 ░ 一.引言 对于带Logo(如抖音Logo.电视台标)的视频,有三种方案进行Logo消除: 直接将对应区域用对应图像替换: 直接将对应区域模糊化: 通过变换将要 ...
- Moviepy音视频开发:视频转gif动画或jpg图片exe图形化工具开发案例
☞ ░ 前往老猿Python博文目录 ░ 一.引言 老猿之所以学习和研究Moviepy的使用,是因为需要一个将视频转成动画的工具,当时在网上到处搜索查找免费使用工具,结果找了很多自称免费的工具,但转完 ...
- 【秒懂音视频开发】02_Windows开发环境搭建
音视频开发库的选择 每个主流平台基本都有自己的音视频开发库(API),用以处理音视频数据,比如: iOS:AVFoundation.AudioUnit等 Android:MediaPlayer.Med ...
- Android音视频开发(1):H264 基本原理
前言 H264 视频压缩算法现在无疑是所有视频压缩技术中使用最广泛,最流行的.随着 x264/openh264 以及 ffmpeg 等开源库的推出,大多数使用者无需再对H264的细节做过多的研究,这大 ...
- Android IOS WebRTC 音视频开发总结(八十三)-- 使用WebRTC广播网络摄像头视频(上)
本文主要介绍WebRTC (我们翻译和整理的,译者:weizhenwei,校验:blacker),最早发表在[编风网] 支持原创,转载必须注明出处,欢迎关注我的微信公众号blacker(微信ID:bl ...
随机推荐
- centos rar文件解压不出
楼主的项目开发是在centos系统上面的,但是经常小伙伴发的压缩文件都是rar格式的. 在centos系统自带的解压的软件并不能解压rar格式的文件: 双击文件时,能弹出解压窗口,可是发现可以解压的更 ...
- python处理大量数据excel表格中间格式神器pickle.pkl文件操作说明
读取写入千万级别的excel文件费时费力,调试起来比较慢,面对这个问题,第一步可以先无脑全部转换成pkl文件,这样几乎和内存操作一样的速度. 例如: t=pd.read_excel("12月 ...
- 干货分享:开启PWM调光之门,一起来做呼吸灯
PWM作为一种灵活且高效的信号调制手段,在电气设备的性能控制和调节中发挥着重要作用,常用于电机控制.灯光调光.音频信号生成.加热控制等应用. 本文将以合宙低功耗4G模组经典型号--Air780E为例, ...
- Wgpu图文详解(03)缓冲区Buffer
在上一篇文章中,我们介绍了Wgpu中的渲染管线与着色器的概念以及基本用法.相信读者还记得,我们在渲染一个三角形的时候,使用了三角形的三个顶点的索引作为了顶点着色器的输入,并根据索引值计算了三个几何顶点 ...
- AI智能学生体测小程序解决方案
引言: 近年来,随着教育理念的提升,对学生综合素质的教育越发重视,特别是越发重视学生的身体素质提升,各阶段的升学考试也将体测纳入考核范围.学校也推出了各种体测锻炼促进手段,今天为您介绍一个基于小程序的 ...
- 我们有40%代码是 AI 写的
- highcharts中的折线图
折现图表的样式如下所示: 整体的一个设置代码如下: that.options = { title: { text: null }, subtitle: { text: null }, yAxis: { ...
- 3-XSS渗透与防御
1.HTTP协议回顾 XSS又名跨站脚本攻击 web页面登陆页面,往往有一个"记住密码"功能 ---> Cookie 1.1 HTTP流程 1.2 HTTP特点: 请求应答模 ...
- 论文泛读《T-Miner: A Generative Approach to Defend Against Trojan Attacks on DNN-based Text Classification》
发表时间:2021 期刊会议:30th USENIX Security Symposium 论文单位:Virginia Tech 论文作者:Ahmadreza Azizi,Ibrahim Asadul ...
- ZCMU-1038
其实感觉不太难,读懂题意就行,我一开始没有仔细去读感觉就很懵.其题目意思就是一段字符串含有数字和'<'或者'>',一开始从左开始遍历,遇到'>'这类东西换方向,如果有多次遇到就删之前 ...