2

简介

本文主要会对虹软人脸识别SDK在 Qt 平台下的使用过程做简要介绍,其中包含材料准备、环境搭建、代码实现三个主要步骤,帮助我们有过程上的参考。

开发环境 : win10 Qt5.11.2(Mingw 32位)


材料准备

  • 人脸识别SDK(ArcSoft_ArcFace)下载

虹软对外有免费的AI开发平台,包括人脸识别SDK、活体检测SDK、人证核验SDK,这里我们使用的是人脸检测SDK,详细可以登陆虹软官网进行具体功能查阅。

登陆 http://ai.arcsoft.com.cn/prod... 进行注册后下载ArcSoft_ArcFace 2.0版本。

需要注意的是,下载SDK的版本要与qt编译器版本一致。这里选择下载 windous(x86)版本。
图片描述
下载完成后,首先阅读 releasenotes.txt -> ARCSOFT_ARC_FACE_DEVELOPER'S_GUIDE.pdf。

  • openCV 下载

这里我们直接下载编译好的 OpenCV(x86 MinGW 版)https://github.com/huihut/Ope...
图片描述
下载完成后,首先阅读 README.md。

环境搭建

  • 新建Qt工程

在 .pro 文件里面添加OpenCV相关库:

win32 {
INCLUDEPATH += D:\OpenCV-MinGW-Build-OpenCV-3.3.1\include\
INCLUDEPATH += D:\OpenCV-MinGW-Build-OpenCV-3.3.1\include\opencv
INCLUDEPATH += D:\OpenCV-MinGW-Build-OpenCV-3.3.1\include\opencv2
LIBS += D:\OpenCV-MinGW-Build-OpenCV-3.3.1\bin\libopencv_*.dll
}
1. 打开 Qt 工程,添加ArcSoft_ArcFace相关库:
2. 鼠标右键,添加库,外部库;
3. 平台选中 windows,链接选中 动态,其它不做勾选;
4. 库文件指下载好的 ArcSoft_ArcFace 的lib、dll 文件;
5. 包含路径指下载好的 ArcSoft_ArcFace 库相关的头文件。

图片描述

6. 鼠标右键,添加现有文件,把 ArcSoft_ArcFace 库相关的头文件添加到工程中。
HEADERS += \
ArcSoft_ArcFace/inc/amcomdef.h \
ArcSoft_ArcFace/inc/arcsoft_face_sdk.h \
ArcSoft_ArcFace/inc/asvloffscreen.h \
ArcSoft_ArcFace/inc/merror.h
  • 复制运行相关 dll 文件

将ArcSoft_ArcFace 与 OpenCV 相关的 dll 文件复制到工程生成的应用程序文件夹。如果没有添加完整,生成的应用程序运行时,将提示“由于找不到 xxx_.dll,无法继续执行代码。重新安装程序可能会解决此问题”或者“应用程序无法正常启动0xc000007b”等问题。

图片描述

代码实现

  • UI界面实现: 使用 Axure 画界面原型图,组件采用绝对定位方式。

图片描述

void Wigdet::initUI()
{
    m_photoBox.setParent(this);
    m_photoBox.move(8, 7);
    m_photoBox.resize(422, 278);

    m_idCardBox.setParent(this);
    m_idCardBox.move(440, 7);
    m_idCardBox.resize(276, 154);
    
    m_valLab.setParent(this);
    m_valLab.move(482, 171);
    m_valLab.resize(33, 16);
    m_valLab.setText("阈 值:");
    
    m_valSpinBox.setParent(this);
    m_valSpinBox.move(544, 166);
    m_valSpinBox.resize(159, 24);
    m_valSpinBox.setSingleStep(0.01);
    m_valSpinBox.setMinimum(0.01);
    m_valSpinBox.setMaximum(1.00);
    m_valSpinBox.setValue(0.82);
    
    m_loadPhotoBtn.setParent(this);
    m_loadPhotoBtn.move(454, 197);
    m_loadPhotoBtn.resize(249, 24);
    m_loadPhotoBtn.setText("图像导入");

    m_loadIdCardBtn.setParent(this);
    m_loadIdCardBtn.move(454, 228);
    m_loadIdCardBtn.resize(249, 24);
    m_loadIdCardBtn.setText("ID卡导入");
    
    m_compareBtn.setParent(this);
    m_compareBtn.move(454, 257);
    m_compareBtn.resize(249, 24);
    m_compareBtn.setText("面部识别");
    
    setWindowIcon(QIcon(":/pic/pic/icon.png"));
    setWindowTitle("AI Changes life [ 1508539502@qq.com -- TianSong ]");

    setFixedSize(726, 295);
}
  • 关键的槽函数
