一、前言

上一篇文章写道人脸识别客户端程序,当然要对应一个服务端程序,客户端才能正常运行,毕竟客户端程序需要与服务端程序进行交互他才能正常工作。通常人脸识别服务端程序需要和人脸识别的相关处理库在一起,这样他接收到相关的处理需求以后比如人脸识别的处理请求,需要调用本地的人脸识别库来处理,处理完成以后拿到结果,再组成协议的格式返回给客户端程序。

自定义人脸识别协议采用的是tcp通信协议,其实也可以改成http协议,这也是大部分厂家的做法,毕竟现在http post非常流行,通用性好,返回个json数据非常规范,本程序目前采用的tcp协议是为了兼容以前的旧的系统,毕竟之前的系统都是按照那个格式定义的,推翻重来那之前写过的很多设备的程序都需要更改,一个人肯定忙不过来,而且之前的程序也是经过大量的现场应用检验过的,非常稳定,一旦改动程序的话有需要很长时间的磨合测试。

自定义人脸识别协议功能:

  1. 离线使用,同时支持百度的离线包和嵌入式linux人脸识别静态库。
  2. 支持多个连接并发,自动排队处理,返回的时候带上唯一标识区分。
  3. 传入单张图片返回人脸区域。
  4. 传入单张图片返回人脸特征值。
  5. 传入单张图片或者多张图片返回是否是活体。
  6. 传入两张图片返回比对结果。
  7. 传入两个特征值返回比对结果。
  8. 传入单张图片添加人脸。
  9. 指定唯一标识符删除人脸。
  10. 传入单张照片返回相似度最大的人脸信息。
  11. 修改人脸服务的配置参数比如是否快速查找、人脸占比等。

