C# 离线人脸识别 ArcSoft

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C# 离线人脸识别 ArcSoft相关的知识,希望对你有一定的参考价值。

人脸识别&比对发展到今天,已经是一个非常成熟的技术了,而且应用在生活的方方面面,比如手机、车站、天网等。


虹软人脸识别服务是免费的。最重要的是它还支持离线识别,并且提供androidios、C++、C#版SDK,现在已经升级到全新的3.0版本,支持活体识别。包含人脸检测,人脸对比,人脸检索功能.

过程如下:

1、 传入一张单人脸照片;

2、调用检测人脸函数ASFDetectFaces,成功返回人脸信息的指针;

3、使用 Marshal.ReadByte(intPtr,offset) 函数读出字节数,发现前16个字节是人脸框范围。

/// <summary>
/// 初始化引擎
/// </summary>
private void InitEngines()

//读取配置文件
AppSettingsReader reader = new AppSettingsReader();
string appId = (string)reader.GetValue("APP_ID", typeof(string));
string sdkKey64 = (string)reader.GetValue("SDKKEY64", typeof(string));
string sdkKey32 = (string)reader.GetValue("SDKKEY32", typeof(string));
rgbCameraIndex = (int)reader.GetValue("RGB_CAMERA_INDEX", typeof(int));
irCameraIndex = (int)reader.GetValue("IR_CAMERA_INDEX", typeof(int));
//判断CPU位数
var is64CPU = Environment.Is64BitProcess;
if (string.IsNullOrWhiteSpace(appId) || string.IsNullOrWhiteSpace(is64CPU?sdkKey64: sdkKey32))

//禁用相关功能按钮
ControlsEnable(false, chooseMultiImgBtn, matchBtn, btnClearFaceList, chooseImgBtn);
MessageBox.Show(string.Format("请在App.config配置文件中先配置APP_ID和SDKKEY0!", is64CPU ? "64" : "32"));
return;

//在线激活引擎    如出现错误,1.请先确认从官网下载的sdk库已放到对应的bin中,2.当前选择的CPU为x86或者x64
int retCode = 0;
try

retCode = ASFFunctions.ASFActivation(appId, is64CPU ? sdkKey64 : sdkKey32);

catch (Exception ex)

//禁用相关功能按钮
ControlsEnable(false, chooseMultiImgBtn, matchBtn, btnClearFaceList, chooseImgBtn);
if (ex.Message.Contains("无法加载 DLL"))

MessageBox.Show("请将sdk相关DLL放入bin对应的x86或x64下的文件夹中!");

else

MessageBox.Show("激活引擎失败!");

return;

Console.WriteLine("Activate Result:" + retCode);
//初始化引擎
uint detectMode = DetectionMode.ASF_DETECT_MODE_IMAGE;
//Video模式下检测脸部的角度优先值
int videoDetectFaceOrientPriority = ASF_OrientPriority.ASF_OP_0_HIGHER_EXT;
//Image模式下检测脸部的角度优先值
int imageDetectFaceOrientPriority = ASF_OrientPriority.ASF_OP_0_ONLY;
//人脸在图片中所占比例,如果需要调整检测人脸尺寸请修改此值,有效数值为2-32
int detectFaceScaleVal = 16;
//最大需要检测的人脸个数
int detectFaceMaxNum = 5;
//引擎初始化时需要初始化的检测功能组合
int combinedMask = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_AGE | FaceEngineMask.ASF_GENDER | FaceEngineMask.ASF_FACE3DANGLE;
//初始化引擎,正常值为0,其他返回值请参考http://ai.arcsoft.com.cn/bbs/forum.php?mod=viewthread&tid=19&_dsign=dbad527e
retCode = ASFFunctions.ASFInitEngine(detectMode, imageDetectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMask, ref pImageEngine);
Console.WriteLine("InitEngine Result:" + retCode);
AppendText((retCode == 0) ? "引擎初始化成功!\\n" : string.Format("引擎初始化失败!错误码为:0\\n", retCode));
if (retCode != 0)

//禁用相关功能按钮
ControlsEnable(false, chooseMultiImgBtn, matchBtn, btnClearFaceList, chooseImgBtn);

//初始化视频模式下人脸检测引擎
uint detectModeVideo = DetectionMode.ASF_DETECT_MODE_VIDEO;
int combinedMaskVideo = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION;
retCode = ASFFunctions.ASFInitEngine(detectModeVideo, videoDetectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMaskVideo, ref pVideoEngine);
//RGB视频专用FR引擎
detectFaceMaxNum = 1;
combinedMask = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_LIVENESS;
retCode = ASFFunctions.ASFInitEngine(detectMode, imageDetectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMask, ref pVideoRGBImageEngine);
//IR视频专用FR引擎
combinedMask = FaceEngineMask.ASF_FACE_DETECT  | FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_IR_LIVENESS;
retCode = ASFFunctions.ASFInitEngine(detectMode, imageDetectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMask, ref pVideoIRImageEngine);
Console.WriteLine("InitVideoEngine Result:" + retCode);
initVideo();

