zoukankan      html  css  js  c++  java
  • 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, "返回最相似人脸");
    }
    
  • 相关阅读:
    UVa 12174 (滑动窗口) Shuffle
    UVa 1607 (二分) Gates
    CodeForces ZeptoLab Code Rush 2015
    HDU 1525 (博弈) Euclid's Game
    HDU 2147 (博弈) kiki's game
    UVa 11093 Just Finish it up
    UVa 10954 (Huffman 优先队列) Add All
    CodeForces Round #298 Div.2
    UVa 12627 (递归 计数 找规律) Erratic Expansion
    UVa 714 (二分) Copying Books
  • 原文地址:https://www.cnblogs.com/feiyangqingyun/p/13901682.html
Copyright © 2011-2022 走看看