• Qt音视频开发43-人脸识别服务端


    一、前言

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

    自定义人脸识别协议采用的是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, "返回最相似人脸");
    }
    
  • 相关阅读:
    成长
    mui组件通用CSS类
    CSS推荐的类名
    必须认识的http请求包
    Bootstrap技术: 如何给nav导航组件的tab页增加关闭按钮以及动态的添加和关闭tab页
    bootstrap 关闭tabs
    typescirpt 知识点
    手动使用gulp4.0配合rollup搭建typescript 写一个自己的一个开源库(起步 构建自动化处理,热更新浏览器)
    Wuss Weapp 一款高质量,组件齐全,高自定义的微信小程序 UI 组件库
    wussUI v1.0.0小程序UI组件库 第一期开发已完成
  • 原文地址:https://www.cnblogs.com/feiyangqingyun/p/13901682.html
Copyright © 2020-2023  润新知