ubuntu下用Qt实现人脸识别(四)

ubuntu下用Qt实现人脸识别(四)

一、流程

首先是程序启动的时候,从指定的路径下获取图片,然后从图片中提取出人脸特征值,将这些人脸特征值存储到缓冲区中。接着不断从摄像头获取一帧图片,传到人脸识别的算法接口,然后通过判别人脸阈值的大小,来确定是否识别成功。如果成功,在人脸框上显示该人脸的姓名。又因为,提取特征值和识别人脸的任务用时可能比较大,所以需要创建一条线程来执行这些任务。

二、源码

ubuntu下用Qt实现人脸识别之检测人脸并绘制人脸框(三)的基础下增加以下修改

主线程

主线程头文件代码如下(示例):

#ifndef HRQTPRO_H
#define HRQTPRO_H

#include <QWidget>
#include <QImage>
#include <QPainter>
#include <QObject>
#include <QTimer>
#include <QDebug>

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/highgui.hpp"

#include "ASF/inc/merror.h"
#include "ASF/inc/asvloffscreen.h"
#include "ASF/inc/arcsoft_face_sdk.h"
#include "ASF/inc/amcomdef.h"

#include "services/detect/workpthread.h"
//激活SDK的ID和KEY
#define APP_ID ""
#define SDK_KEY ""

using namespace cv;

namespace Ui {
class hrQtPro;
}

class hrQtPro : public QWidget
{
    Q_OBJECT

public:
    explicit hrQtPro(QWidget *parent = 0);
    ~hrQtPro();

protected:
    void paintEvent(QPaintEvent* e);

private slots:
    int slotReadFarme();
    void slotRegisterFace();
    void slotFaceCompareFinished(QString name, QString ID, float scores);

private:
    Ui::hrQtPro *ui;
    VideoCapture capture;
    QTimer  *cvTimer;
    Mat cap;
    QImage qImg;
    ASF_MultiFaceInfo detectedFaces;
    MHandle handle;
    //识别线程
    workPthread *thread1;
    //人脸姓名
    QString faceName;
    QString faceID;

    int ASFInit();
    int OpenCamera();
    QImage Mat2QImage(Mat &src);
};

#endif // HRQTPRO_H

主线程cpp代码如下(示例):

#include "hrqtpro.h"
#include "ui_hrqtpro.h"

hrQtPro::hrQtPro(QWidget *parent) :
    QWidget(parent),
    handle(NULL),
    ui(new Ui::hrQtPro)
{
    ui->setupUi(this);

    thread1 = new workPthread();
    connect(thread1, SIGNAL(faceCompareFinished(QString , QString ,float)),
            this, SLOT(slotFaceCompareFinished(QString , QString ,float)));
    thread1->start();
	//注册人脸按钮
    connect(ui->registerBtn, SIGNAL(clicked()), this, SLOT(slotRegisterFace()));

    cvTimer = new QTimer(this);
    connect(cvTimer, SIGNAL(timeout()), this, SLOT(slotReadFarme()));
    ASFInit();
    OpenCamera();
}

hrQtPro::~hrQtPro()
{
    if(thread1) {
        thread1->slotDestroy(true);
        thread1->quit();
        thread1->wait(300);
        delete thread1;
        thread1 = nullptr;
    }
    MRESULT res = ASFUninitEngine(handle);
    if (res != MOK)
        printf("ASFUninitEngine fail: %d\n", res);
    else
        printf("ASFUninitEngine sucess: %d\n", res);
    if(cvTimer) {
        cvTimer->stop();
        delete cvTimer;
        cvTimer = NULL;
    }
    delete ui;
}