/// <summary>
/// 摄像头初始化
/// </summary>
private void initVideo()

filterInfoCollection = new FilterInfoCollection(FilterCategory.VideoInputDevice);
//如果没有可用摄像头,“启用摄像头”按钮禁用,否则使可用
if (filterInfoCollection.Count == 0)

btnStartVideo.Enabled = false;

else

btnStartVideo.Enabled = true;


#endregion
#region 注册人脸按钮事件
private object locker = new object();
/// <summary>
/// 人脸库图片选择按钮事件
/// </summary>
private void ChooseMultiImg(object sender, EventArgs e)

lock (locker)

OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Title = "选择图片";
openFileDialog.Filter = "图片文件|*.bmp;*.jpg;*.jpeg;*.png";
openFileDialog.Multiselect = true;
openFileDialog.FileName = string.Empty;
imageList.Refresh();
if (openFileDialog.ShowDialog() == DialogResult.OK)

List<string> imagePathListTemp = new List<string>();
var numStart = imagePathList.Count;
int isGoodImage = 0;
//保存图片路径并显示
string[] fileNames = openFileDialog.FileNames;
for (int i = 0; i < fileNames.Length; i++)

//图片格式判断
if (checkImage(fileNames[i]))  
imagePathListTemp.Add(fileNames[i]);


//人脸检测以及提取人脸特征
ThreadPool.QueueUserWorkItem(new WaitCallback(delegate

//禁止点击按钮
Invoke(new Action(delegate

chooseMultiImgBtn.Enabled = false;
matchBtn.Enabled = false;
btnClearFaceList.Enabled = false;
chooseImgBtn.Enabled = false;
btnStartVideo.Enabled = false;
));
//人脸检测和剪裁
for (int i = 0; i < imagePathListTemp.Count; i++)

Image image = ImageUtil.readFromFile(imagePathListTemp[i]);
if(image == null)

continue;

if (image.Width > 1536 || image.Height > 1536)

image = ImageUtil.ScaleImage(image, 1536, 1536);

if (image == null)

continue;

if (image.Width % 4 != 0)

image = ImageUtil.ScaleImage(image, image.Width - (image.Width % 4), image.Height);

//人脸检测
ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(pImageEngine, image);
//判断检测结果
if (multiFaceInfo.faceNum > 0)

imagePathList.Add(imagePathListTemp[i]);
MRECT rect = MemoryUtil.PtrToStructure<MRECT>(multiFaceInfo.faceRects);
image = ImageUtil.CutImage(image, rect.left, rect.top, rect.right, rect.bottom);

else

if(image != null)

image.Dispose();

continue;

//显示人脸
this.Invoke(new Action(delegate

if (image == null)

image = ImageUtil.readFromFile(imagePathListTemp[i]);
if (image.Width > 1536 || image.Height > 1536)

image = ImageUtil.ScaleImage(image, 1536, 1536);


imageLists.Images.Add(imagePathListTemp[i], image);
imageList.Items.Add((numStart + isGoodImage) + "号", imagePathListTemp[i]);
imageList.Refresh();
isGoodImage += 1;
if (image != null)

image.Dispose();

));

//提取人脸特征
for (int i = numStart; i < imagePathList.Count; i++)

ASF_SingleFaceInfo singleFaceInfo = new ASF_SingleFaceInfo();
Image image = ImageUtil.readFromFile(imagePathList[i]);
if (image == null)

continue;

IntPtr feature = FaceUtil.ExtractFeature(pImageEngine, image, out singleFaceInfo);
this.Invoke(new Action(delegate

if (singleFaceInfo.faceRect.left == 0 && singleFaceInfo.faceRect.right == 0)

AppendText(string.Format("0号未检测到人脸\\r\\n", i));

else

AppendText(string.Format("已提取0号人脸特征值,[left:1,right:2,top:3,bottom:4,orient:5]\\r\\n", i, singleFaceInfo.faceRect.left, singleFaceInfo.faceRect.right, singleFaceInfo.faceRect.top, singleFaceInfo.faceRect.bottom, singleFaceInfo.faceOrient));
imagesFeatureList.Add(feature);

));
if (image != null)

image.Dispose();


//允许点击按钮
Invoke(new Action(delegate

chooseMultiImgBtn.Enabled = true;
btnClearFaceList.Enabled = true;
btnStartVideo.Enabled = true;
if (btnStartVideo.Text == "启用摄像头")

chooseImgBtn.Enabled = true;
matchBtn.Enabled = true;

else

chooseImgBtn.Enabled = false;
matchBtn.Enabled = false;

));
));