void Wigdet::initSLot()
{
    connect(&m_loadPhotoBtn, SIGNAL(clicked()), this, SLOT(onLoadPhotoBtnClicked()));
    connect(&m_loadIdCardBtn, SIGNAL(clicked()), this, SLOT(onLoadIdCardBtnClicked()));
    connect(&m_compareBtn, SIGNAL(clicked()), this, SLOT(onCompareBtnClicked()));
}

void Wigdet::onLoadPhotoBtnClicked();
void Wigdet::onLoadIdCardBtnClicked();
void Wigdet::onCompareBtnClicked();
  • 重写 painEvent 事件函数

void Wigdet::paintEvent(QPaintEvent*);

  • 人脸识别处理 (查阅帮助文档与示例代码)
#define APPID   "申请的APPID"   //APPID
#define SDKKey  "申请的SDKKey"  //SDKKey
#define MERR_ASF_BASE_ALREADY_ACTIVATED (0x16002) 

bool Wigdet::doCompare(Imag& img_photo, Imag& img_idcard, float val)
{
    bool pResult = false;

    /** 1. 激活SDK */
    MRESULT res = ASFActivation(const_cast<char*>(APPID), const_cast<char*>(SDKKey));
    if (MOK == res || MERR_ASF_BASE_ALREADY_ACTIVATED == res)
    {
        qDebug() << "ALActivation sucess: " << res;
    }
    else
    {
        qDebug() << "ALActivation fail: " << res;
    }

    /** 2. 初始化引擎 */
    MHandle handle = NULL;
    MInt32 mask = ASF_FACE_DETECT | ASF_FACERECOGNITION | ASF_AGE | ASF_GENDER | ASF_FACE3DANGLE;
    res = ASFInitEngine(static_cast<MInt32>(ASF_DETECT_MODE_IMAGE), ASF_OP_0_ONLY, 16, 5, mask, &handle);
    if (res == MOK)
    {
        qDebug() << "ALInitEngine sucess: " << res;
    }
    else
    {
        qDebug() << "ALInitEngine fail: " << res;
    }

    /** 3. 人脸检测 */
    img_photo.img.scaled(img_photo.img.width()/4*4, img_photo.img.height()/4*4).save("img1.png");
    img_idcard.img.scaled(img_idcard.img.width()/4*4, img_idcard.img.height()/4*4).save("img2.png");

    IplImage* img1  = cvLoadImage("img1.png");
    IplImage* img2  = cvLoadImage("img2.png");

    if (img1 && img2)
    {
        /** 3.1 第一张人脸特征提取 */
        ASF_MultiFaceInfo  detectedFaces1       = { 0 };
        ASF_SingleFaceInfo SingleDetectedFaces1 = { 0 };
        ASF_FaceFeature    feature1             = { 0 };
        ASF_FaceFeature    copyfeature1         = { 0 };
        res = ASFDetectFaces(handle, img1->width, img1->height, ASVL_PAF_RGB24_B8G8R8, (MUInt8*)img1->imageData, &detectedFaces1);
        if (MOK == res)
        {
            SingleDetectedFaces1.faceRect.left   = detectedFaces1.faceRect[0].left;
            SingleDetectedFaces1.faceRect.top    = detectedFaces1.faceRect[0].top;
            SingleDetectedFaces1.faceRect.right  = detectedFaces1.faceRect[0].right;
            SingleDetectedFaces1.faceRect.bottom = detectedFaces1.faceRect[0].bottom;
            SingleDetectedFaces1.faceOrient      = 0x05;

            qDebug() << detectedFaces1.faceNum;

            res = ASFFaceFeatureExtract(handle, img1->width, img1->height, ASVL_PAF_RGB24_B8G8R8, reinterpret_cast<MUInt8*>(img1->imageData), &SingleDetectedFaces1, &feature1);
            if (res == MOK)
            {
                /** 3.1.1 拷贝feature */
                copyfeature1.featureSize = feature1.featureSize;
                copyfeature1.feature = reinterpret_cast<MByte*>(malloc(static_cast<size_t>(feature1.featureSize)));
                memset(copyfeature1.feature, 0, static_cast<size_t>(feature1.featureSize));
                memcpy(copyfeature1.feature, feature1.feature, static_cast<size_t>(feature1.featureSize));  

                int x = SingleDetectedFaces1.faceRect.left;
                int y = SingleDetectedFaces1.faceRect.top;
                int w = SingleDetectedFaces1.faceRect.right - SingleDetectedFaces1.faceRect.left;
                int h = SingleDetectedFaces1.faceRect.bottom - SingleDetectedFaces1.faceRect.top;

                img_photo.rect = QRect(x, y, w, h);

                /** 3.1.2 人脸信息检测 */
                MInt32 processMask = ASF_AGE | ASF_GENDER;
                res = ASFProcess(handle, img1->width, img1->height, ASVL_PAF_RGB24_B8G8R8, reinterpret_cast<MUInt8*>(img1->imageData), &detectedFaces1, processMask);
                if (res == MOK)
                {
                    qDebug() << "ASFProcess sucess: " << res;
                }
                else
                {
                    qDebug() << "ASFProcess fail: " << res;
                }

                /** 3.1.3 获取年龄 */
                ASF_AgeInfo ageInfo = { 0 };
                res = ASFGetAge(handle, &ageInfo);
                if (res == MOK)
                {
                    img_photo.age = ageInfo.ageArray[0];

                    qDebug() << ageInfo.ageArray[0];
                    qDebug() << "ASFGetAge sucess: " << res;
                }
                else
                {
                    qDebug() << "ASFGetAge fail: " << res;
                }

                /** 3.1.4 获取性别 */
                ASF_GenderInfo genderInfo = { 0 };
                res = ASFGetGender(handle, &genderInfo);
                if (res == MOK)
                {
                    img_photo.gender = genderInfo.genderArray[0];

                    qDebug() << genderInfo.genderArray[0];
                    qDebug() << "ASFGetGender sucess: " << res;
                }
                else
                {
                    qDebug() << "ASFGetGender fail: " << res;
                }

                qDebug() << "ASFFaceFeatureExtract 1 Success";
            }
            else
            {
                qDebug() << "ASFFaceFeatureExtract 1 fail: " << res;
            }
        }
        else
        {
            qDebug() << "ASFDetectFaces 1 fail: " << res;
        }

        /** 3.2 第二张人脸特征提取 */
    ASF_MultiFaceInfo    detectedFaces2       = { 0 };
        ASF_SingleFaceInfo  SingleDetectedFaces2 = { 0 };
        ASF_FaceFeature     feature2             = { 0 };
        res = ASFDetectFaces(handle, img2->width, img2->height, ASVL_PAF_RGB24_B8G8R8, reinterpret_cast<MUInt8*>(img2->imageData), &detectedFaces2);
        if (MOK == res)
        {
            SingleDetectedFaces2.faceRect.left   = detectedFaces2.faceRect[0].left;
            SingleDetectedFaces2.faceRect.top    = detectedFaces2.faceRect[0].top;
            SingleDetectedFaces2.faceRect.right  = detectedFaces2.faceRect[0].right;
            SingleDetectedFaces2.faceRect.bottom = detectedFaces2.faceRect[0].bottom;
            SingleDetectedFaces2.faceOrient      = detectedFaces2.faceOrient[0];

            res = ASFFaceFeatureExtract(handle, img2->width, img2->height, ASVL_PAF_RGB24_B8G8R8, reinterpret_cast<MUInt8*>(img2->imageData), &SingleDetectedFaces2, &feature2);
            if (res == MOK)
            {
                int x = SingleDetectedFaces2.faceRect.left;
                int y = SingleDetectedFaces2.faceRect.top;
                int w = SingleDetectedFaces2.faceRect.right - SingleDetectedFaces2.faceRect.left;
                int h = SingleDetectedFaces2.faceRect.bottom - SingleDetectedFaces2.faceRect.top;

                img_idcard.rect = QRect(x, y, w, h);

                qDebug() << "ASFFaceFeatureExtract 2 Success";
            }
            else
            {
                qDebug() << "ASFFaceFeatureExtract 2 fail: " << res;
            }
        }
        else
        {
            qDebug() << "ASFDetectFaces 2 fail: " << res;
        }

        /** 3.3 单人脸特征比对 */
        MFloat confidenceLevel;
        res = ASFFaceFeatureCompare(handle, &copyfeature1, &feature2, &confidenceLevel);
        if (res == MOK)
        {
            qDebug() << "ASFFaceFeatureCompare sucess: " << confidenceLevel;

            if( confidenceLevel >= val ) pResult = true;
        }
        else
        {
            qDebug() << "ASFFaceFeatureCompare fail: " << res;
        }
    }

    /** 4. 反初始化 */
    res = ASFUninitEngine(handle);
    if (res != MOK)
    {
        qDebug() << "ALUninitEngine fail: " << res;
    }
    else
    {
        qDebug() << "ALUninitEngine sucess: " << res;
    }

    return pResult;
}

以上步骤完成后,就可以正常的编译运行了。

【因篇幅的限制,部分函数实现没有展开,可以根据文章末尾链接到github 中下载查看】

注意事项

ArcSoft_ArcFace_S 版本需要与 Qt 编译器一致 x64 或者 x86
opencv 版本需要与 Qt 编译器一致 x64 或者 x86.

下载链接

OpenCV-MinGW-Build-OpenCV-3.3.1:
https://github.com/cocowts/Op...

ArcSoft_ArcFace_Windows_x86_V2.0:
https://github.com/cocowts/Ar...

Qt 工程:
https://github.com/cocowts/Qt...


TianSong
734 声望138 粉丝

阿里山神木的种子在3000年前已经埋下,今天不过是看到当年注定的结果,为了未来的自己,今天就埋下一颗好种子吧