int hrQtPro::ASFInit()
{
    MRESULT res = MOK;
#if 0
    //激活SDK,只需要激活一次即可
    //这里的APP_ID和SDK_KEY是从官网中获取到的
    res = ASFOnlineActivation(APP_ID, SDK_KEY);
    if (MOK != res && MERR_ASF_ALREADY_ACTIVATED != res)
        qDebug("ASFOnlineActivation fail: %d\n", res);
    else
        qDebug("ASFOnlineActivation sucess: %d\n", res);
#endif
    //只是检测人脸,如果需要人脸识别还需要或上ASF_FACERECOGNITION,例如:ASF_FACE_DETECT | ASF_FACERECOGNITION
    MInt32 mask = ASF_FACE_DETECT;
    res = ASFInitEngine(ASF_DETECT_MODE_VIDEO, ASF_OP_0_ONLY, 16, 5, mask, &handle);
    if (res != MOK)
        qDebug("ASFInitEngine fail: %d\n", res);
    else
        qDebug("ASFInitEngine sucess: %d\n", res);
    return res;
}

int hrQtPro::OpenCamera()
{
    //打开摄像头
    if(capture.open(0)) {
        qDebug() << "open success";
    } else {
        qDebug() << "open error";
        return -1;
    }
    //设置格式
    capture.set(cv::CAP_PROP_FOURCC, cv::VideoWriter::fourcc('M','J','P','G'));
    cvTimer->start(20);
    return 0;
}

//Mat转换为QImage
QImage hrQtPro::Mat2QImage(Mat &src) {
    if(src.type() == CV_8UC3) {
        const uchar *pSrc = (const uchar*)src.data;
        QImage qImage(pSrc,src.cols,src.rows,src.step,QImage::Format_RGB888);
        return qImage.rgbSwapped();
    }
    return QImage();
}

int hrQtPro::slotReadFarme()
{
    //读取摄像头数据
   capture.read(cap);
   if(!cap.data || cap.empty()) {
       return -1;
   }
   //Mat转换为QImage
   qImg = Mat2QImage(cap);
   if(qImg.isNull())
       return -1;

   IplImage ipi(cap);
   IplImage* img = cvCreateImage(cvSize(ipi.width - ipi.width % 4,
                                        ipi.height), IPL_DEPTH_8U, ipi.nChannels);
   ASVLOFFSCREEN offscreen = { 0 };
   if (img) {
       CutIplImage(&ipi, img, 0, 0);
       offscreen.u32PixelArrayFormat = ASVL_PAF_RGB24_B8G8R8;
       offscreen.i32Width = img->width;
       offscreen.i32Height = img->height;
       offscreen.pi32Pitch[0] = img->widthStep;
       offscreen.ppu8Plane[0] = (MUInt8*)img->imageData;
		//人脸检测
       MRESULT res = ASFDetectFacesEx(handle, &offscreen, &detectedFaces);
       if (MOK != res) {
           qDebug("ASFDetectFacesEx failed: %d\n", res);
       } else {
           if(detectedFaces.faceNum > 0 ) {
               compareInfo t;
               t.faceImg = NULL;
               t.faceImg = cvCreateImage(cvSize(ipi.width - ipi.width % 4,
                                                       ipi.height), IPL_DEPTH_8U, ipi.nChannels);
               CutIplImage(&ipi, t.faceImg, 0, 0);
               t.compareDetectedFaces.faceRect.left = detectedFaces.faceRect[0].left;
               t.compareDetectedFaces.faceRect.top = detectedFaces.faceRect[0].top;
               t.compareDetectedFaces.faceRect.right = detectedFaces.faceRect[0].right;
               t.compareDetectedFaces.faceRect.bottom = detectedFaces.faceRect[0].bottom;
               t.compareDetectedFaces.faceOrient = detectedFaces.faceOrient[0];
               t.faceID[0] = detectedFaces.faceID[0];
               t.faceNum = detectedFaces.faceNum;
               memcpy(&t.compareOffscreen, &offscreen, sizeof(offscreen));
               //将检测到的人脸数据添加到一个队列中,等待识别线程的处理
               thread1->addFrame(t);
               //设置faceID
               thread1->setFaceID(*detectedFaces.faceID);
               cvReleaseImage(&t.faceImg);
           }
       }
       cvReleaseImage(&img);
   }

}
//绘制人脸框
void hrQtPro::paintEvent(QPaintEvent* e)
{
    if(!qImg.isNull()) {
        QPainter painter(&qImg);
        if(detectedFaces.faceNum > 0) {
            // 设置人脸框颜色
            painter.setPen(QColor(255, 0, 0));
            //显示识别结果
            painter.drawText(QPoint(detectedFaces.faceRect[0].left, detectedFaces.faceRect[0].top - 20),
                    faceName);
            //绘制人脸的框
            painter.drawRect(detectedFaces.faceRect[0].left,
                    detectedFaces.faceRect[0].top,
                    detectedFaces.faceRect[0].right - detectedFaces.faceRect[0].left,
                    detectedFaces.faceRect[0].bottom - detectedFaces.faceRect[0].top);
        }
        ui->previewLabel->setPixmap(QPixmap::fromImage(qImg));
    }
}

