1. 程式人生 > >基於虹軟人臉識別API和Qt5的人臉識別

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

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

1基本流程

  (1)載入儲存的參考影象資料和影象標籤,這裡簡單的使用影象的名字作為標籤

  (2)使用虹軟人臉識別API計算參考影象的人臉位置資料並存儲

  (3)使用opencv  VideoCapture  類採集攝像頭影象資料

  (2)採集的影象資料送入虹軟人臉識別API 計算人臉位置,並和參考人臉資料計算相似距離,返回最相似的人臉標籤

2 Visual Studio 下構建Qt工程

(1)工程目錄如下圖所示: 在這裡插入圖片描述 其中QtGuiApplication1.ui是介面檔案,Header File資料夾中的amcomdef.h

ammem.h arcsoft_fsdk_face_detection.h arcsoft_fsdk_face_recognition.h

asvloffscreen.h merror.h 是從虹軟庫中拷貝的標頭檔案未做任何修改

FaceDiscern.h 和FaceDiscern.cpp是自定義的一個人臉識別類

(2)工程屬性配置

點選工程屬性->聯結器->輸入中出了QT5的庫檔案,新增opencv_world340d.lib 在這裡插入圖片描述 點選工程屬性-》VC++目錄新增OpenCV的標頭檔案和庫檔案的路徑,其中包含目錄新增opencv的標頭檔案路徑,庫目錄新增opencv的dll路徑,如下圖 在這裡插入圖片描述

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) 介面展示 在這裡插入圖片描述 最後是SDK下載地址 https://ai.arcsoft.com.cn/ucenter/user/reg?utm_source=csdn1&ut