#endregion
#region 清空人脸库按钮事件
/// <summary>
/// 清除人脸库事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnClearFaceList_Click(object sender, EventArgs e)

//清除数据
imageLists.Images.Clear();
imageList.Items.Clear();
foreach (IntPtr intptr in imagesFeatureList)

MemoryUtil.Free(intptr);

imagesFeatureList.Clear();
imagePathList.Clear();

#endregion
#region 选择识别图按钮事件
/// <summary>
/// “选择识别图片”按钮事件
/// </summary>
private void ChooseImg(object sender, EventArgs e)

lblCompareInfo.Text = "";
//判断引擎是否初始化成功
if (pImageEngine == IntPtr.Zero)

//禁用相关功能按钮
ControlsEnable(false, chooseMultiImgBtn, matchBtn, btnClearFaceList, chooseImgBtn);
MessageBox.Show("请先初始化引擎!");
return;

//选择图片
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Title = "选择图片";
openFileDialog.Filter = "图片文件|*.bmp;*.jpg;*.jpeg;*.png";
openFileDialog.Multiselect = false;
openFileDialog.FileName = string.Empty;
if (openFileDialog.ShowDialog() == DialogResult.OK)

image1Path = openFileDialog.FileName;
//检测图片格式
if (!checkImage(image1Path))

return;

DateTime detectStartTime = DateTime.Now;
AppendText(string.Format("------------------------------开始检测,时间:0------------------------------\\n", detectStartTime.ToString("yyyy-MM-dd HH:mm:ss:ms")));
//获取文件,拒绝过大的图片
FileInfo fileInfo = new FileInfo(image1Path);
if (fileInfo.Length > maxSize)

MessageBox.Show("图像文件最大为2MB,请压缩后再导入!");
AppendText(string.Format("------------------------------检测结束,时间:0------------------------------\\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ms")));
AppendText("\\n");
return;

Image srcImage = ImageUtil.readFromFile(image1Path);
if (srcImage == null)

MessageBox.Show("图像数据获取失败,请稍后重试!");
AppendText(string.Format("------------------------------检测结束,时间:0------------------------------\\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ms")));
AppendText("\\n");
return;

if (srcImage.Width > 1536 || srcImage.Height > 1536)

srcImage = ImageUtil.ScaleImage(srcImage, 1536, 1536);

if (srcImage == null)

MessageBox.Show("图像数据获取失败,请稍后重试!");
AppendText(string.Format("------------------------------检测结束,时间:0------------------------------\\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ms")));
AppendText("\\n");
return;

//调整图像宽度,需要宽度为4的倍数
if (srcImage.Width % 4 != 0)

srcImage = ImageUtil.ScaleImage(srcImage, srcImage.Width - (srcImage.Width % 4), srcImage.Height);

//调整图片数据,非常重要
ImageInfo imageInfo = ImageUtil.ReadBMP(srcImage);
if (imageInfo == null)

MessageBox.Show("图像数据获取失败,请稍后重试!");
AppendText(string.Format("------------------------------检测结束,时间:0------------------------------\\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ms")));
AppendText("\\n");
return;

//人脸检测
ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(pImageEngine, imageInfo);
//年龄检测
int retCode_Age = -1;
ASF_AgeInfo ageInfo = FaceUtil.AgeEstimation(pImageEngine, imageInfo, multiFaceInfo, out retCode_Age);
//性别检测
int retCode_Gender = -1;
ASF_GenderInfo genderInfo = FaceUtil.GenderEstimation(pImageEngine, imageInfo, multiFaceInfo, out retCode_Gender);
//3DAngle检测
int retCode_3DAngle = -1;
ASF_Face3DAngle face3DAngleInfo = FaceUtil.Face3DAngleDetection(pImageEngine, imageInfo, multiFaceInfo, out retCode_3DAngle);
MemoryUtil.Free(imageInfo.imgData);
if (multiFaceInfo.faceNum < 1)