void hrQtPro::slotFaceCompareFinished(QString name, QString ID, float scores)
{
	//识别成功后,得到一个姓名和ID
    if(scores >= 0.8) {
        faceName = name;
        faceID = ID;
    } else {
    //识别失败,清除信息
        faceName = "";
        faceID = "";
    }
}
//手动点击按钮进行拍照注册
void hrQtPro::slotRegisterFace()
{
    QString name  = QString::number(thread1->getFaceNum());
    QString ID = QString::number(thread1->getFaceNum());
    QString imgStr = AUTOMATIC_REGISTERI_MAGE_PATH + name + ".jpg";

    if(qImg.isNull()) {
        qDebug("[%s:%d] img is NULL!!!", __func__, __LINE__);
        return;
    }
	//保存这张图片,以便下次程序启动的时候自动获取人脸特征值
    if(!qImg.save(imgStr)) {
        qDebug("[%s:%d] save %s failed!!!", __func__, __LINE__, imgStr.toUtf8().data());
        return;
    } else {
        qDebug("[%s:%d] save %s success!!!", __func__, __LINE__, imgStr.toUtf8().data());
    }

    ASF_FaceFeature feature = { 0 };
    if(exFeaturesOfFile(imgStr, &feature) == 0){
        persionInfo registerInfo;
        memset(&registerInfo, 0, sizeof(persionInfo));

        strcpy(registerInfo.name, name.toUtf8().data());
        strcpy(registerInfo.ID, ID.toUtf8().data());
        memcpy(registerInfo.feature, feature.feature, feature.featureSize);
        registerInfo.featureLen = feature.featureSize;

        //更新缓存区的人脸数据
        thread1->addFace(&registerInfo);
        qDebug("[%s:%d] register %s success!!!", __func__, __LINE__, name.toUtf8().data());
    } else {
        qDebug("[%s:%d] register %s failed!!!", __func__, __LINE__, name.toUtf8().data());
    }
}

识别线程

头文件代码如下:

#ifndef WORKPTHREAD_H
#define WORKPTHREAD_H

#include <QThread>
#include <QDebug>
#include <QQueue>
#include <QObject>
#include <QDir>

#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/objdetect/objdetect.hpp>
#include "opencv2/highgui.hpp"

#include "ASF/inc/merror.h"
#include "ASF/inc/asvloffscreen.h"
#include "ASF/inc/arcsoft_face_sdk.h"
#include "ASF/inc/amcomdef.h"

#define NSCALE 16
#define FACE_NUM 5
#define FEATURE_LEN 2048
#define ID_LEN 64
#define NAME_LEN 64
//注册的图片路径
#define AUTOMATIC_REGISTERI_MAGE_PATH "image/automatic/"

#define SafeFree(p) { if ((p)) free(p); (p) = NULL; }
#define SafeArrayDelete(p) { if ((p)) delete [] (p); (p) = NULL; }
#define SafeDelete(p) { if ((p)) delete (p); (p) = NULL; }