二、功能特点

  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 FaceSdkSocket::checkData()
{
QMutexLocker locker(&mutex);
QDomDocument dom;
if (!DeviceFun::getReceiveXmlData(buffer, dom, "IFACE:", 10)) {
return;
} //逐个取出节点判断数据
emit receiveData(clientIP, dom.toString());
QDomElement element = dom.documentElement();
if (element.tagName() == "FaceClient") {
QString deviceIP = element.attribute("DeviceIP");
QDomNode childNode = element.firstChild();
QString name = childNode.nodeName();
QString value = element.text();
//qDebug() << TIMEMS << name << value; if (name == "DeviceHeart") {
sendOk();
emit receiveAnaly(deviceIP, "设备心跳");
} else if (name == "FindFace") {
element = childNode.toElement();
value = element.text();
if (value.length() == 0) {
return;
} //用连接的描述符加上用户传入的描述符组合,方便接收处判断发送给那个连接
QString faceID = element.attribute("FaceID");
QString flag = QString("%1_%2").arg(this->socketDescriptor()).arg(faceID);
QImage image = DeviceFun::getImage(value);
checkImage(image);
emit receiveAnaly(deviceIP, "请求人脸区域"); //发送到人脸识别线程处理
#ifdef facelocal
FaceLocalBaiDu::Instance()->setType(0);
FaceLocalBaiDu::Instance()->append(flag, image);
#elif facearm
FaceLocalArm::Instance()->setType(0);
FaceLocalArm::Instance()->append(flag, image);
#endif
} else if (name == "FindFeature") {
element = childNode.toElement();
value = element.text();
if (value.length() == 0) {
return;
} //用连接的描述符加上用户传入的描述符组合,方便接收处判断发送给那个连接
QString faceID = element.attribute("FaceID");
QString flag = QString("%1_%2").arg(this->socketDescriptor()).arg(faceID);
QImage image = DeviceFun::getImage(value);
checkImage(image);
emit receiveAnaly(deviceIP, "请求人脸特征"); //发送到人脸识别线程处理
#ifdef facelocal
FaceLocalBaiDu::Instance()->setType(1);
FaceLocalBaiDu::Instance()->append(flag, image);
#elif facearm
FaceLocalArm::Instance()->setType(1);
FaceLocalArm::Instance()->append(flag, image);
#endif
} else if (name == "FindLive") {
element = childNode.toElement();
value = element.text();
if (value.length() == 0) {
return;
} //用连接的描述符加上用户传入的描述符组合,方便接收处判断发送给那个连接
QString faceID = element.attribute("FaceID");
QString flag = QString("%1_%2").arg(this->socketDescriptor()).arg(faceID);
QImage image = DeviceFun::getImage(value);
checkImage(image);
emit receiveAnaly(deviceIP, "请求活体检测"); //发送到人脸识别线程处理
#ifdef facelocal
FaceLocalBaiDu::Instance()->setType(5);
FaceLocalBaiDu::Instance()->append(flag, image);
#endif
} else if (name == "CompareByImage") {
//取出子节点
QDomNodeList nodeList = childNode.childNodes();
int nodeCount = nodeList.count();
if (nodeCount == 2) {
QString faceID1, faceID2;
QImage faceImage1, faceImage2;
for (int i = 0; i < nodeCount; i++) {
childNode = nodeList.at(i);
element = childNode.toElement();
name = childNode.nodeName();
value = element.text(); if (name == "FaceImage1") {
faceID1 = element.attribute("FaceID");
faceImage1 = DeviceFun::getImage(value);
} else if (name == "FaceImage2") {
faceID2 = element.attribute("FaceID");
faceImage2 = DeviceFun::getImage(value);
}
} checkImage(faceImage1);
checkImage(faceImage2); QString flag1 = QString("%1_%2").arg(this->socketDescriptor()).arg(faceID1);
QString flag2 = QString("%1_%2").arg(this->socketDescriptor()).arg(faceID2);
emit receiveAnaly(deviceIP, "请求人脸照片比对"); //发送到人脸识别线程处理
#ifdef facelocal
FaceLocalBaiDu::Instance()->setType(2);
FaceLocalBaiDu::Instance()->append(flag1 + "|" + flag2, faceImage1, faceImage2);
#elif facearm
FaceLocalArm::Instance()->setType(2);
FaceLocalArm::Instance()->append(flag1 + "|" + flag2, faceImage1, faceImage2);
#endif
}
} else if (name == "CompareByFeature") {
element = childNode.toElement();
QString faceID = element.attribute("FaceID"); //取出子节点
QDomNodeList nodeList = childNode.childNodes();
int nodeCount = nodeList.count();
if (nodeCount == 2) {
QStringList faceFeature1, faceFeature2;
for (int i = 0; i < nodeCount; i++) {
childNode = nodeList.at(i);
element = childNode.toElement();
name = childNode.nodeName();
value = element.text(); if (name == "FaceFeature1") {
faceFeature1 = value.split("|");
} else if (name == "FaceFeature2") {
faceFeature2 = value.split("|");
}
} emit receiveAnaly(deviceIP, "请求人脸特征比对"); //特征比对速度非常快,忽略不计,立即比对并将结果返回
int count1 = faceFeature1.count();
int count2 = faceFeature2.count();
if (count1 != 256 || count2 != 256) {
sendMsg("faceFeature count != 256");
return;
} QList<float> feature1, feature2;
for (int i = 0; i < 256; i++) {
feature1 << faceFeature1.at(i).toFloat();
feature2 << faceFeature2.at(i).toFloat();
} #ifdef facelocal
float result = FaceLocalBaiDu::Instance()->getFaceCompare(faceID, feature1, feature2);
sendFaceCompare(faceID, result, 1);
#elif facearm
float result = FaceLocalArm::Instance()->getFaceCompare(faceID, feature1, feature2);
sendFaceCompare(faceID, result, 1);
#endif
}
} else if (name == "AppendFace") {
element = childNode.toElement();
value = element.text();
if (value.length() == 0) {
return;
} //用连接的描述符加上用户传入的描述符组合,方便接收处判断发送给那个连接
QString faceID = element.attribute("FaceID");
QString flag = QString("%1_%2").arg(this->socketDescriptor()).arg(faceID);
QImage image = DeviceFun::getImage(value);
checkImage(image);
emit receiveAnaly(deviceIP, "添加人脸"); //发送到人脸识别线程处理
QString file = QString("%1/%2.txt").arg(FACEPATH).arg(faceID);
#ifdef facelocal
FaceLocalBaiDu::Instance()->appendFace(flag, image, file);
#elif facearm
FaceLocalArm::Instance()->appendFace(flag, image, file);
#endif
} else if (name == "DeleteFace") {
element = childNode.toElement();
value = element.text();
if (value.length() == 0) {
return;
} //用连接的描述符加上用户传入的描述符组合,方便接收处判断发送给那个连接
QString faceID = element.attribute("FaceID");
emit receiveAnaly(deviceIP, "删除人脸"); //发送到人脸识别线程处理
#ifdef facelocal
FaceLocalBaiDu::Instance()->deleteFace(faceID);
#elif facearm
FaceLocalArm::Instance()->deleteFace(faceID);
#endif
sendOk();
} else if (name == "FindByImage") {
element = childNode.toElement();
value = element.text();
if (value.length() == 0) {
return;
} //用连接的描述符加上用户传入的描述符组合,方便接收处判断发送给那个连接
QString faceID = element.attribute("FaceID");
QString flag = QString("%1_%2").arg(this->socketDescriptor()).arg(faceID);
QImage image = DeviceFun::getImage(value);
checkImage(image);
emit receiveAnaly(deviceIP, "根据图片查找人脸"); //发送到人脸识别线程处理
#ifdef facelocal
FaceLocalBaiDu::Instance()->setOneImg(flag, image);
#elif facearm
FaceLocalArm::Instance()->setOneImg(flag, image);
#endif
} else if (name == "FindByFeature") {
element = childNode.toElement();
value = element.text();
if (value.length() == 0) {
return;
} //用连接的描述符加上用户传入的描述符组合,方便接收处判断发送给那个连接
QString faceID = element.attribute("FaceID");
QStringList faceFeature = value.split("|");
emit receiveAnaly(deviceIP, "根据特征查找人脸"); int count = faceFeature.count();
if (count != 256) {
return;
} QList<float> feature;
for (int i = 0; i < 256; i++) {
feature << faceFeature.at(i).toFloat();
} //比对速度很快,立即比对并返回结果
QString targetName;
float result;
int msec;
#ifdef facelocal
FaceLocalBaiDu::Instance()->getFaceOne(faceID, feature, targetName, result, msec);
#elif facearm
FaceLocalArm::Instance()->getFaceOne(faceID, feature, targetName, result, msec);
#endif
if (!targetName.isEmpty()) {
QString imageFile = QString("%1/%2").arg(FACEPATH).arg(targetName);
sendFaceCompareOne(faceID, QImage(), QImage(imageFile), targetName, result, msec);
}
} else if (name == "UpdateConfig") {
element = childNode.toElement();
emit receiveAnaly(deviceIP, "修改人脸配置"); bool findFast = (element.attribute("FindFast") == "true" ? true : false);
int facePercent = element.attribute("FacePercent").toInt();
#ifdef facelocal
FaceLocalBaiDu::Instance()->setFindFast(findFast);
FaceLocalBaiDu::Instance()->setPercent(facePercent);
#elif facearm
FaceLocalArm::Instance()->setFindFast(findFast);
FaceLocalArm::Instance()->setPercent(facePercent);
#endif
//立即回复
sendFaceConfig(findFast, facePercent);
emit configChanged(findFast, facePercent);
}
}
} void FaceSdkSocket::checkImage(const QImage &image)
{
//如果照片宽高过大则发出提示到客户端
if (image.width() >= 1280 || image.height() >= 720) {
sendMsg("Image Size Is Too Large");
}
} void FaceSdkSocket::setClientIP(const QString &clientIP)
{
this->clientIP = clientIP;
} QString FaceSdkSocket::getClientIP()
{
return this->clientIP;
} void FaceSdkSocket::sendData(const QString &body)
{
//构建xml字符串
QStringList list;
list.append(QString("<FaceServer TargetIP=\"%1\" NowTime=\"%2\">").arg(clientIP).arg(DATETIME));
list.append(body);
list.append("</FaceServer>"); QString data = DeviceFun::getSendXmlData(list.join(""), "IFACE:");
this->write(data.toUtf8());
emit sendData(clientIP, data);
} void FaceSdkSocket::sendOk()
{
sendData("");
emit sendAnaly(clientIP, "心跳应答");
} void FaceSdkSocket::sendMsg(const QString &msg)
{
sendData(QString("<Msg>%1</Msg>").arg(msg));
emit sendAnaly(clientIP, "提示信息");
} void FaceSdkSocket::sendFaceRect(const QString &faceID, const QString &faceRect, int msec)
{
QString data = QString("<FaceRect FaceID=\"%1\" TimeUsed=\"%3\">%2</FaceRect>")
.arg(faceID).arg(faceRect).arg(msec);
sendData(data);
emit sendAnaly(clientIP, "返回人脸区域");
} void FaceSdkSocket::sendFaceFeature(const QString &faceID, const QString &faceFeature, int msec)
{
QString data = QString("<FaceFeature FaceID=\"%1\" TimeUsed=\"%3\">%2</FaceFeature>")
.arg(faceID).arg(faceFeature).arg(msec);
sendData(data);
emit sendAnaly(clientIP, "返回人脸特征");
} void FaceSdkSocket::sendFaceCompare(const QString &faceID, float result, int msec)
{
QString data = QString("<FaceCompare FaceID=\"%1\" TimeUsed=\"%3\" Result=\"%2\" />")
.arg(faceID).arg(result).arg(msec);
sendData(data);
emit sendAnaly(clientIP, "返回人脸比对结果");
} void FaceSdkSocket::sendFaceCompareOne(const QString &faceID, const QImage &sourceImage, const QImage &targetImage, const QString &targetName, float result, int msec)
{
QString data = QString("<FaceCompareOne FaceID=\"%1\" TimeUsed=\"%6\" Result=\"%2\" TargetName=\"%3\">%4|%5</FaceCompareOne>")
.arg(faceID).arg(result).arg(targetName).arg(DeviceFun::getImageData(sourceImage)).arg(DeviceFun::getImageData(targetImage)).arg(msec);
sendData(data);
emit sendAnaly(clientIP, "返回最相似人脸");
}

