【置顶】项目解决方案下载链接
提取码为demo
本文主要基于C++语言,利用虹软人脸识别SDK,来实现本地视频流或RTSP视频流的人脸追踪。
实现内容包括实时检测摄像头的图像帧,实现多人脸的跟踪或将摄像头图像帧的每个人脸特征与人脸库中的人脸信息特征进行对比,实现指定目标的人脸跟踪。
C++ 是一种中级语言,它是由 Bjarne Stroustrup 于 1979 年在贝尔实验室开始设计开发的。. C++ 进一步扩充和完善了 C 语言,是一种面向对象的程序设计语言,并且C++可以很好地应用在多种平台上,比如:Windows、Mac以及Unix的各个版本。
OpenCV是一个开元的跨平台的计算机视觉库,可以运行在Linux、Windows、Android和Mac OS操作系统上。由一系列 C 函数和少量 C++ 类构成,实现了图像处理和计算机视觉方面的很多通用算法,在图像处理方面有着极高的性能表现!
虹软提供的离线人脸识别库,版本为4.1。
虹软拥有着全球领先的视觉智能技术,在多平台多语言的人脸检测、人脸跟踪、人脸对比以及更为丰富的人脸属性检测、IR/RGB活体检测、图像质量检测方便都有着不错的性能。
我们使用的是windows版本,直接下载虹软SDK得到一个压缩包,解压缩之后就可以得到我们需要的文件(结构如下):
需要注意的是:我们的工程是在32位(x86)环境下进行的!
使用VS2017在E盘新建一个控制台工程(对MFC还不熟悉,仅使用控制台作为演示),名为myFaceDemo。
我们这里创建一个预编译头的(或者空项目皆可)解决方案
然后先在Debug x86环境下运行一次,使得解决方案生成debug文件夹目录。
E:\myFaceDemo\myFaceDemo\opencv249\include
E:\myFaceDemo\myFaceDemo\opencv249\include\opencv
E:\myFaceDemo\myFaceDemo\opencv249\include\opencv2
E:\myFaceDemo\myFaceDemo\lib
E:\myFaceDemo\Debug
opencv_imgproc249d.lib
opencv_highgui249d.lib
opencv_core249d.lib
#include "pch.h"
#include
#include
#include "arcsoft_face_sdk.h"
#include "amcomdef.h"
#include "asvloffscreen.h"
#include "merror.h"
#include
#include
#include
#include
#include "opencv2/opencv.hpp"
#include //用于文件操作,保存设备信息用于生成离线激活文件.dat
加入上面的头文件之后我们再次生成运行,没有报错,表示我们的配置成功!!!
using namespace std;
using namespace cv;
#pragma comment(lib, "libarcsoft_face_engine.lib")
ofstream fout;//需要添加头文件
void GetDeviceInfo()
{
MRESULT res = MOK;
char* deviceInfo = NULL;//保存设备信息
res = ASFGetActiveDeviceInfo(&deviceInfo);
if (res != MOK) {
printf("ASFGetActiveDeviceInfo: %d\n", res);
}
fout.open("device.txt");
fout << deviceInfo;//将设备信息输出到文件
}
int main()
{
GetDeviceInfo();
}
这步操作需要在虹软官网上进行。
我们再包装一个用于离线激活设备的函数**devActive()**并在主函数中调用:
void devActive()
{
char ActiveFileName[] = "decActive.dat"; //创建一个字符数组来保存激活文件的文件名
MRESULT res = ASFOfflineActivation(ActiveFileName); //离线激活
if (MOK != res && MERR_ASF_ALREADY_ACTIVATED != res &&
MERR_ASF_LOCAL_EXIST_USEFUL_ACTIVE_FILE != res)
{
cout << "ASFOfflineActivation failed: " << res << endl; //激活失败打印
}
else cout << "ASFOfflineActivation success" << endl; //激活成功打印
}
然后我们在主函数中调用该函数,得到如下结果,说明我们的设备激活成功啦!
激活引擎需要调用的接口函数为:ASFInitEngine()
MRESULT InitEngine(ASF_DetectMode detectMode)//初始化引擎
{
m_hEngine = NULL;
MInt32 mask = 0;
if (ASF_DETECT_MODE_IMAGE == detectMode)
{
mask = ASF_FACE_DETECT | ASF_FACERECOGNITION | ASF_AGE | ASF_GENDER | ASF_LIVENESS | ASF_IR_LIVENESS;
}
else
{
mask = ASF_FACE_DETECT | ASF_FACERECOGNITION | ASF_LIVENESS | ASF_IR_LIVENESS;//功能选择,mask作为ASFInitEngine的参数
}
MRESULT res = ASFInitEngine(detectMode, ASF_OP_ALL_OUT, FACENUM, mask, &m_hEngine);
return res;
}
这里我们重新包装了一下,调用MRESULT InitEngine(ASF_DetectMode detectMode)来进行激活,可以选择激活模式为图片或者视频:
这样我们在主函数中可以通过下面的语句激活引擎m_hEngine。
这里注意m_hEngine要先进行定义(见完整程序)
MHandle m_hEngine;//引擎handle
然后调用
InitEngine(ASF_DETECT_MODE_VIDEO);
这样我们就完成了引擎的激活,在之后我们进行识别跟踪时,调用虹软sdk函数需要用到我们激活的引擎:m_hEngine
//裁剪图片
void PicCutOut(IplImage* src, IplImage* dst, int x, int y)
{
if (!src || !dst)
{
return;
}
CvSize size = cvSize(dst->width, dst->height);//区域大小
cvSetImageROI(src, cvRect(x, y, size.width, size.height));//设置源图像ROI
cvCopy(src, dst); //复制图像
cvResetImageROI(src);//源图像用完后,清空ROI
}
将处理后的图像信息保存在offscreen中
//颜色空间转换
int ColorSpaceConversion(IplImage* image, MInt32 format, ASVLOFFSCREEN& offscreen)
{
switch (format)
{
case ASVL_PAF_RGB24_B8G8R8:
offscreen.u32PixelArrayFormat = (unsigned int)format;
offscreen.i32Width = image->width;
offscreen.i32Height = image->height;
offscreen.pi32Pitch[0] = image->widthStep;
offscreen.ppu8Plane[0] = (MUInt8*)image->imageData;
break;
case ASVL_PAF_GRAY:
offscreen.u32PixelArrayFormat = (unsigned int)format;
offscreen.i32Width = image->width;
offscreen.i32Height = image->height;
offscreen.pi32Pitch[0] = image->widthStep;
offscreen.ppu8Plane[0] = (MUInt8*)image->imageData;
break;
default:
return 0;
}
return 1;
}
使用该函数,输入为:
实现图像帧中人脸信息的提取,返回值为:
//提取人脸信息
//如果不传入使用的引擎,那么默认使用视频引擎,返回为保存所有人脸的多信息
MRESULT PreDetectFace(IplImage* image, ASF_MultiFaceInfo& MultiFaces, MHandle& tempEngine = m_hEngine)
{
if (!image) { cout << "图片为空" << endl; return -1; }
IplImage* cutImg = NULL;
MRESULT res = MOK;
MultiFaces = { 0 };//人脸检测
cutImg = cvCreateImage(cvSize(image->width - (image->width % 4), image->height), IPL_DEPTH_8U, image->nChannels);
PicCutOut(image, cutImg, 0, 0);
ASVLOFFSCREEN offscreen = { 0 };
ColorSpaceConversion(cutImg, ASVL_PAF_RGB24_B8G8R8, offscreen);
res = ASFDetectFacesEx(tempEngine, &offscreen, &MultiFaces);
if (res != MOK || MultiFaces.faceNum < 1)
{
cvReleaseImage(&cutImg);
return -1;
}
return res;
}
使用该函数,输入为:
注意: 我们使用的是同一个引擎提取人脸特征,所以对于提取到的特征要进行复制(memcpy),因为同一个引擎多次进行特征提取会覆盖之前的特征,所以进行特征比对时,如果没有对特征进行复制,得到的对比结果始终为1。
//提取特征,如果不输入引擎,那么默认选用视频引擎,进行人脸人脸追踪,否则进行图片特征提取
MRESULT PreExtractFeature(IplImage* image, ASF_FaceFeature& feature, ASF_SingleFaceInfo& faceRect, MHandle& tempEngine = m_hEngine)
{
if (!image || image->imageData == NULL)
return -1;
IplImage* cutImg = cvCreateImage(cvSize(image->width - (image->width % 4), image->height), IPL_DEPTH_8U, image->nChannels);
PicCutOut(image, cutImg, 0, 0);
if (!cutImg) { cvReleaseImage(&cutImg); return -1; }
MRESULT res = MOK;
ASF_FaceFeature detectFaceFeature = { 0 };//特征值
ASVLOFFSCREEN offscreen = { 0 };
ColorSpaceConversion(cutImg, ASVL_PAF_RGB24_B8G8R8, offscreen);
if (tempEngine == m_hEngine)
res = ASFFaceFeatureExtractEx(tempEngine, &offscreen, &faceRect, ASF_RECOGNITION, 0, &detectFaceFeature);//这里相比于3.0版本做了修改
else
res = ASFFaceFeatureExtractEx(tempEngine, &offscreen, &faceRect, ASF_REGISTER, 0, &detectFaceFeature);
if (MOK != res) { cvReleaseImage(&cutImg); return res; }
if (!detectFaceFeature.feature)
return -1;
feature.featureSize = detectFaceFeature.featureSize;
feature.feature = (MByte *)malloc(detectFaceFeature.featureSize);
memset(feature.feature, 0, detectFaceFeature.featureSize);
memcpy(feature.feature, detectFaceFeature.feature, detectFaceFeature.featureSize);
cvReleaseImage(&cutImg);
return res;
}
然后在main()函数中我们通过本地摄像头或者本地视频来获取图像帧进行检测:
cv::Mat rgbFrame;//用于保存图像帧
cv::VideoCapture rgbCapture;
if (!rgbCapture.isOpened())
{
bool res = rgbCapture.open("testVideo.avi");//可以使用本地视频路径
//bool res = rgbCapture.open(0);//可以使用本地摄像头
if (res)
cout << "获取本地视频成功!" << endl;
}
//如果是使用摄像头,即rgbCapture.open(0)参数为0,那么可以使用以下内容设置摄像头的长宽,并判断初始化是否成功
//其中的CV_CAP_PROP_FRAME_WIDTH和CV_CAP_PROP_FRAME_HEIGHT在程序中要定义,见完整程序中宏定义关键量
//if (!(rgbCapture.set(CV_CAP_PROP_FRAME_WIDTH, VIDEO_FRAME_DEFAULT_WIDTH) &&
// rgbCapture.set(CV_CAP_PROP_FRAME_HEIGHT, VIDEO_FRAME_DEFAULT_HEIGHT)))//设置摄像头的宽和高分别为设定值(这里为640×480)
//{
// cout << "RGB摄像头初始化失败!" << endl;
// return 1;//设置失败的话就返回1,程序结束
//}
while (true)
{
rgbCapture >> rgbFrame;
if (!rgbFrame.empty())//判断获取到的视频真是否为空,不为空时进行处理
{
ASF_SingleFaceInfo faceInfo = { 0 };//用于保存从视频帧上识别到的最大单人脸信息
ASF_MultiFaceInfo multiFaceInfo = { 0 };//用于保存从视频帧上识别到的多人脸信息
IplImage rgbImage(rgbFrame);
MRESULT detectRes = PreDetectFace(&rgbImage, faceInfo, multiFaceInfo, true);//faceInfo保存的是视频帧上识别到的最大的单人脸信息
if (MOK == detectRes)//检测到人脸信息
{
//对检测到的所有人脸信息画框
for (int i = 0; i < multiFaceInfo.faceNum; i++)
{
cvRectangle(&rgbImage, cvPoint(multiFaceInfo.faceRect[i].left, multiFaceInfo.faceRect[i].top), cvPoint(multiFaceInfo.faceRect[i].right, multiFaceInfo.faceRect[i].bottom), cvScalar(0, 0, 255), 2);
cvPutText(&rgbImage, to_string(multiFaceInfo.faceID[i]).c_str(), cvPoint(multiFaceInfo.faceRect[i].left, multiFaceInfo.faceRect[i].top), &font, CV_RGB(255, 0, 0));//在人脸框左上角标记人脸ID
}
}
//显示图像
IplImage* m_curVideoImage;//保存拷贝后的图像
m_curVideoImage = cvCloneImage(&rgbImage);//复制一份图像
cvNamedWindow("show image");
cvShowImage("show image", m_curVideoImage);
if (waitKey(1) >= 0)
break;
}
else//如果视频帧为空,结束
{
cout << "视频播放结束" << endl;
break;
}
}
rgbCapture.release();//跳出while循环后释放rgbCapture
下面是完整程序!!!
#include "pch.h"
#include
#include
#include "arcsoft_face_sdk.h"
#include "amcomdef.h"
#include "asvloffscreen.h"
#include "merror.h"
#include
#include
#include
#include
#include "opencv2/opencv.hpp"
#include
#include
#include
#include
#include
#include
#include //进行文件操作
//使用命名空间std和cv
using namespace std;
using namespace cv;
//宏定义关键量
#define NSCALE 32 //支持最小的人脸
#define FACENUM 10 //可识别的人脸数目最大为10
#define VIDEO_FRAME_DEFAULT_WIDTH 640//定义摄像头宽
#define VIDEO_FRAME_DEFAULT_HEIGHT 480//定义摄像头高
//进行全局变量的定义
MHandle m_hEngine;//引擎handle
//加载人脸识别库
#pragma comment(lib, "libarcsoft_face_engine.lib")
void GetDevInfo()
{
ofstream fout;
fout.open("devInfo.txt");//将设备信息保存在该文件中
MRESULT res = MOK;
char* deviceInfo = NULL;
res = ASFGetActiveDeviceInfo(&deviceInfo);
if (res != MOK)
{
printf("ASFGetActiveDeviceInfo: %d\n", res);
}
fout << deviceInfo; //进行保存
}
void devActive()
{
char ActiveFileName[] = "decActive.dat"; //创建一个字符数组来保存激活文件的文件名
MRESULT res = ASFOfflineActivation(ActiveFileName); //离线激活
if (MOK != res && MERR_ASF_ALREADY_ACTIVATED != res &&
MERR_ASF_LOCAL_EXIST_USEFUL_ACTIVE_FILE != res)
{
cout << "ASFOfflineActivation failed: " << res << endl; //激活失败打印
}
else cout << "ASFOfflineActivation success" << endl; //激活成功打印
}
//裁剪图片
void PicCutOut(IplImage* src, IplImage* dst, int x, int y)
{
if (!src || !dst)
{
return;
}
CvSize size = cvSize(dst->width, dst->height);//区域大小
cvSetImageROI(src, cvRect(x, y, size.width, size.height));//设置源图像ROI
cvCopy(src, dst); //复制图像
cvResetImageROI(src);//源图像用完后,清空ROI
}
//颜色空间转换
int ColorSpaceConversion(IplImage* image, MInt32 format, ASVLOFFSCREEN& offscreen)
{
switch (format)
{
case ASVL_PAF_RGB24_B8G8R8:
offscreen.u32PixelArrayFormat = (unsigned int)format;
offscreen.i32Width = image->width;
offscreen.i32Height = image->height;
offscreen.pi32Pitch[0] = image->widthStep;
offscreen.ppu8Plane[0] = (MUInt8*)image->imageData;
break;
case ASVL_PAF_GRAY:
offscreen.u32PixelArrayFormat = (unsigned int)format;
offscreen.i32Width = image->width;
offscreen.i32Height = image->height;
offscreen.pi32Pitch[0] = image->widthStep;
offscreen.ppu8Plane[0] = (MUInt8*)image->imageData;
break;
default:
return 0;
}
return 1;
}
MRESULT InitEngine(ASF_DetectMode detectMode)//初始化引擎
{
m_hEngine = NULL;
MInt32 mask = 0;
if (ASF_DETECT_MODE_IMAGE == detectMode)
{
mask = ASF_FACE_DETECT | ASF_FACERECOGNITION | ASF_AGE | ASF_GENDER | ASF_LIVENESS | ASF_IR_LIVENESS;
}
else
{
mask = ASF_FACE_DETECT | ASF_FACERECOGNITION | ASF_LIVENESS | ASF_IR_LIVENESS;
}
MRESULT res = ASFInitEngine(detectMode, ASF_OP_ALL_OUT, FACENUM, mask, &m_hEngine);
return res;
}
//提取人脸信息
//如果不传入使用的引擎,那么默认使用视频引擎,返回为保存所有人脸的多信息
MRESULT PreDetectFace(IplImage* image, ASF_MultiFaceInfo& MultiFaces, bool isRGB = true, MHandle& tempEngine = m_hEngine)
{
if (!image) { cout << "图片为空" << endl; return -1; }
IplImage* cutImg = NULL;
MRESULT res = MOK;
MultiFaces = { 0 };//人脸检测
cutImg = cvCreateImage(cvSize(image->width - (image->width % 4), image->height), IPL_DEPTH_8U, image->nChannels);
PicCutOut(image, cutImg, 0, 0);
ASVLOFFSCREEN offscreen = { 0 };
ColorSpaceConversion(cutImg, ASVL_PAF_RGB24_B8G8R8, offscreen);
res = ASFDetectFacesEx(tempEngine, &offscreen, &MultiFaces);
if (res != MOK || MultiFaces.faceNum < 1)
{
cvReleaseImage(&cutImg);
return -1;
}
return res;
}
//提取特征,如果不输入引擎,那么默认选用视频引擎,进行人脸人脸追踪,否则进行图片特征提取
MRESULT PreExtractFeature(IplImage* image, ASF_FaceFeature& feature, ASF_SingleFaceInfo& faceRect, MHandle& tempEngine = m_hEngine)
{
if (!image || image->imageData == NULL)
return -1;
IplImage* cutImg = cvCreateImage(cvSize(image->width - (image->width % 4), image->height), IPL_DEPTH_8U, image->nChannels);
PicCutOut(image, cutImg, 0, 0);
if (!cutImg) { cvReleaseImage(&cutImg); return -1; }
MRESULT res = MOK;
ASF_FaceFeature detectFaceFeature = { 0 };//特征值
ASVLOFFSCREEN offscreen = { 0 };
ColorSpaceConversion(cutImg, ASVL_PAF_RGB24_B8G8R8, offscreen);
if (tempEngine == m_hEngine)
res = ASFFaceFeatureExtractEx(tempEngine, &offscreen, &faceRect, ASF_RECOGNITION, 0, &detectFaceFeature);//这里相比于3.0版本做了修改
else
res = ASFFaceFeatureExtractEx(tempEngine, &offscreen, &faceRect, ASF_REGISTER, 0, &detectFaceFeature);
if (MOK != res) { cvReleaseImage(&cutImg); return res; }
if (!detectFaceFeature.feature)
return -1;
feature.featureSize = detectFaceFeature.featureSize;
feature.feature = (MByte *)malloc(detectFaceFeature.featureSize);
memset(feature.feature, 0, detectFaceFeature.featureSize);
memcpy(feature.feature, detectFaceFeature.feature, detectFaceFeature.featureSize);
cvReleaseImage(&cutImg);
return res;
}
int main()
{
//获取设备信息
GetDevInfo();//调用获取设备信息的函数
//离线激活设备
devActive();//调用激活设备的函数
InitEngine(ASF_DETECT_MODE_VIDEO);
//捕获图像并开始处理
cv::Mat rgbFrame;//保存图像帧
cv::VideoCapture rgbCapture;//获取本地摄像头视频
if (!rgbCapture.isOpened())
{
bool res = rgbCapture.open("testVideo.avi");//可以使用本地视频路径
//bool res = rgbCapture.open("rtsp://127.0.0.1:8554/demo");//可以使用rtsp视频流
//bool res = rgbCapture.open(0);//可以使用本地摄像头
if (res)
cout << "打开本地摄像头成功!" << endl;
}
if (!(rgbCapture.set(CV_CAP_PROP_FRAME_WIDTH, VIDEO_FRAME_DEFAULT_WIDTH) &&
rgbCapture.set(CV_CAP_PROP_FRAME_HEIGHT, VIDEO_FRAME_DEFAULT_HEIGHT)))//设置摄像头的宽和高分别为设定值(这里为640×480)
{
cout << "RGB摄像头初始化失败!" << endl;
return 1;//设置失败的话就返回1,程序结束
}
CvFont font;
cvInitFont(&font, CV_FONT_HERSHEY_COMPLEX, 0.5, 0.5, 1, 1, 8);
while (true)
{
rgbCapture >> rgbFrame;
if (!rgbFrame.empty())
{
ASF_MultiFaceInfo multiFaceInfo = { 0 };
IplImage rgbImage(rgbFrame);
MRESULT detectRes = PreDetectFace(&rgbImage, multiFaceInfo);//faceInfo保存的是视频帧上识别到的最大的单人脸信息
if (MOK == detectRes)
{
//对检测到的所有人脸信息画框
int i = multiFaceInfo.faceNum;
for (int i = 0; i < multiFaceInfo.faceNum; i++)
{
cvRectangle(&rgbImage, cvPoint(multiFaceInfo.faceRect[i].left, multiFaceInfo.faceRect[i].top),
cvPoint(multiFaceInfo.faceRect[i].right, multiFaceInfo.faceRect[i].bottom), cvScalar(0, 0, 255), 2);
cvPutText(&rgbImage, to_string(multiFaceInfo.faceID[i]).c_str(), cvPoint(multiFaceInfo.faceRect[i].left, multiFaceInfo.faceRect[i].top), &font, CV_RGB(255, 0, 0));
}
}
//显示图像
IplImage* m_curVideoImage;//保存拷贝后的图像
m_curVideoImage = cvCloneImage(&rgbImage);//复制一份图像
cvNamedWindow("show image");
cvShowImage("show image", m_curVideoImage);
if (waitKey(1) >= 0)
break;
}
else
{
cout << "视频播放结束" << endl;
break;
}
}
rgbCapture.release();
return 0;
}
截取了几张使用本地视频进行人脸跟踪的效果图,可以看到效果还是很好的!!!
如果我们有rtsp视频流的推流链接,那么就很简单,只需要修改下面这句代码:
bool res = rgbCapture.open("testVideo.avi");
修改为:
bool res = rgbCapture.open("rtsp视频流链接");
这样就可以获取对应的画面。
如果我们没有现成的链接,那么我们可以使用一个VLC软件来生成一个rtsp视频流链接来进行测试!
下载VLC软件——>下载链接
下载安装后打开软件,选择左上角的媒体->流,然后选择捕获设备,选择我们的笔记本摄像头和音频输入,最后选择串流进入下一步。
然后按照下图操作,选择rtsp,点击添加后随便输入一个路径名,然后生成的rtsp链接地址就是:rtsp://127.0.0.1:8554/自定义的路径名
我这里的地址就是:rtsp://127.0.0.1:8554/demo
bool res = rgbCapture.open("rtsp://127.0.0.1:8554/demo");
对于指定人脸的跟踪识别,我们在主程序开始时加入了提取指定图片人脸特征,在视频帧处理时加入了提取所有人脸特征并一 一对比判断置信度数值是否大于设定阈值THRESHOLD(这里设置0.82,可以根据实际情况进行调整)
下面主要介绍人脸对比跟踪部分:
string img_name;
cout << "请输入图片库中姓名:" << endl;
cin >> img_name;
string fimgPath = filePath + img_name + ".jpg"; //简单直接的做法,如果想要更多样正式的获取路径或文件,可以使用c++的文件操作
Mat tempImg = imread(fimgPath.c_str());
IplImage getImg(tempImg);
ASF_MultiFaceInfo getMultiFaceInfo = { 0 };
PreDetectFace(&getImg, getMultiFaceInfo, true, m_hImgEngine);//图片中的人脸信息保存在getMultiFaceInfo中
//将多人脸信息提取第一个保存在getFaceInfo,用于下一步的特征提取
ASF_SingleFaceInfo getFaceInfo;
getFaceInfo.faceRect = getMultiFaceInfo.faceRect[0];
getFaceInfo.faceOrient = getMultiFaceInfo.faceOrient[0];
getFaceInfo.faceDataInfo = getMultiFaceInfo.faceDataInfoList[0];//相比于3.1版本新加入的
ASF_FaceFeature faceFeature = { 0 };
PreExtractFeature(&getImg, faceFeature, getFaceInfo, m_hImgEngine);
上述代码的主要步骤为:
for (int i = 0; i < multiFaceInfo.faceNum; i++)
{
tempFaceInfo.faceDataInfo = multiFaceInfo.faceDataInfoList[i];
tempFaceInfo.faceOrient = multiFaceInfo.faceOrient[i];
tempFaceInfo.faceRect = multiFaceInfo.faceRect[i];
cvRectangle(&rgbImage, cvPoint(multiFaceInfo.faceRect[i].left, multiFaceInfo.faceRect[i].top),
cvPoint(multiFaceInfo.faceRect[i].right, multiFaceInfo.faceRect[i].bottom), cvScalar(0, 0, 255), 2);
PreExtractFeature(&rgbImage, tempFeature, tempFaceInfo);
ASFFaceFeatureCompare(m_hEngine, &tempFeature, &faceFeature, &confidenceLeve);
if (confidenceLeve >= THRESHOLD)
{
string text = to_string(multiFaceInfo.faceID[i]) + " level:" + to_string(confidenceLeve);
cvPutText(&rgbImage, text.c_str(), cvPoint(multiFaceInfo.faceRect[i].left, multiFaceInfo.faceRect[i].top), &font, CV_RGB(255, 0, 0));
}
#include "pch.h"
#include
#include
#include
#include "arcsoft_face_sdk.h"
#include "amcomdef.h"
#include "asvloffscreen.h"
#include "merror.h"
#include
#include "opencv2/opencv.hpp"
#include //进行文件操作
//使用命名空间std和cv
using namespace std;
using namespace cv;
//宏定义关键量
#define NSCALE 32 //支持最小的人脸
#define FACENUM 10 //可识别的人脸数目最大为10
#define VIDEO_FRAME_DEFAULT_WIDTH 640//定义摄像头宽
#define VIDEO_FRAME_DEFAULT_HEIGHT 480//定义摄像头高
#define filePath "./face_lib/"
#define THRESHOLD 0.82
//进行全局变量的定义
MHandle m_hEngine;//引擎handle
MHandle m_hImgEngine;
//加载人脸识别库
#pragma comment(lib, "libarcsoft_face_engine.lib")
void GetDevInfo()
{
ofstream fout;
fout.open("devInfo.txt");//将设备信息保存在该文件中
MRESULT res = MOK;
char* deviceInfo = NULL;
res = ASFGetActiveDeviceInfo(&deviceInfo);
if (res != MOK)
{
printf("ASFGetActiveDeviceInfo: %d\n", res);
}
fout << deviceInfo; //进行保存
}
void devActive()
{
char ActiveFileName[] = "decActive.dat"; //创建一个字符数组来存储激活文件的文件名
MRESULT res = ASFOfflineActivation(ActiveFileName); //离线激活
if (MOK != res && MERR_ASF_ALREADY_ACTIVATED != res &&
MERR_ASF_LOCAL_EXIST_USEFUL_ACTIVE_FILE != res)
{
cout << "ASFOfflineActivation failed: " << res << endl; //激活失败打印
}
else cout << "ASFOfflineActivation success" << endl; //激活成功打印
}
//裁剪图片
void PicCutOut(IplImage* src, IplImage* dst, int x, int y)
{
if (!src || !dst)
{
return;
}
CvSize size = cvSize(dst->width, dst->height);//区域大小
cvSetImageROI(src, cvRect(x, y, size.width, size.height));//设置源图像ROI
cvCopy(src, dst); //复制图像
cvResetImageROI(src);//源图像用完后,清空ROI
}
//颜色空间转换
int ColorSpaceConversion(IplImage* image, MInt32 format, ASVLOFFSCREEN& offscreen)
{
switch (format)
{
case ASVL_PAF_RGB24_B8G8R8:
offscreen.u32PixelArrayFormat = (unsigned int)format;
offscreen.i32Width = image->width;
offscreen.i32Height = image->height;
offscreen.pi32Pitch[0] = image->widthStep;
offscreen.ppu8Plane[0] = (MUInt8*)image->imageData;
break;
case ASVL_PAF_GRAY:
offscreen.u32PixelArrayFormat = (unsigned int)format;
offscreen.i32Width = image->width;
offscreen.i32Height = image->height;
offscreen.pi32Pitch[0] = image->widthStep;
offscreen.ppu8Plane[0] = (MUInt8*)image->imageData;
break;
default:
return 0;
}
return 1;
}
//初始化引擎
//如果是图片模式,那么激活引擎m_hImgEngine
//如果是视频模式,那么激活引擎m_hEngine
MRESULT InitEngine(ASF_DetectMode detectMode)
{
MInt32 mask = 0;
if (ASF_DETECT_MODE_IMAGE == detectMode)
{
mask = ASF_FACE_DETECT | ASF_FACERECOGNITION | ASF_LIVENESS;
MRESULT res = ASFInitEngine(detectMode, ASF_OP_ALL_OUT, FACENUM, mask, &m_hImgEngine);
return res;
}
else
{
mask = ASF_FACE_DETECT | ASF_FACERECOGNITION | ASF_LIVENESS | ASF_UPDATE_FACEDATA;
MRESULT res = ASFInitEngine(detectMode, ASF_OP_ALL_OUT, FACENUM, mask, &m_hEngine);
return res;
}
}
//提取人脸信息
//如果不传入使用的引擎,那么默认使用视频引擎,返回为保存所有人脸的多信息
MRESULT PreDetectFace(IplImage* image, ASF_MultiFaceInfo& MultiFaces, MHandle& tempEngine = m_hEngine)
{
if (!image) { cout << "图片为空" << endl; return -1; }
IplImage* cutImg = NULL;
MRESULT res = MOK;
MultiFaces = { 0 };//人脸检测
cutImg = cvCreateImage(cvSize(image->width - (image->width % 4), image->height), IPL_DEPTH_8U, image->nChannels);
PicCutOut(image, cutImg, 0, 0);
ASVLOFFSCREEN offscreen = { 0 };
ColorSpaceConversion(cutImg, ASVL_PAF_RGB24_B8G8R8, offscreen);
res = ASFDetectFacesEx(tempEngine, &offscreen, &MultiFaces);
if (res != MOK || MultiFaces.faceNum < 1)
{
cvReleaseImage(&cutImg);
return -1;
}
return res;
}
//提取特征,如果不输入引擎,那么默认选用视频引擎,进行人脸人脸追踪,否则进行图片特征提取
MRESULT PreExtractFeature(IplImage* image, ASF_FaceFeature& feature, ASF_SingleFaceInfo& faceRect, MHandle& tempEngine = m_hEngine)
{
if (!image || image->imageData == NULL)
return -1;
IplImage* cutImg = cvCreateImage(cvSize(image->width - (image->width % 4), image->height), IPL_DEPTH_8U, image->nChannels);
PicCutOut(image, cutImg, 0, 0);
if (!cutImg) { cvReleaseImage(&cutImg); return -1; }
MRESULT res = MOK;
ASF_FaceFeature detectFaceFeature = { 0 };//特征值
ASVLOFFSCREEN offscreen = { 0 };
ColorSpaceConversion(cutImg, ASVL_PAF_RGB24_B8G8R8, offscreen);
if (tempEngine == m_hEngine)
res = ASFFaceFeatureExtractEx(tempEngine, &offscreen, &faceRect, ASF_RECOGNITION, 0, &detectFaceFeature);//这里相比于3.0版本做了修改
else
res = ASFFaceFeatureExtractEx(tempEngine, &offscreen, &faceRect, ASF_REGISTER, 0, &detectFaceFeature);
if (MOK != res) { cvReleaseImage(&cutImg); return res; }
if (!detectFaceFeature.feature)
return -1;
feature.featureSize = detectFaceFeature.featureSize;
feature.feature = (MByte *)malloc(detectFaceFeature.featureSize);
memset(feature.feature, 0, detectFaceFeature.featureSize);
memcpy(feature.feature, detectFaceFeature.feature, detectFaceFeature.featureSize);
cvReleaseImage(&cutImg);
return res;
}
int main()
{
//离线激活设备
devActive();//调用激活设备的函数
InitEngine(ASF_DETECT_MODE_VIDEO);
InitEngine(ASF_DETECT_MODE_IMAGE);
//输入名字,从图片库中加载图片提取特征
string img_name;
cout << "请输入图片库中姓名:" << endl;
cin >> img_name;
string fimgPath = filePath + img_name + ".jpg"; //简单直接的做法,如果想要更准确正式的获取路径或文件,可以使用c++的文件操作
Mat tempImg = imread(fimgPath.c_str());
IplImage getImg(tempImg);
ASF_MultiFaceInfo getMultiFaceInfo = { 0 };
PreDetectFace(&getImg, getMultiFaceInfo, m_hImgEngine);//图片中的人脸信息保存在getMultiFaceInfo中
//将多人脸信息提取第一个保存在getFaceInfo,用于下一步的特征提取
ASF_SingleFaceInfo getFaceInfo;
getFaceInfo.faceRect = getMultiFaceInfo.faceRect[0];
getFaceInfo.faceOrient = getMultiFaceInfo.faceOrient[0];
getFaceInfo.faceDataInfo = getMultiFaceInfo.faceDataInfoList[0];//相比于3.1版本新加入的
ASF_FaceFeature faceFeature = { 0 };
PreExtractFeature(&getImg, faceFeature, getFaceInfo, m_hImgEngine);
//捕获图像并开始处理
cv::Mat rgbFrame;//保存图像帧
cv::VideoCapture rgbCapture;//获取本地摄像头视频
if (!rgbCapture.isOpened())
{
//bool res = rgbCapture.open("rtsp://127.0.0.1:8554/demo");//可以使用本地视频路径
bool res = rgbCapture.open(0);//可以使用本地摄像头
if (res)
cout << "打开本地摄像头成功!" << endl;
else
{
cout << "打开本地摄像头失败!" << endl;
return -1;
}
}
if (!(rgbCapture.set(CV_CAP_PROP_FRAME_WIDTH, VIDEO_FRAME_DEFAULT_WIDTH) &&
rgbCapture.set(CV_CAP_PROP_FRAME_HEIGHT, VIDEO_FRAME_DEFAULT_HEIGHT)))//设置摄像头的宽和高分别为设定值(这里为640×480)
{
cout << "RGB摄像头初始化失败!" << endl;
return 1;//设置失败的话就返回1,程序结束
}
CvFont font;
cvInitFont(&font, CV_FONT_HERSHEY_COMPLEX, 0.5, 0.5, 1, 1, 8);
ASF_SingleFaceInfo tempFaceInfo = { 0 };//临时保存从视频帧上识别到的单人脸信息
ASF_MultiFaceInfo multiFaceInfo = { 0 };
ASF_FaceFeature tempFeature = { 0 };
MFloat confidenceLeve;
cout << "开始识别!" << endl;
while (true)
{
rgbCapture >> rgbFrame;
if (!rgbFrame.empty())//判断不为空时再处理
{
tempFaceInfo = { 0 };//临时保存从视频帧上识别到的单人脸信息
multiFaceInfo = { 0 };
tempFeature = { 0 };
IplImage rgbImage(rgbFrame);
MRESULT detectRes = PreDetectFace(&rgbImage, multiFaceInfo);//faceInfo保存的是视频帧上识别到的最大的单人脸信息
if (MOK == detectRes)
{
//对检测到的所有人脸信息画框
for (int i = 0; i < multiFaceInfo.faceNum; i++)
{
tempFaceInfo.faceDataInfo = multiFaceInfo.faceDataInfoList[i];
tempFaceInfo.faceOrient = multiFaceInfo.faceOrient[i];
tempFaceInfo.faceRect = multiFaceInfo.faceRect[i];
cvRectangle(&rgbImage, cvPoint(multiFaceInfo.faceRect[i].left, multiFaceInfo.faceRect[i].top),
cvPoint(multiFaceInfo.faceRect[i].right, multiFaceInfo.faceRect[i].bottom), cvScalar(0, 0, 255), 2);
PreExtractFeature(&rgbImage, tempFeature, tempFaceInfo);
ASFFaceFeatureCompare(m_hEngine, &tempFeature, &faceFeature, &confidenceLeve);
if (confidenceLeve >= THRESHOLD)
{
string text = to_string(multiFaceInfo.faceID[i]) + " level:" + to_string(confidenceLeve);
cvPutText(&rgbImage, text.c_str(), cvPoint(multiFaceInfo.faceRect[i].left, multiFaceInfo.faceRect[i].top), &font, CV_RGB(255, 0, 0));
}
}
}
//显示图像
IplImage* m_curVideoImage;//保存拷贝后的图像
m_curVideoImage = cvCloneImage(&rgbImage);//复制一份图像
cvNamedWindow("show image");
cvShowImage("show image", m_curVideoImage);
if (waitKey(1) >= 0)
break;
}
else
{
cout << "视频获取结束" << endl;
break;
}
}
rgbCapture.release();
cout << "识别结束" << endl;
return 0;
}
/***************************
//第一次运行时使用
int main()
{
//获取设备信息
GetDevInfo();//调用获取设备信息的函数
}
***************************/
我们在项目文件夹中新建一个face_lib文件夹,并放入指定人脸的图片
运行程序时可以输入图片库中的名称,进行指定人脸的跟踪
运行效果如下:
这次是我第一次使用SDK4.1的版本,之前使用的都是3.1的版本,相比于3.1版本,这次版本做了一些改动,我在进行demo的开发时,是根据之前的经验来做的,没有阅读版本改动,致使此次开发走了很多弯路,最后都是通过debug一点一点找到问题所在,然后再次阅读开发文档才得以解决。在使用SDK或其他技术进行项目开发时,要对开发文档以及改动说明进行了解,可以少花时间,少走弯路。