typedef struct compare{
    IplImage* faceImg;
    ASF_SingleFaceInfo compareDetectedFaces;
    ASVLOFFSCREEN compareOffscreen;
    MInt32 faceID[5];
    MInt32 faceNum;
}compareInfo;
Q_DECLARE_METATYPE(compareInfo)

struct persionInfo {
    char ID[ID_LEN];
    char name[NAME_LEN];
    char feature[FEATURE_LEN];
    unsigned int featureLen;
};

Q_DECLARE_METATYPE(persionInfo)

void CutIplImage(IplImage* src, IplImage* dst, int x, int y);
int exFeaturesOfFile(QString filePth, ASF_FaceFeature *OutFeature);

class workPthread : public QThread
{
    Q_OBJECT
public:
    explicit workPthread(QObject *parent = nullptr);
    void run();
    void addFace(persionInfo *p);
    void addFrame(compareInfo frame);
    int getFaceNum();
    void slotDestroy(bool flag);
    void setFaceID(MInt32 id);

signals:
    void faceCompareFinished(QString name, QString ID, float scores);

private:
	//待识别的人脸信息队列
    QQueue<compareInfo> compareInfoQeue;
    //线程结束的标志
    bool isDestroy;
    QVector<persionInfo> faces;
    compareInfo recvCompareInfo;
    ASF_FaceFeature copyfeature1;
    //上一个人脸ID
    MInt32 lastID;

    int ASFInit();
    int compareResult(compareInfo *info, ASF_FaceFeature *databaseFeature, float *scores);
    int exFeaturesOfImage(compareInfo* p, ASF_FaceFeature *feature);
    void getFeatures();
};

#endif // WORKPTHREAD_H

cpp代码如下:

#include "workpthread.h"

MHandle handle = NULL;

#if 1
//图像四字节对齐
void CutIplImage(IplImage* src, IplImage* dst, int x, int y)
{
    CvSize size = cvSize(dst->width, dst->height);//区域大小
    cvSetImageROI(src, cvRect(x, y, size.width, size.height));//设置源图像ROI
    cvCopy(src, dst); //复制图像
    cvResetImageROI(src);//源图像用完后,清空ROI
}
//提取人脸特征值
int exFeaturesOfFile(QString filePth, ASF_FaceFeature *OutFeature)
{
    if(filePth.isEmpty() || filePth.isNull()) {
        qDebug("filePth is NULL");
        return -1;
    }

    MRESULT res = MOK;
    IplImage* pImg = cvLoadImage(filePth.toUtf8().data());
    IplImage* img = NULL;

    ASF_FaceFeature feature = { 0 };

    if(pImg) {
        img = cvCreateImage(cvSize(pImg->width - pImg->width % 4,
                                             pImg->height), IPL_DEPTH_8U, pImg->nChannels);
    } else {
        qDebug("pImg is null");
        return -1;
    }

    if(img) {
        CutIplImage(pImg, img, 0, 0);
    } else {
        qDebug("img is null");
        cvReleaseImage(&pImg);
        return -1;
    }

    if(img) {
        ASVLOFFSCREEN offscreen = { 0 };
        offscreen.u32PixelArrayFormat = ASVL_PAF_RGB24_B8G8R8;
        offscreen.i32Width = img->width;
        offscreen.i32Height = img->height;
        offscreen.pi32Pitch[0] = img->widthStep;
        offscreen.ppu8Plane[0] = (MUInt8*)img->imageData;
        ASF_MultiFaceInfo detectedFaces;
        res = ASFDetectFacesEx(handle, &offscreen, &detectedFaces);
        if (MOK != res) {
            qDebug("[%s]ASFDetectFacesEx failed: %d\n", __func__, res);
        } else {
            ASF_SingleFaceInfo SingleDetectedFaces = { 0 };

            SingleDetectedFaces.faceRect.left = detectedFaces.faceRect[0].left;
            SingleDetectedFaces.faceRect.top = detectedFaces.faceRect[0].top;
            SingleDetectedFaces.faceRect.right = detectedFaces.faceRect[0].right;
            SingleDetectedFaces.faceRect.bottom = detectedFaces.faceRect[0].bottom;
            SingleDetectedFaces.faceOrient = detectedFaces.faceOrient[0];
            res = ASFFaceFeatureExtractEx(handle, &offscreen, &SingleDetectedFaces, &feature);
            if (res != MOK) {
                qDebug("%s ASFFaceFeatureExtractEx fail: %d\n", filePth.toUtf8().data(), res);
            } else{
                OutFeature->featureSize = feature.featureSize;
                OutFeature->feature = (MByte *)malloc(feature.featureSize);
                memset(OutFeature->feature, 0, feature.featureSize);
                memcpy(OutFeature->feature, feature.feature, feature.featureSize);
                qDebug("%s ASFFaceFeatureExtractEx:%#x success: %d\n", filePth.toUtf8().data(), feature.feature,res);
            }
        }
        cvReleaseImage(&img);
    } else {
        qDebug("cutImg is null %s", filePth.toUtf8().data());
    }
    cvReleaseImage(&pImg);
    return res;
}
#endif