Qt音视频开发43-人脸识别服务端的更多相关文章

  1. Android IOS WebRTC 音视频开发总结(八十五)-- 使用WebRTC广播网络摄像头视频(下)

    本文主要介绍WebRTC (我们翻译和整理的,译者:weizhenwei,校验:blacker),最早发表在[编风网] 支持原创,转载必须注明出处,欢迎关注我的微信公众号blacker(微信ID:bl ...

  2. WebRTC 音视频开发

    WebRTC 音视频开发 webrtc   Android IOS WebRTC 音视频开发总结(七八)-- 为什么WebRTC端到端监控很关键? 摘要: 本文主要介绍WebRTC端到端监控(我们翻译 ...

  3. 转:Android IOS WebRTC 音视频开发总结 (系列文章集合)

    随笔分类 - webrtc   Android IOS WebRTC 音视频开发总结(七八)-- 为什么WebRTC端到端监控很关键? 摘要: 本文主要介绍WebRTC端到端监控(我们翻译和整理的,译 ...

  4. Android IOS WebRTC 音视频开发总结(六)-- iOS开发之含泪经验

    前段时间在搞webrtc iOS开发,所以将标题改为了Android IOS WebRTC 音视频开发总结, 下面都是开发过程中的经验总结,转载请说明出处(博客园RTC.Blacker): 1. IO ...

  5. Python音视频开发:消除抖音短视频Logo的图形化工具实现

    ☞ ░ 前往老猿Python博文目录 ░ 一.引言 在<Python音视频开发:消除抖音短视频Logo和去电视台标的实现详解>节介绍了怎么通过Python+Moviepy+OpenCV实现 ...

  6. Python音视频开发:消除抖音短视频Logo和去电视台标

    ☞ ░ 前往老猿Python博文目录 ░ 一.引言 对于带Logo(如抖音Logo.电视台标)的视频,有三种方案进行Logo消除: 直接将对应区域用对应图像替换: 直接将对应区域模糊化: 通过变换将要 ...

  7. Moviepy音视频开发:视频转gif动画或jpg图片exe图形化工具开发案例

    ☞ ░ 前往老猿Python博文目录 ░ 一.引言 老猿之所以学习和研究Moviepy的使用,是因为需要一个将视频转成动画的工具,当时在网上到处搜索查找免费使用工具,结果找了很多自称免费的工具,但转完 ...

  8. 【秒懂音视频开发】02_Windows开发环境搭建

    音视频开发库的选择 每个主流平台基本都有自己的音视频开发库(API),用以处理音视频数据,比如: iOS:AVFoundation.AudioUnit等 Android:MediaPlayer.Med ...

  9. Android音视频开发(1):H264 基本原理

    前言 H264 视频压缩算法现在无疑是所有视频压缩技术中使用最广泛,最流行的.随着 x264/openh264 以及 ffmpeg 等开源库的推出,大多数使用者无需再对H264的细节做过多的研究,这大 ...

  10. Android IOS WebRTC 音视频开发总结(八十三)-- 使用WebRTC广播网络摄像头视频(上)

    本文主要介绍WebRTC (我们翻译和整理的,译者:weizhenwei,校验:blacker),最早发表在[编风网] 支持原创,转载必须注明出处,欢迎关注我的微信公众号blacker(微信ID:bl ...

随机推荐

  1. KubeSphere 在互联网医疗行业的应用实践

    作者:宇轩辞白,运维研发工程师,目前专注于云原生.Kubernetes.容器.Linux.运维自动化等领域. 前言 2020 年我国互联网医疗企业迎来了"爆发元年",越来越多居民在 ...

  2. 运营商业务系统基于 KubeSphere 的容器化实践

    本篇文章是 KubeSphere 2020 年度 Meetup 上讲师宋磊分享内容整理而成. 大家好,我是宋磊,在运营商的一个科技子公司任职,主要做大数据业务.我主要负责公司的 IaaS 层和 Paa ...

  3. .NET + 微信小程序开源多功能电商系统

    前言 推荐一款基于微信小程序.LayUI 和 .NET 平台的多功能电商系统,支持二次开发和扩展,帮助大家轻松快速搭建一个功能全面且易于管理的在线商城. 项目介绍 该项目不仅包含了微信小程序前端,还配 ...

  4. spring boot下跨域安全配置

    1 @Bean 2 public FilterRegistrationBean corsFilter() { 3 final UrlBasedCorsConfigurationSource sourc ...

  5. 搞人工智能开源大语言模型GPT2、Llama的正确姿势

    (如果想及时收到人工智能相关的知识更新,请点击关注!!) 序言:目前我们每一小节的内容都讲解得非常慢,因为这是人工智能研发中的最基础知识.如果我们不能扎实掌握这些知识,将很难理解后续更复杂且实用的概念 ...

  6. 强化学习算法——TPG算法(遗传编程GP算法)代码

    tpg算法是一个使用模块涌现和复用机制的遗传编程(GP)算法,该算法在一些强化学习问题上有着不错的表现,本文给出该算法的项目地址. tpg算法的C++实现代码大概有1万的逻辑代码,如果这个比例换做使用 ...

  7. 案例分享-导致MySQL崩溃的SQL语句

    背景 周一刚上班一个开发小哥火急火燎的过来找我,黑龙江某客户私有化环境的服务过一阵就报数据库连接失败,不知道是什么原因导致的,我以为是客户调整了网络,但是客户说并没有做任何调整,我使用ping测试也看 ...

  8. NewStar CTF 2024 Crypto

    Week1 xor #As a freshman starting in 2024, you should know something about XOR, so this task is for ...

  9. linux下文件夹文件名称最大长度

    今天突发奇想,如果创建一个文件,不写入内容,就如我们之前说的写入扩展属性能快速查找数据,但是在SSD下只能写4000个左右的字符,那么有没有更快速的方法存储这样的信息呢? 我想到可以同文件名来存储信息 ...

  10. VS中使用Qt方法详解

    相信大家都知道在 Qt Creator 中可以使用 MSVC 编译工具对 Qt 项目进行编译.若有人比较习惯于使用 Visual Studio,或某些项目必须使用 Visual Studio,也可以在 ...