srcImage = ImageUtil.ScaleImage(srcImage, picImageCompare.Width, picImageCompare.Height);
image1Feature = IntPtr.Zero;
picImageCompare.Image = srcImage;
AppendText(string.Format("0 - 未检测出人脸!\\n\\n", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
AppendText(string.Format("------------------------------检测结束,时间:0------------------------------\\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ms")));
AppendText("\\n");
return;

MRECT temp = new MRECT();
int ageTemp = 0;
int genderTemp = 0;
int rectTemp = 0;
//标记出检测到的人脸
for (int i = 0; i < multiFaceInfo.faceNum; i++)

MRECT rect = MemoryUtil.PtrToStructure<MRECT>(multiFaceInfo.faceRects + MemoryUtil.SizeOf<MRECT>() * i);
int orient = MemoryUtil.PtrToStructure<int>(multiFaceInfo.faceOrients + MemoryUtil.SizeOf<int>() * i);
int age = 0;
if (retCode_Age != 0)

AppendText(string.Format("年龄检测失败,返回0!\\n\\n", retCode_Age));

else

age = MemoryUtil.PtrToStructure<int>(ageInfo.ageArray + MemoryUtil.SizeOf<int>() * i);

int gender = -1;
if (retCode_Gender != 0)

AppendText(string.Format("性别检测失败,返回0!\\n\\n", retCode_Gender));

else

gender = MemoryUtil.PtrToStructure<int>(genderInfo.genderArray + MemoryUtil.SizeOf<int>() * i);

int face3DStatus = -1;
float roll = 0f;
float pitch = 0f;
float yaw = 0f;
if (retCode_3DAngle != 0)

AppendText(string.Format("3DAngle检测失败,返回0!\\n\\n", retCode_3DAngle));

else

//角度状态 非0表示人脸不可信
face3DStatus = MemoryUtil.PtrToStructure<int>(face3DAngleInfo.status + MemoryUtil.SizeOf<int>() * i);
//roll为侧倾角,pitch为俯仰角,yaw为偏航角
roll = MemoryUtil.PtrToStructure<float>(face3DAngleInfo.roll + MemoryUtil.SizeOf<float>() * i);
pitch = MemoryUtil.PtrToStructure<float>(face3DAngleInfo.pitch + MemoryUtil.SizeOf<float>() * i);
yaw = MemoryUtil.PtrToStructure<float>(face3DAngleInfo.yaw + MemoryUtil.SizeOf<float>() * i);

int rectWidth = rect.right - rect.left;
int rectHeight = rect.bottom - rect.top;
//查找最大人脸
if (rectWidth * rectHeight > rectTemp)

rectTemp = rectWidth * rectHeight;
temp = rect;
ageTemp = age;
genderTemp = gender;

AppendText(string.Format("0 - 人脸坐标:[left:1,top:2,right:3,bottom:4,orient:5,roll:6,pitch:7,yaw:8,status:11] Age:9 Gender:10\\n", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), rect.left, rect.top, rect.right, rect.bottom, orient, roll, pitch, yaw, age, (gender >= 0 ? gender.ToString() : ""), face3DStatus));

AppendText(string.Format("0 - 人脸数量:1\\n\\n", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), multiFaceInfo.faceNum));
DateTime detectEndTime = DateTime.Now;
AppendText(string.Format("------------------------------检测结束,时间:0------------------------------\\n", detectEndTime.ToString("yyyy-MM-dd HH:mm:ss:ms")));
AppendText("\\n");
ASF_SingleFaceInfo singleFaceInfo = new ASF_SingleFaceInfo();
//提取人脸特征
image1Feature = FaceUtil.ExtractFeature(pImageEngine, srcImage, out singleFaceInfo);
//清空上次的匹配结果
for (int i = 0; i < imagesFeatureList.Count; i++)

imageList.Items[i].Text = string.Format("0号", i);

//获取缩放比例
float scaleRate = ImageUtil.getWidthAndHeight(srcImage.Width, srcImage.Height, picImageCompare.Width, picImageCompare.Height);
//缩放图片
srcImage = ImageUtil.ScaleImage(srcImage, picImageCompare.Width, picImageCompare.Height);
//添加标记
srcImage = ImageUtil.MarkRectAndString(srcImage, (int)(temp.left * scaleRate), (int)(temp.top * scaleRate), (int)(temp.right * scaleRate) - (int)(temp.left * scaleRate), (int)(temp.bottom * scaleRate) - (int)(temp.top * scaleRate), ageTemp, genderTemp, picImageCompare.Width);
//显示标记后的图像
picImageCompare.Image = srcImage;


#endregion
#region 开始匹配按钮事件
/// <summary>
/// 匹配事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void matchBtn_Click(object sender, EventArgs e)

if (imagesFeatureList.Count == 0)