workPthread::workPthread(QObject *parent): QThread(parent)
{
    ASFInit();
    getFeatures();
    isDestroy = false;
}

void workPthread::run()
{
    int ret = -1;
    int i = 0;
    int lastSize = 0;
    int delCnt = 10;
    bool isFind = false;

    MFloat confidenceLevel = 0.0;
    MRESULT res;
    MInt32 curID = 0;
    ASF_FaceFeature featureImage = { 0 };

    lastSize = faces.size();
    lastID = -1;

    while(1){

        if(isDestroy) {
            break;
        }
		//去重,如果识别到一直是同一个人,就不重复提取人脸特征去识别了
        if((lastID != curID || confidenceLevel < 0.8) && !compareInfoQeue.isEmpty()) {
        	//10 * 100 * 1000us 内一直是同一个人,不重复识别
            if(delCnt-- <= 0) {
                delCnt = 10;
                curID = lastID;
            }
            recvCompareInfo = compareInfoQeue.back();
            memset(&featureImage, 0, sizeof(ASF_FaceFeature));
            //提取特征值
            ret = exFeaturesOfImage(&recvCompareInfo, &featureImage);
            lastSize = faces.size();
            copyfeature1.feature = (MByte *)malloc(FEATURE_LEN + 1);

            if(!isFind) {
                for(i=0; i<faces.size(); ++i) {
                    copyfeature1.featureSize = faces[i].featureLen;
                    memset(copyfeature1.feature, 0, FEATURE_LEN + 1);
                    memcpy(copyfeature1.feature, faces[i].feature, FEATURE_LEN);
					//特征值比对
                    res = ASFFaceFeatureCompare(handle, &featureImage, &copyfeature1, &confidenceLevel);
                    //阈值大于0.8,则识别成功
                    if(confidenceLevel >= 0.8) {
                        if(copyfeature1.feature) {
                             SafeFree(copyfeature1.feature);
                         }
                        break;
                    }
                }
            }
            isFind = false;

            SafeFree(copyfeature1.feature);
            copyfeature1.featureSize = 0;
            compareInfoQeue.dequeue();

            if(confidenceLevel >= 0.8 && faces.size() > 0) {
                qDebug() << "confidenceLevel=" << confidenceLevel;
                //识别成功,向主线程发送该人脸的信息
                emit faceCompareFinished(faces[i].name,
                                        faces[i].ID,
                                        confidenceLevel);
            } else {
            	//识别失败,清除主线程上一个人脸的信息
                emit faceCompareFinished("", "", 0.0);
            }
        }

        usleep(100*000);
    }

    res = ASFUninitEngine(handle);
    if (res != MOK)
        qDebug("ASFUninitEngine fail: %d\n", res);
    else
        qDebug("ASFUninitEngine sucess: %d\n", res);
    compareInfoQeue.clear();
}

void workPthread::slotDestroy(bool flag)
{
    isDestroy = flag;
}

