基於虹軟人臉識別API和Qt5的人臉識別

測試和使用了虹軟的人臉API在QT5環境下設計了一個簡單的人臉識別軟件,實現了對人臉的跟蹤和人臉識別。攝像頭的控制以及圖像格式的轉換使用了Opencv,圖像顯示使用的是QT5的Qimage控件。下面是詳細介紹java

**1基本流程**ios

(1)加載存儲的參考圖像數據和圖像標籤,這裏簡單的使用圖像的名字做爲標籤app

(2)使用虹軟人臉識別API計算參考圖像的人臉位置數據並存儲ide

(3)使用opencv VideoCapture 類採集攝像頭圖像數據函數

(2)採集的圖像數據送入虹軟人臉識別API 計算人臉位置,並和參考人臉數據計算類似距離,返回最類似的人臉標籤
**2 Visual Studio 下構建Qt工程**測試

(1)工程目錄以下圖所示:
![在這裏插入圖片描述](https://img-blog.csdn.net/20180827151422853?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d4dGNzdHQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
其中QtGuiApplication1.ui是界面文件,Header File文件夾中的amcomdef.h網站

ammem.h arcsoft_fsdk_face_detection.h arcsoft_fsdk_face_recognition.h ui

asvloffscreen.h merror.h 是從虹軟庫中拷貝的頭文件未作任何修改this

FaceDiscern.h 和FaceDiscern.cpp是自定義的一我的臉識別類spa

(2)工程屬性配置

點擊工程屬性->鏈接器->輸入中出了QT5的庫文件,添加opencv_world340d.lib
![在這裏插入圖片描述](https://img-blog.csdn.net/20180827151527187?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d4dGNzdHQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
點擊工程屬性-》VC++目錄添加OpenCV的頭文件和庫文件的路徑,其中包含目錄添加opencv的頭文件路徑,庫目錄添加opencv的dll路徑,以下圖
![在這裏插入圖片描述](https://img-blog.csdn.net/20180827151539104?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d4dGNzdHQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
**2工程類文件詳解**

(1)QtGuiApplication1 ui類的源文件以下所示,其中Mat2QImage函數將opencv採集的圖像數據轉化爲QImage支 持 的數據格式, VideoCapture 是Opencv用來操做攝像頭的類,QImage用來顯示採集的圖像數據

#pragma once
#include <QtWidgets/QMainWindow>
#include "ui_QtGuiApplication1.h"
#include "qmessagebox.h"
#include "opencv2/core/core.hpp" 
#include "opencv2/highgui/highgui.hpp" 
#include "opencv2/imgproc/imgproc.hpp" 
#include <iostream>
#include "qtimer.h"
#include "FaceDiscern.h"
#include "qrect.h"
#include "qpainter.h"
using namespace cv;
using namespace std;
class QtGuiApplication1 : public QMainWindow
{
Q_OBJECT
public:
QtGuiApplication1(QWidget *parent = Q_NULLPTR);
~QtGuiApplication1();
QImage Mat2QImage(cv::Mat cvImg); //圖像格式轉換
QTimer *timer;
Mat frame; //攝像頭直接得到的數據
FaceDiscern *facediscern; //人臉識別類
private:
Ui::QtGuiApplication1Class ui;
VideoCapture capture; //採集攝像頭的數據
QImage qImg; //展現圖像的控件
//---槽函數 用做事件觸發
public slots :
void openVideo();
void stopVideo();
void nextFrame();

};

  

(2)QtGuiApplication1.cpp

#include "QtGuiApplication1.h"

QtGuiApplication1::QtGuiApplication1(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
ui.image->setScaledContents(true); //fit video to label area
facediscern = new FaceDiscern("F:\\trainimages");//加載參考圖像數據和標籤
facediscern->Train();//計算參考數據圖像數據的人臉位置等

}

QtGuiApplication1::~QtGuiApplication1()
{
if (capture.isOpened())
capture.release();
delete(timer);
}

void QtGuiApplication1::openVideo()
{
if (capture.isOpened())
capture.release(); //decide if capture is already opened; if so,close it
capture.open(0); //open the default camera
if (capture.isOpened())
{
double rate = capture.get(CV_CAP_PROP_FPS);
capture >> frame; //得到攝像頭圖像數據
if (!frame.empty())
{
QImage image = Mat2QImage(frame); //將攝像頭的圖像數據轉換爲QImage支持的格式
this->ui.image->setPixmap(QPixmap::fromImage(image));

timer = new QTimer(this); //循環得到攝像頭數據
connect(timer, SIGNAL(timeout()), this, SLOT(nextFrame()));
timer->start(40);
}
}
}
void QtGuiApplication1::stopVideo()
{
if (capture.isOpened())
{
capture.release();
}
}
//循環得到攝像頭數據
void QtGuiApplication1::nextFrame()
{
capture >> frame;
double rate = capture.get(CV_CAP_PROP_FPS);
if (!frame.empty())
{
QImage image = Mat2QImage(frame);

//經過人臉檢測API得到人臉的位置並在Qimage上顯示人臉框
QRect rect;
//RecognizeFace識別人臉的位置並計算人臉所屬的標籤
string result = facediscern->RecognizeFace(&frame, rect);

static QTextCodec *codecForCStrings;
QString strQ = QString::fromLocal8Bit(result.c_str());
QString s1 = strQ;//這是在qlabel中顯示中文的辦法
this->ui.result->setText(s1); //在控件上顯示人臉所屬的標籤

QPainter painter(&image);
// 設置畫筆顏色
painter.setPen(QColor(255, 0, 0));
painter.drawRect(rect);//繪製人臉的框
this->ui.image->setPixmap(QPixmap::fromImage(image));

}

}

//將opencv 的cv::Mat 格式圖像轉換爲QImage圖像
QImage QtGuiApplication1::Mat2QImage(cv::Mat cvImg)
{
if (cvImg.channels() == 3) //3 channels color image
{
cv::cvtColor(cvImg, cvImg, CV_BGR2RGB); //BGR 轉爲 RGB
qImg = QImage((const unsigned char*)(cvImg.data),
cvImg.cols, cvImg.rows,
cvImg.cols*cvImg.channels(),
QImage::Format_RGB888);
}
else if (cvImg.channels() == 1) //grayscale image
{
qImg = QImage((const unsigned char*)(cvImg.data),
cvImg.cols, cvImg.rows,
cvImg.cols*cvImg.channels(),
QImage::Format_Indexed8);
}
else
{
qImg = QImage((const unsigned char*)(cvImg.data),
cvImg.cols, cvImg.rows,
cvImg.cols*cvImg.channels(),
QImage::Format_RGB888);
}
return qImg;

}

  

(3) FaceDiscern.h

FaceDiscern 是人臉識別的主類 執行了人臉位置檢測和人臉類似度計算等功能

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <Windows.h>
#include <iostream>
#include <vector>
#include <string>
#include <io.h>
#include <map>
#include "arcsoft_fsdk_face_recognition.h"
#include "merror.h"
#include "arcsoft_fsdk_face_detection.h"
#include "opencv2/core/core.hpp" 
#include "opencv2/highgui/highgui.hpp" 
#include "opencv2/imgproc/imgproc.hpp" 
#include "qrect.h"
//動態載入人臉識別的API庫 libarcsoft_fsdk_face_detection是人臉檢測庫
//libarcsoft_fsdk_face_recognition.lib是人臉識別庫
#pragma comment(lib,"libarcsoft_fsdk_face_detection.lib")
#pragma comment(lib,"./libarcsoft_fsdk_face_recognition.lib")
using namespace cv;
#define WORKBUF_SIZE (40*1024*1024)

class FaceDiscern
{
public:
FaceDiscern(std::string _trainpath);
~FaceDiscern();
//將cv::Mat格式的圖像轉換爲Bitmap
void ConvertMatToBitmap(cv::Mat *img, uint8_t **imageData, int *pWidth, int *pHeight);
void getFiles(std::string path, std::vector<std::string>& files, std::vector<std::string> &ownname);
void Train();
bool readBmp24(const char* path, uint8_t **imageData, int *pWidth, int *pHeight);
std::string RecognizeFace(cv::Mat *img, QRect &rect);

//APPID是從網站上註冊的無償使用id 
char APPID[45] = "9aEAsHDYzzzWapX9rH9BZHhdBz8CPTfws4WuF5xdmgnf";
char SDKKey[45] = "61MrwdsfKaMT8cm41uKPQBdCm4rKMLSELtJqs12p7WoV";	//SDKKey
char DETECTIONKKey[45] = "61MrwdsfKaMT8cm41uKPQBci7TocqKmAASGS7infomre";
std::string trainpath = "F:\\trainimages";
MRESULT nRet ;
MHandle hEngine ;
MInt32 nScale ;
MInt32 nMaxFace ;
MByte *pWorkMem;

std::vector<std::string> trainfullfiles;//完整路徑名
std::vector<std::string> trainnamefiles;
std::string *labels;
std::map<std::string, std::string> dicfilenametoname;

/* 初始化引擎和變量 */
MRESULT detectionnRet;
MHandle hdetectionEngine;
MInt32 ndetetionScale;
MInt32 ndetectionMaxFace ;
MByte *pdetectionWorkMem;

int trainCount = 0;
LPAFR_FSDK_FACEMODEL *trainfaceModels;

AFR_FSDK_FACEMODEL dectfaceModels;

};

  

(4)FaceDiscern.cpp

#include "FaceDiscern.h"
FaceDiscern::FaceDiscern(std::string _trainpath)
{
nRet = MERR_UNKNOWN;
hEngine = nullptr;
nScale = 16;
nMaxFace = 10;
pWorkMem = (MByte *)malloc(WORKBUF_SIZE);

/* 初始化引擎和變量 */
detectionnRet = MERR_UNKNOWN;
hdetectionEngine = nullptr;
ndetetionScale = 16;
ndetectionMaxFace = 10;
pdetectionWorkMem = (MByte *)malloc(WORKBUF_SIZE);
dicfilenametoname.insert(std::pair<std::string, std::string>("bingbing.bmp", "冰冰女神"));
dicfilenametoname.insert(std::pair<std::string, std::string>("fangfang.bmp", "村裏有個姑娘叫小芳"));
dicfilenametoname.insert(std::pair<std::string, std::string>("feifei.bmp", "劉亦菲"));
dicfilenametoname.insert(std::pair<std::string, std::string>("huihui.bmp", "冷工"));
dicfilenametoname.insert(std::pair<std::string, std::string>("shishi.bmp", "詩詩妹妹"));
dicfilenametoname.insert(std::pair<std::string, std::string>("xiaxia.bmp", "天上掉下個林妹妹"));
dicfilenametoname.insert(std::pair<std::string, std::string>("xudasong.bmp", "鬆哥"));
dicfilenametoname.insert(std::pair<std::string, std::string>("likunpeng.bmp", "李工"));
dicfilenametoname.insert(std::pair<std::string, std::string>("gaojianjun.bmp", "高建軍"));
dicfilenametoname.insert(std::pair<std::string, std::string>("liuzhen.bmp", "小鮮肉振哥"));
dicfilenametoname.insert(std::pair<std::string, std::string>("liting.bmp", "女王婷姐"));
dicfilenametoname.insert(std::pair<std::string, std::string>("wangxuetao.bmp", "雪濤"));
dicfilenametoname.insert(std::pair<std::string, std::string>("guowei.bmp", "郭大俠")); 
dicfilenametoname.insert(std::pair<std::string, std::string>("mingxin.bmp", "寶寶鳴新"));
this->trainpath = _trainpath;
}


FaceDiscern::~FaceDiscern()
{
/* 釋放引擎和內存 */
detectionnRet = AFD_FSDK_UninitialFaceEngine(hdetectionEngine);
if (detectionnRet != MOK)
{
fprintf(stderr, "UninitialFaceEngine failed , errorcode is %d \n", detectionnRet);
}
free(pdetectionWorkMem);

for (int i = 0; i < trainCount; i++)
{
if (trainfaceModels[i]->pbFeature != NULL)
free(trainfaceModels[i]->pbFeature);
}
nRet = AFR_FSDK_UninitialEngine(hEngine);
if (nRet != MOK)
{
fprintf(stderr, "UninitialFaceEngine failed , errorcode is %d \n", nRet);
}
}

//加載全部的參考圖像和圖像名字做爲參考庫
void FaceDiscern::getFiles(std::string path, std::vector<std::string>& files, std::vector<std::string> &ownname)
{
/*files存儲文件的路徑及名稱(eg. C:\Users\WUQP\Desktop\test_devided\data1.txt)
4 ownname只存儲文件的名稱(eg. data1.txt)*/
//文件句柄 
long long hFile = 0;
//文件信息 
struct _finddata_t fileinfo;
std::string p;
if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1)
{
do
{
//若是是目錄,迭代之 
//若是不是,加入列表 
if ((fileinfo.attrib & _A_SUBDIR))
{ /*
if(strcmp(fileinfo.name,".") != 0 && strcmp(fileinfo.name,"..") != 0)
getFiles( p.assign(path).append("\\").append(fileinfo.name), files, ownname ); */
}
else
{
files.push_back(p.assign(path).append("\\").append(fileinfo.name));
ownname.push_back(fileinfo.name);
}
} while (_findnext(hFile, &fileinfo) == 0);
_findclose(hFile);
}


}
//將cv::Mat轉換爲Bitmap
void FaceDiscern::ConvertMatToBitmap(cv::Mat *img, uint8_t **imageData, int *pWidth, int *pHeight)
{
//======創建位圖信息 ===========
int width, height, depth, channel;
width = img->cols;
height = img->rows;
depth = img->depth();
channel = img->channels();
*pWidth = width; //圖像寬。高
*pHeight = height;

int linebyte = width * channel;
*imageData = (uint8_t *)malloc(linebyte * (*pHeight));
for (int i = 0; i<height; i++) {
for (int j = 0; j<width; j++) {

*((*imageData) + i * width*channel + j * channel) = (*img).at<Vec3b>(i, j)[2];// (uint8_t)(*(img + i * width*channel + j * width + 2));
*((*imageData) + i * width*channel + j * channel + 1) = (*img).at<Vec3b>(i, j)[1];
*((*imageData) + i * width*channel + j * channel + 2) = (*img).at<Vec3b>(i, j)[0];
} // end of line 
}
}
//從文件中讀取圖像並轉化爲bitmap
bool FaceDiscern::readBmp24(const char* path, uint8_t **imageData, int *pWidth, int *pHeight)
{
if (path == NULL || imageData == NULL || pWidth == NULL || pHeight == NULL)
{
return false;
}
FILE *fp = fopen(path, "rb");
if (fp == NULL)
{
return false;
}
fseek(fp, sizeof(BITMAPFILEHEADER), 0);
BITMAPINFOHEADER head;
fread(&head, sizeof(BITMAPINFOHEADER), 1, fp);
*pWidth = head.biWidth;
*pHeight = head.biHeight;
int biBitCount = head.biBitCount;
if (24 == biBitCount)
{
int lineByte = ((*pWidth) * biBitCount / 8 + 3) / 4 * 4;
*imageData = (uint8_t *)malloc(lineByte * (*pHeight));
uint8_t * data = (uint8_t *)malloc(lineByte * (*pHeight));
fseek(fp, 54, SEEK_SET);
fread(data, 1, lineByte * (*pHeight), fp);
for (int i = 0; i < *pHeight; i++)
{
for (int j = 0; j < *pWidth; j++)
{
memcpy((*imageData) + i * (*pWidth) * 3 + j * 3, data + (((*pHeight) - 1) - i) * lineByte + j * 3, 3);
}
}
free(data);
}
else
{
fclose(fp);
return false;
}
fclose(fp);
return true;
}

//加載全部的參考數據
void FaceDiscern::Train()
{
if (pWorkMem == nullptr)
{
return;
}
nRet = AFR_FSDK_InitialEngine(APPID, SDKKey, pWorkMem, WORKBUF_SIZE, &hEngine); //初始化引擎

if (nRet != MOK)
{
return;
}

getFiles(trainpath, trainfullfiles, trainnamefiles);
//生成訓練數據 特徵集合

if (trainfullfiles.size() > 0)
{
//參考圖像數據的人臉特徵和標籤的存儲
trainfaceModels = new LPAFR_FSDK_FACEMODEL[trainfullfiles.size()];
labels = new std::string[trainfullfiles.size()];
}
else
{
return ;
}
for (int i = 0; i < trainfullfiles.size(); i++)
{
std::string filename = trainfullfiles[i];
/* 讀取第一張靜態圖片信息,並保存到ASVLOFFSCREEN結構體 (以ASVL_PAF_RGB24_B8G8R8格式爲例) */
ASVLOFFSCREEN offInput = { 0 };
offInput.u32PixelArrayFormat = ASVL_PAF_RGB24_B8G8R8;
offInput.ppu8Plane[0] = nullptr;
const char * path = filename.c_str();
readBmp24(path, (uint8_t**)&offInput.ppu8Plane[0], &offInput.i32Width, &offInput.i32Height);
if (!offInput.ppu8Plane[0])
{
fprintf(stderr, "fail to ReadBmp(%s)\n", path);
AFR_FSDK_UninitialEngine(hEngine);
free(pWorkMem);
continue ;
}
offInput.pi32Pitch[0] = offInput.i32Width * 3;
AFR_FSDK_FACEMODEL *faceModels = new AFR_FSDK_FACEMODEL();
{
AFR_FSDK_FACEINPUT faceInput;
//第一張人臉信息經過face detection\face tracking得到
faceInput.lOrient = AFR_FSDK_FOC_0;//人臉方向
//人臉框位置
faceInput.rcFace.left = 0;
faceInput.rcFace.top = 0;
faceInput.rcFace.right = offInput.i32Width - 2;;
faceInput.rcFace.bottom = offInput.i32Height - 2;;
//提取第一張人臉特徵
AFR_FSDK_FACEMODEL LocalFaceModels = { 0 };
nRet = AFR_FSDK_ExtractFRFeature(hEngine, &offInput, &faceInput, &LocalFaceModels);
if (nRet != MOK)
{
fprintf(stderr, "fail to Extract 1st FR Feature, error code: %d\n", nRet);
}
/* 拷貝人臉特徵結果 */
faceModels->lFeatureSize = LocalFaceModels.lFeatureSize;
faceModels->pbFeature = (MByte*)malloc(faceModels->lFeatureSize);
memcpy(faceModels->pbFeature, LocalFaceModels.pbFeature, faceModels->lFeatureSize);
}
trainfaceModels[i] = faceModels;
labels[i] = trainnamefiles[i];
trainCount++;
}

if (pdetectionWorkMem == nullptr)
{
return;
}
//人臉檢測engine
detectionnRet = AFD_FSDK_InitialFaceEngine(APPID, DETECTIONKKey, pdetectionWorkMem, WORKBUF_SIZE, &hdetectionEngine, AFD_FSDK_OPF_0_HIGHER_EXT, ndetetionScale, ndetectionMaxFace);
if (detectionnRet != MOK)
{
return;
}

}
//簡單的經過距離類似計算出最類似的參考圖像
std::string FaceDiscern::RecognizeFace(cv::Mat *img, QRect &rect)
{
/* 讀取靜態圖片信息,並保存到ASVLOFFSCREEN結構體 (以ASVL_PAF_RGB24_B8G8R8格式爲例) */
/* 人臉檢測 */

ASVLOFFSCREEN offInput = { 0 };
offInput.u32PixelArrayFormat = ASVL_PAF_RGB24_B8G8R8;
offInput.ppu8Plane[0] = nullptr;
ConvertMatToBitmap(img, (uint8_t**)&offInput.ppu8Plane[0], &offInput.i32Width, &offInput.i32Height);
if (!offInput.ppu8Plane[0])
{
return "";
}
offInput.pi32Pitch[0] = offInput.i32Width * 3;
LPAFD_FSDK_FACERES	FaceRes = nullptr;
detectionnRet = AFD_FSDK_StillImageFaceDetection(hdetectionEngine, &offInput, &FaceRes);
void *imgptr = offInput.ppu8Plane[0];
////識別人臉信息
AFR_FSDK_FACEINPUT faceInput;
faceInput.lOrient = AFR_FSDK_FOC_0;//人臉方向	//人臉框位置
faceInput.rcFace.left =FaceRes->rcFace[0].left;
faceInput.rcFace.top = FaceRes->rcFace[0].top;
faceInput.rcFace.right = FaceRes->rcFace[0].right;
faceInput.rcFace.bottom = FaceRes->rcFace[0].bottom;

rect.setLeft(FaceRes->rcFace[0].left);
rect.setTop(FaceRes->rcFace[0].top);
rect.setRight(FaceRes->rcFace[0].right);
rect.setBottom(FaceRes->rcFace[0].bottom);
//提取人臉特徵
nRet = AFR_FSDK_ExtractFRFeature(hEngine, &offInput, &faceInput, &dectfaceModels);
free(imgptr);

if (nRet != MOK)
{
return "";
}
float maxscore = -1.0;
int index = -1;
for (int i = 0; i < trainCount; i++)
{
MFloat fSimilScore = 0.0f;
nRet = AFR_FSDK_FacePairMatching(hEngine, &dectfaceModels, trainfaceModels[i], &fSimilScore);
if (fSimilScore > maxscore)
{
maxscore = fSimilScore;
index = i;
}
}
if (index != -1)
{
double num = maxscore * 100.0;
std::string str;
char ctr[10];
_gcvt(num, 6, ctr);
str = ctr;
std::string nameresult = labels[index];
if (dicfilenametoname.find(nameresult) != dicfilenametoname.end())
{
nameresult = dicfilenametoname[nameresult];
}
return nameresult + "," + str;
}
//釋放
if(dectfaceModels.lFeatureSize>0)
free(dectfaceModels.pbFeature);

return "";
}

  

**(3) 界面展現** ![在這裏插入圖片描述](https://img-blog.csdn.net/20180827163354615?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d4dGNzdHQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) 最後是SDK下載地址 https://ai.arcsoft.com.cn/ucenter/user/reg?utm_source=csdn1&utm_medium=referral

相關文章
相關標籤/搜索