zoukankan      html  css  js  c++  java
  • Qt音视频开发41-人脸识别嵌入式

    一、前言

    大概几年前搞过一套嵌入式linux上的人脸识别程序,当然人脸识别的核心算法并不是自己开发的,关于人脸识别算法这一块,虽然有众多的开源库可以用,甚至还可以用opencv搞算法训练深度学习之类的,个人认为始终达不到准确度的要求,尤其是人脸比对的准确度,这个需要专业的人脸训练模型才行。目前市面上绝大部分的人脸识别库提供的都是X86的或者安卓ios的库,并没有嵌入式linux的库,估计一方面因为嵌入式linux跑的板子性能比较低,还有一个就是依赖特定编译器,版本众多难以提供,市场也小,所以大部分的厂家都没有提供嵌入式linux的开发包,这个就比较鸡肋,所以很多终端厂家最终弃用linux而选用安卓作为载体系统,这样就可以用上高大上的人脸识别库了,比如萤火虫开发板,RK3288 RK3399等。

    记得当时还特意搞了一整套的非常详细的通信协议,产品也初步成型,大概的设备有人脸识别终端、双目门禁、人工访客机、自助访客机、人脸比对服务器等,也试运行了一些小区,效果还行,不过在抗逆光和晚上的情况下效果不是很好,当然这是所有人脸识别设备的通病,必须依赖补光或者调整安装位置增加抗逆光摄像机来处理,这样一来对施工就有要求了增加了复杂度,设备成本也上来了,对于小终端厂商来说,这个要选择一个平衡点才行,只有用户愿意付出对应的成本才提供对应的版本。

    通信方式及端口:

    • 客户端和服务端等设备统一提供web访问修改配置,端口6660。
    • 人工访客机客户端与人工访客机服务端通信采用TCP短连接,通信端口6661。
    • 自助访客机客户端与自助访客机服务端通信采用TCP长连接,通信端口6661。
    • 人脸识别比对数据库服务器采用TCP长连接,通信端口6662。
    • 服务端与数据库服务器通信采用TCP长连接,通信端口6666。
    • 数据库服务器下发人脸通行证数据采用TCP短连接,通信端口6667。
    • 电脑PC端下发配置到双目门禁采用TCP短连接,通信端口6668。
    • 双目门禁与数据库服务器通信采用TCP长连接,通信端口6669。
    • 双目门禁电脑客户端升级通信采用TCP短连接,通信端口6670。
    • 检测测试与手机app或者其他客户端通信采用TCP长连接,通信端口6671。

    二、功能特点

    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 FaceLocalArm::imgToBgr(const QImage &img, quint8 *bgr, int w, int h)
    {
        for (int i = 0; i < h; ++i) {
            const quint8 *scanline = img.scanLine(i);
            for (int j = 0; j < w; ++j) {
                *bgr++ = scanline[j * 3 + 0];
                *bgr++ = scanline[j * 3 + 1];
                *bgr++ = scanline[j * 3 + 2];
            }
        }
    }
    
    void FaceLocalArm::bgrToYuv(quint8 *yuv, const quint8 *bgr, int w, int h)
    {
        int b, g, r;
        for (int i = 0; i < w * h; ++i) {
            b = bgr[3 * i + 0];
            g = bgr[3 * i + 1];
            r = bgr[3 * i + 2];
            yuv[i] = (quint8)((r * 30 + g * 59 + b * 11 + 50) / 100);
        }
    }
    
    void FaceLocalArm::init()
    {
        //如果已经正常则无需初始化
        if (isOk) {
            return;
        }
    #ifdef __arm__
        int res = CRface::FaceDetect_Init_ColorReco(sdkPath.toStdString());
        if (res != 1) {
            qDebug() << TIMEMS << QString("FaceDetect_Init_ColorReco error: %1").arg(res);
        } else {
            qDebug() << TIMEMS << "FaceDetect_Init_ColorReco ok";
            res = CRface::FaceReco_Init_ColorReco(sdkPath.toStdString());
            if (res != 1) {
                qDebug() << TIMEMS << QString("FaceReco_Init_ColorReco error: %1").arg(res);
            } else {
                isOk = true;
                qDebug() << TIMEMS << "FaceReco_Init_ColorReco ok";
            }
        }
    
        emit sdkInitFinsh(isOk);
    #endif
    }
    
    bool FaceLocalArm::getFaceRect(const QString &flag, const QImage &img, QRect &rect, int &msec)
    {
        if (!isOk) {
            return false;
        }
    #ifdef __arm__
        //qDebug() << TIMEMS << flag << "getFaceRect";
    
        QTime time;
        if (countTime) {
            time.start();
        }
    
        int w = img.width();
        int h = img.height();
    
        //这里有隐患,如果图片像素特别大会崩溃,应该改为quint8 *bgr=(quint8 *)calloc(w * h * 3, 1);然后后面free(bgr);
        quint8 yuv[w * h];
        quint8 bgr[w * h * 3];
        imgToBgr(img, bgr, w, h);
    
        int facebox[32 * 5];
        bgrToYuv(yuv, bgr, w, h);
        facebox[0] = 0;
        int result = 0;
        if (findFast) {
            result = CRface::FaceDetect_Fast_ColorReco(yuv, w, h, facebox, true);
        } else {
            result = CRface::FaceDetect_Normal_ColorReco(yuv, w, h, facebox, true, w / percent);
        }
    
        if (result == 1) {
            rect = QRect(facebox[1], facebox[2], facebox[3], facebox[4]);
            msec = getTime(time);
            return true;
        }
    #endif
        return false;
    }
    
    bool FaceLocalArm::getFaceFeature(const QString &flag, const QImage &img, QList<float> &feature, int &msec)
    {
        if (!isOk) {
            return false;
        }
    #ifdef __arm__
        //qDebug() << TIMEMS << flag << "getFaceFeature" << img.width() << img.height() << img.size();
    
        QTime time;
        if (countTime) {
            time.start();
        }
    
        int w = img.width();
        int h = img.height();
    
        //这里有隐患,如果图片像素特别大会崩溃,应该改为quint8 *bgr=(quint8 *)calloc(w * h * 3, 1);然后后面free(bgr);
        quint8 yuv[w * h];
        quint8 bgr[w * h * 3];
        imgToBgr(img, bgr, w, h);
    
        int facebox[32 * 5];
        bgrToYuv(yuv, bgr, w, h);
        facebox[0] = 0;
        int result = 0;
        if (findFast) {
            result = CRface::FaceDetect_Fast_ColorReco(yuv, w, h, facebox, true);
        } else {
            result = CRface::FaceDetect_Normal_ColorReco(yuv, w, h, facebox, true, w / percent);
        }
    
        if (result == 1) {
            QRect rect = QRect(facebox[1], facebox[2], facebox[3], facebox[4]);
            msec = getTime(time);
            emit receiveFaceRect(flag, rect, msec);
    
            float fea[256];
            int result = CRface::FaceReco_Extract_ColorReco(bgr, w, h, facebox + 1, fea);
    
            if (result == 256) {
                feature.clear();
                for (int i = 0; i < 256; i++) {
                    feature.append(fea[i]);
                }
    
                msec = getTime(time);
                return true;
            }
        } else {
            emit receiveFaceRectFail(flag);
        }
    #endif
        return false;
    }
    
    float FaceLocalArm::getFaceCompare(const QString &flag, const QList<float> &feature1, const QList<float> &feature2)
    {
        if (!isOk) {
            return 0;
        }
    #ifdef __arm__
        //qDebug() << TIMEMS << flag << "getFaceCompareXXX";
    
        float fea1[256], fea2[256];
        for (int i = 0; i < 256; i++) {
            fea1[i] = feature1.at(i);
            fea2[i] = feature2.at(i);
        }
    
        float result = CRface::FaceReco_Match_ColorReco(fea1, fea2);
        result = result * 100;
        //过滤非法的值
        result = result > 100 ? 0 : result;
        return result;
    #endif
    
        return 0;
    }
    
  • 相关阅读:
    SVN版本库备份和恢复
    Jira的安装使用
    前端CSS的选择器整理搜集 s
    事务复制在不重新初始化的情况下添加新表
    没有共享存储和仲裁盘的SQL Server 2012HADR故障手动切换TSQL
    SQLSERVER聚集索引的整理(重建)的必要性测试
    sqlserver字符串与表格互相转换的函数和方法
    当一个DBA离职时,如何清除掉数据库里属于他的那些“东西”
    SQLSERVER复制订阅中的数据库版本选择
    1979年12月3日世界协调时间取代格林威治准时间
  • 原文地址:https://www.cnblogs.com/feiyangqingyun/p/13889036.html
Copyright © 2011-2022 走看看