void workPthread::addFrame(compareInfo frame)
{
    compareInfoQeue.enqueue(frame);
}

int workPthread::ASFInit()
{
    MRESULT res = MOK;
    handle = NULL;
    MInt32 mask = ASF_FACE_DETECT | ASF_FACERECOGNITION;
    res = ASFInitEngine(ASF_DETECT_MODE_VIDEO, ASF_OP_0_ONLY, NSCALE * 2, FACE_NUM, mask, &handle);
    if (res != MOK)
        qDebug("ASFInitEngine fail: %d\n", res);
    else
        qDebug("ASFInitEngine sucess: %d\n", res);
    return res;
}

int workPthread::exFeaturesOfImage(compareInfo* p, ASF_FaceFeature *feature)
{
    MRESULT res = MOK;

    if(p->faceNum <= 0) {
        qDebug() << "faceNum == 0";
        return -1;
    }
    res = ASFFaceFeatureExtractEx(handle, &p->compareOffscreen, &p->compareDetectedFaces, feature);
    if (res != MOK) {
        qDebug("%s ASFFaceFeatureExtractEx fail: %d\n", __func__, res);
        emit faceCompareFinished("", "", 0.0);
    }

    return res;
}
//添加人脸到缓冲区中
void workPthread::addFace(persionInfo *p) {
    persionInfo t;
    memset(&t, 0, sizeof(persionInfo));

    memcpy(t.feature, p->feature, p->featureLen);
    strcpy(t.ID, p->ID);
    strcpy(t.name, p->name);
    t.featureLen = p->featureLen;

    faces.push_back(t);
}
//获取人脸数量
int workPthread::getFaceNum() {
    return faces.size();
}
//设置人脸ID,起到去重的作用
void workPthread::setFaceID(MInt32 id) {
    lastID = id;
}
//程序启动的时候,加载指定路径下的人脸图片特征
void workPthread::getFeatures() {
    QDir dir;
    //如果该路径不存在,则创建
    if (!dir.exists(AUTOMATIC_REGISTERI_MAGE_PATH)) {
        dir.mkpath(AUTOMATIC_REGISTERI_MAGE_PATH);
    } else {
        qDebug() << AUTOMATIC_REGISTERI_MAGE_PATH << "is exists";
    }
    dir.setPath(AUTOMATIC_REGISTERI_MAGE_PATH);

    QStringList filename;
    QStringList results;
    quint32 ID = 0;
    QString path, name;
    persionInfo registerInfo;
    ASF_FaceFeature feature = { 0 };

    filename << "*.png" << "*.jpg"; //只选择png和jpg的图片
    results = dir.entryList(filename, QDir::Files | QDir::Readable, QDir::Name);

    for (int i=0; i<results.size(); ++i) {
        path = AUTOMATIC_REGISTERI_MAGE_PATH + results.at(i);
        //使用图片的名字来作为人脸的名字
        name = results.at(i).split(".").at(0);
        if(exFeaturesOfFile(path, &feature) == 0){
            ID++;
            memset(&registerInfo, 0, sizeof(persionInfo));

            strcpy(registerInfo.name, name.toUtf8().data());
            sprintf(registerInfo.ID, "%d", ID);
            memcpy(registerInfo.feature, feature.feature, feature.featureSize);
            registerInfo.featureLen = feature.featureSize;

            //更新缓存区的人脸数据
            faces.push_back(registerInfo);
            qDebug("obtain facial features success:%s", registerInfo.name);

        } else {
            qDebug("Num %d failed to obtain facial features. Name=%s", i, name.toUtf8().data());
        }
    }
}

最后结果

在这里插入图片描述
OK,完成。但是,在指定路径读取图片,并且提取人脸特征值的方法还需要改进,应该将注册好的特征值和人脸信息存储在一个数据库中,比如sqlite,下次程序启动只需要从数据库中进行读取即可;同时,还需要添加一些增删改查等功能。

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__不高兴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值