MessageBox.Show("请注册人脸!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;

if (image1Feature == IntPtr.Zero)

if (picImageCompare.Image == null)

MessageBox.Show("请选择识别图!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);

else

MessageBox.Show("比对失败,识别图未提取到特征值!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);

return;

//标记已经做了匹配比对,在开启视频的时候要清除比对结果
isCompare = true;
float compareSimilarity = 0f;
int compareNum = 0;
AppendText(string.Format("------------------------------开始比对,时间:0------------------------------\\r\\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ms")));
for (int i = 0; i < imagesFeatureList.Count; i++)

IntPtr feature = imagesFeatureList[i];
float similarity = 0f;
int ret = ASFFunctions.ASFFaceFeatureCompare(pImageEngine, image1Feature, feature, ref similarity);
//增加异常值处理
if(similarity.ToString().IndexOf("E") > -1)

similarity = 0f;

AppendText(string.Format("与0号比对结果:1\\r\\n", i, similarity));
imageList.Items[i].Text = string.Format("0号(1)", i, similarity);
if (similarity > compareSimilarity)

compareSimilarity = similarity;
compareNum = i;


if (compareSimilarity > 0)

lblCompareInfo.Text = " " + compareNum + "号," + compareSimilarity;

AppendText(string.Format("------------------------------比对结束,时间:0------------------------------\\r\\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ms")));

#endregion
#region 视频检测相关(<摄像头按钮点击事件、摄像头Paint事件、特征比对、摄像头播放完成事件>)
/// <summary>
/// 摄像头按钮点击事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnStartVideo_Click(object sender, EventArgs e)

//在点击开始的时候再坐下初始化检测,防止程序启动时有摄像头,在点击摄像头按钮之前将摄像头拔掉的情况
initVideo();
//必须保证有可用摄像头
if (filterInfoCollection.Count == 0)

MessageBox.Show("未检测到摄像头,请确保已安装摄像头或驱动!");
return;

if (rgbVideoSource.IsRunning || irVideoSource.IsRunning)

btnStartVideo.Text = "启用摄像头";
//关闭摄像头
if (irVideoSource.IsRunning)

irVideoSource.SignalToStop();
irVideoSource.Hide();

if (rgbVideoSource.IsRunning)

rgbVideoSource.SignalToStop();
rgbVideoSource.Hide();

//“选择识别图”、“开始匹配”按钮可用,阈值控件禁用
chooseImgBtn.Enabled = true;
matchBtn.Enabled = true;
txtThreshold.Enabled = false;

else

if (isCompare)

//比对结果清除
for (int i = 0; i < imagesFeatureList.Count; i++)

imageList.Items[i].Text = string.Format("0号", i);

lblCompareInfo.Text = "";
isCompare = false;

//“选择识别图”、“开始匹配”按钮禁用,阈值控件可用,显示摄像头控件
txtThreshold.Enabled = true;
rgbVideoSource.Show();
irVideoSource.Show();
chooseImgBtn.Enabled = false;
matchBtn.Enabled = false;
btnStartVideo.Text = "关闭摄像头";
//获取filterInfoCollection的总数
int maxCameraCount = filterInfoCollection.Count;
//如果配置了两个不同的摄像头索引
if(rgbCameraIndex != irCameraIndex && maxCameraCount>=2)

//RGB摄像头加载
rgbDeviceVideo = new VideoCaptureDevice(filterInfoCollection[rgbCameraIndex < maxCameraCount ? rgbCameraIndex : 0].MonikerString);
rgbDeviceVideo.VideoResolution = rgbDeviceVideo.VideoCapabilities[0];
rgbVideoSource.VideoSource = rgbDeviceVideo;
rgbVideoSource.Start();
//IR摄像头
irDeviceVideo = new VideoCaptureDevice(filterInfoCollection[irCameraIndex< maxCameraCount? irCameraIndex:0].MonikerString);
irDeviceVideo.VideoResolution = irDeviceVideo.VideoCapabilities[0];
irVideoSource.VideoSource = irDeviceVideo;
irVideoSource.Start();
//双摄标志设为true
isDoubleShot = true;

else

//仅打开RGB摄像头,IR摄像头控件隐藏
rgbDeviceVideo = new VideoCaptureDevice(filterInfoCollection[rgbCameraIndex <= maxCameraCount ? rgbCameraIndex : 0].MonikerString);
rgbDeviceVideo.VideoResolution = rgbDeviceVideo.VideoCapabilities[0];
rgbVideoSource.VideoSource = rgbDeviceVideo;
rgbVideoSource.Start();
irVideoSource.Hide();



private FaceTrackUnit trackRGBUnit = new FaceTrackUnit();
private FaceTrackUnit trackIRUnit = new FaceTrackUnit();
private Font font = new Font(FontFamily.GenericSerif, 10f, FontStyle.Bold);
private SolidBrush yellowBrush = new SolidBrush(Color.Yellow);
private SolidBrush blueBrush = new SolidBrush(Color.Blue);
private bool isRGBLock = false;
private bool isIRLock = false;
private MRECT allRect = new MRECT();
private object rectLock = new object();
/// <summary>
/// RGB摄像头Paint事件,图像显示到窗体上,得到每一帧图像,并进行处理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void videoSource_Paint(object sender, PaintEventArgs e)

if (rgbVideoSource.IsRunning)

//得到当前RGB摄像头下的图片
Bitmap bitmap = rgbVideoSource.GetCurrentVideoFrame();
if (bitmap == null)

return;

//检测人脸,得到Rect框
ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(pVideoEngine, bitmap);
//得到最大人脸
ASF_SingleFaceInfo maxFace = FaceUtil.GetMaxFace(multiFaceInfo);
//得到Rect
MRECT rect = maxFace.faceRect;
//检测RGB摄像头下最大人脸
Graphics g = e.Graphics;
float offsetX = rgbVideoSource.Width * 1f / bitmap.Width;
float offsetY = rgbVideoSource.Height * 1f / bitmap.Height;
float x = rect.left * offsetX;
float width = rect.right * offsetX - x;
float y = rect.top * offsetY;
float height = rect.bottom * offsetY - y;
//根据Rect进行画框
g.DrawRectangle(Pens.Red, x, y, width, height);
if (trackRGBUnit.message != "" && x > 0 && y > 0)

//将上一帧检测结果显示到页面上
g.DrawString(trackRGBUnit.message, font, trackRGBUnit.message.Contains("活体") ? blueBrush : yellowBrush, x, y - 15);

//保证只检测一帧,防止页面卡顿以及出现其他内存被占用情况
if (isRGBLock == false)

isRGBLock = true;
//异步处理提取特征值和比对,不然页面会比较卡
ThreadPool.QueueUserWorkItem(new WaitCallback(delegate

if (rect.left != 0 && rect.right != 0 && rect.top != 0 && rect.bottom != 0)

try

lock (rectLock)

allRect.left = (int)(rect.left * offsetX);
allRect.top = (int)(rect.top * offsetY);
allRect.right = (int)(rect.right * offsetX);
allRect.bottom = (int)(rect.bottom * offsetY);

bool isLiveness = false;
//调整图片数据,非常重要
ImageInfo imageInfo = ImageUtil.ReadBMP(bitmap);
if (imageInfo == null)

return;

int retCode_Liveness = -1;
//RGB活体检测
ASF_LivenessInfo liveInfo = FaceUtil.LivenessInfo_RGB(pVideoRGBImageEngine, imageInfo, multiFaceInfo, out retCode_Liveness);
//判断检测结果
if (retCode_Liveness == 0 && liveInfo.num > 0)

int isLive = MemoryUtil.PtrToStructure<int>(liveInfo.isLive);
isLiveness = (isLive == 1) ? true : false;

if (imageInfo != null)

MemoryUtil.Free(imageInfo.imgData);

if (isLiveness)

//提取人脸特征
IntPtr feature = FaceUtil.ExtractFeature(pVideoRGBImageEngine, bitmap, maxFace);
float similarity = 0f;
//得到比对结果
int result = compareFeature(feature, out similarity);
MemoryUtil.Free(feature);
if (result > -1)

//将比对结果放到显示消息中,用于最新显示
trackRGBUnit.message = string.Format(" 0号 1,2", result, similarity, string.Format("RGB0", isLiveness ? "活体" : "假体"));

else

//显示消息
trackRGBUnit.message = string.Format("RGB0", isLiveness ? "活体" : "假体");


else

//显示消息
trackRGBUnit.message = string.Format("RGB0", isLiveness ? "活体" : "假体");


catch (Exception ex)

Console.WriteLine(ex.Message);

finally

if(bitmap != null)

bitmap.Dispose();

isRGBLock = false;


else

lock (rectLock)

allRect.left = 0;
allRect.top = 0;
allRect.right = 0;
allRect.bottom = 0;


isRGBLock = false;
));



/// <summary>
/// RGB摄像头Paint事件,同步RGB人脸框,对比人脸框后进行IR活体检测
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void irVideoSource_Paint(object sender, PaintEventArgs e)

if (isDoubleShot && irVideoSource.IsRunning)

//如果双摄,且IR摄像头工作,获取IR摄像头图片
Bitmap irBitmap = irVideoSource.GetCurrentVideoFrame();
if (irBitmap == null)

return;

//得到Rect
MRECT rect = new MRECT();
lock (rectLock)

rect = allRect;

float irOffsetX = irVideoSource.Width * 1f / irBitmap.Width;
float irOffsetY = irVideoSource.Height * 1f / irBitmap.Height;
float offsetX = irVideoSource.Width * 1f / rgbVideoSource.Width;
float offsetY = irVideoSource.Height * 1f / rgbVideoSource.Height;
//检测IR摄像头下最大人脸
Graphics g = e.Graphics;
float x = rect.left * offsetX;
float width = rect.right * offsetX - x;
float y = rect.top * offsetY;
float height = rect.bottom * offsetY - y;
//根据Rect进行画框
g.DrawRectangle(Pens.Red, x, y, width, height);
if (trackIRUnit.message != "" && x > 0 && y > 0)

//将上一帧检测结果显示到页面上
g.DrawString(trackIRUnit.message, font, trackIRUnit.message.Contains("活体") ? blueBrush : yellowBrush, x, y - 15);

//保证只检测一帧,防止页面卡顿以及出现其他内存被占用情况
if (isIRLock == false)

isIRLock = true;
//异步处理提取特征值和比对,不然页面会比较卡
ThreadPool.QueueUserWorkItem(new WaitCallback(delegate

if (rect.left != 0 && rect.right != 0 && rect.top != 0 && rect.bottom != 0)

bool isLiveness = false;
try

//得到当前摄像头下的图片
if (irBitmap != null)

//检测人脸,得到Rect框
ASF_MultiFaceInfo irMultiFaceInfo = FaceUtil.DetectFace(pVideoIRImageEngine, irBitmap);
if (irMultiFaceInfo.faceNum <= 0)

return;

//得到最大人脸
ASF_SingleFaceInfo irMaxFace = FaceUtil.GetMaxFace(irMultiFaceInfo);
//得到Rect
MRECT irRect = irMaxFace.faceRect;
//判断RGB图片检测的人脸框与IR摄像头检测的人脸框偏移量是否在误差允许范围内
if (isInAllowErrorRange(rect.left * offsetX / irOffsetX, irRect.left) && isInAllowErrorRange(rect.right * offsetX / irOffsetX, irRect.right)
&& isInAllowErrorRange(rect.top * offsetY / irOffsetY, irRect.top) && isInAllowErrorRange(rect.bottom * offsetY / irOffsetY, irRect.bottom))

int retCode_Liveness = -1;
//将图片进行灰度转换,然后获取图片数据
ImageInfo irImageInfo = ImageUtil.ReadBMP_IR(irBitmap);
if (irImageInfo == null)

return;

//IR活体检测
ASF_LivenessInfo liveInfo = FaceUtil.LivenessInfo_IR(pVideoIRImageEngine, irImageInfo, irMultiFaceInfo, out retCode_Liveness);
//判断检测结果
if (retCode_Liveness == 0 && liveInfo.num > 0)

int isLive = MemoryUtil.PtrToStructure<int>(liveInfo.isLive);
isLiveness = (isLive == 1) ? true : false;

if (irImageInfo != null)

MemoryUtil.Free(irImageInfo.imgData);




catch (Exception ex)

Console.WriteLine(ex.Message);

finally

trackIRUnit.message = string.Format("IR0", isLiveness ? "活体" : "假体");
if (irBitmap != null)

irBitmap.Dispose();

isIRLock = false;


else

trackIRUnit.message = string.Empty;

isIRLock = false;
));



/// <summary>
/// 得到feature比较结果
/// </summary>
/// <param name="feature"></param>
/// <returns></returns>
private int compareFeature(IntPtr feature, out float similarity)

int result = -1;
similarity = 0f;
//如果人脸库不为空,则进行人脸匹配
if (imagesFeatureList != null && imagesFeatureList.Count > 0)

for (int i = 0; i < imagesFeatureList.Count; i++)

//调用人脸匹配方法,进行匹配
ASFFunctions.ASFFaceFeatureCompare(pVideoRGBImageEngine, feature, imagesFeatureList[i], ref similarity);
if (similarity >= threshold)

result = i;
break;



return result;
        
/// <summary>
/// 摄像头播放完成事件
/// </summary>
/// <param name="sender"></param>
/// <param name="reason"></param>
private void videoSource_PlayingFinished(object sender, AForge.Video.ReasonToFinishPlaying reason)

try

Control.CheckForIllegalCrossThreadCalls = false;
chooseImgBtn.Enabled = true;
matchBtn.Enabled = true;

catch (Exception e)

Console.WriteLine(e.Message);


#endregion
#region 阈值相关
/// <summary>
/// 阈值文本框键按下事件,检测输入内容是否正确,不正确不能输入
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void txtThreshold_KeyPress(object sender, KeyPressEventArgs e)

//阻止从键盘输入键
e.Handled = true;
//是数值键,回退键,.能输入,其他不能输入
if (char.IsDigit(e.KeyChar) || e.KeyChar == 8 || e.KeyChar == '.')

//渠道当前文本框的内容
string thresholdStr = txtThreshold.Text.Trim();
int countStr = 0;
int startIndex = 0;
//如果当前输入的内容是否是“.”
if (e.KeyChar == '.')

countStr = 1;

//检测当前内容是否含有.的个数
if (thresholdStr.IndexOf('.', startIndex) > -1)

countStr += 1;

//如果输入的内容已经超过12个字符,
if (e.KeyChar != 8 && (thresholdStr.Length > 12 || countStr > 1))

return;

e.Handled = false;


/// <summary>
/// 阈值文本框键抬起事件,检测阈值是否正确,不正确改为0.8f
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void txtThreshold_KeyUp(object sender, KeyEventArgs e)

//如果输入的内容不正确改为默认值
if (!float.TryParse(txtThreshold.Text.Trim(), out threshold))

threshold = 0.8f;


#endregion
#region 窗体关闭
/// <summary>
/// 窗体关闭事件
/// </summary>
private void Form_Closed(object sender, FormClosedEventArgs e)

try

if (rgbVideoSource.IsRunning)

btnStartVideo_Click(sender, e); //关闭摄像头

//销毁引擎
int retCode = ASFFunctions.ASFUninitEngine(pImageEngine);
Console.WriteLine("UninitEngine pImageEngine Result:" + retCode);
//销毁引擎
retCode = ASFFunctions.ASFUninitEngine(pVideoEngine);
Console.WriteLine("UninitEngine pVideoEngine Result:" + retCode);
//销毁引擎
retCode = ASFFunctions.ASFUninitEngine(pVideoRGBImageEngine);
Console.WriteLine("UninitEngine pVideoImageEngine Result:" + retCode);
//销毁引擎
retCode = ASFFunctions.ASFUninitEngine(pVideoIRImageEngine);
Console.WriteLine("UninitEngine pVideoIRImageEngine Result:" + retCode);

catch (Exception ex)

Console.WriteLine("UninitEngine pImageEngine Error:" + ex.Message);


#endregion
#region 公用方法
/// <summary>
/// 恢复使用/禁用控件列表控件
/// </summary>
/// <param name="isEnable"></param>
/// <param name="controls">控件列表</param>
private void ControlsEnable(bool isEnable,params Control[] controls)

if(controls == null || controls.Length <= 0)

return;

foreach(Control control in controls)

control.Enabled = isEnable;


/// <summary>
/// 校验图片
/// </summary>
/// <param name="imagePath"></param>
/// <returns></returns>
private bool checkImage(string imagePath)

if (imagePath == null)

AppendText("图片不存在,请确认后再导入\\r\\n");
return false;

try

//判断图片是否正常,如将其他文件把后缀改为.jpg,这样就会报错
Image image = ImageUtil.readFromFile(imagePath);
if(image == null)

throw new Exception();
else

image.Dispose();


catch

AppendText(string.Format("0 图片格式有问题,请确认后再导入\\r\\n", imagePath));
return false;

FileInfo fileCheck = new FileInfo(imagePath);
if (fileCheck.Exists == false)

AppendText(string.Format("0 不存在\\r\\n", fileCheck.Name));
return false;

else if (fileCheck.Length > maxSize)

AppendText(string.Format("0 图片大小超过2M,请压缩后再导入\\r\\n", fileCheck.Name));
return false;

else if (fileCheck.Length < 2)

AppendText(string.Format("0 图像质量太小,请重新选择\\r\\n", fileCheck.Name));
return false;

return true;

/// <summary>
/// 追加公用方法
/// </summary>
/// <param name="message"></param>
private void AppendText(string message)

logBox.AppendText(message);

/// <summary>
/// 判断参数0与参数1是否在误差允许范围内
/// </summary>
/// <param name="arg0">参数0</param>
/// <param name="arg1">参数1</param>
/// <returns></returns>
private bool isInAllowErrorRange(float arg0, float arg1)

bool rel = false;
if (arg0 > arg1 - allowAbleErrorRange && arg0 < arg1 + allowAbleErrorRange)

rel = true;

return rel;

#endregion

以上是关于C# 离线人脸识别 ArcSoft的主要内容,如果未能解决你的问题,请参考以下文章

基于虹软(ArcSoft)实现人脸识别——Android版

虹软人脸识别活体检测只有Android版本的嘛 ?

Qt编写百度离线版人脸识别+比对+活体检测

人脸识别活体识别源码APP后台接口设计

为啥人脸识别要用彩灯

我们公司要做人脸识别的考勤,哪里有免费的活体检测安卓版本SDK?可以识别真人的