• Qt音视频开发42-人脸识别客户端


    一、前言

    人脸识别客户端程序,不需要和人脸识别相关的库在一起,而是通过协议通信来和人脸识别服务端通信交互,人脸识别客户端和服务端程序框架,主要是为了提供一套通用的框架,按照定好的协议,实现人脸识别的相关处理,很多厂家都会有也都会做类似的机制,以便第三方厂家或者自家的其他设备按照这个通信协议来处理,比如客户端程序可以在PC机上,也可以是网页,还可以是安卓客户端,前端设备比如人工访客机,访客机本地是不需要做人脸识别等处理的,而是发送到服务端处理完以后再拿到结果进行展示,这样就可以利用服务端强大的运算能力。

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

    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

    五、核心代码

    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, "查找最大人脸");
    }
    
  • 相关阅读:
    python正则表达式基础,以及pattern.match(),re.match(),pattern.search(),re.search()方法的使用和区别
    python正则表达式--分组、后向引用、前(后)向断言
    python正则表达式--flag修饰符、match对象属性
    mybatis-核心配置文件和mappe.xml
    mybatis mapper标签
    spring JdbcTemplate 常用的自定义工具包
    web基础
    8.24 JDBC 调用存储过程
    8.24 事务处理
    8.24 批处理
  • 原文地址:https://www.cnblogs.com/feiyangqingyun/p/13894844.html
Copyright © 2020-2023  润新知