C# 离线人脸识别 ArcSoft

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


虹软人脸识别服务是免费的。最重要的是它还支持离线识别,并且提供Android、iOS、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和SDKKEY{0}!", 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("RGB{0}", isLiveness ? "活体" : "假体"));
}
else
{
//显示消息
trackRGBUnit.message = string.Format("RGB{0}", isLiveness ? "活体" : "假体");
}
}
else
{
//显示消息
trackRGBUnit.message = string.Format("RGB{0}", 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("IR{0}", 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
}

656e783ede54d4861761b9b00cb479a3.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/290293.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【mongoDB运维篇③】replication set复制集

介绍 replicattion set 多台服务器维护相同的数据副本,提高服务器的可用性,总结下来有以下好处: 数据备份与恢复读写分离MongoDB 复制集的结构以及基本概念 正如上图所示&#xff0c;MongoDB 复制集的架构中&#xff0c;主要分为两部分&#xff1a;主节点&#xff08;Primary&a…

c++ long 转 short_C精品编程之——C语言的数据类型、运算符、表达式,精品课程...

在前边的文章分享中&#xff0c;我们已经看到程序中使用的各种变量都应预先加以说明&#xff0c;即先说明&#xff0c;后使用。对变量的说明可以包括三个方面&#xff1a;数据类型存储类型作用域在本课中&#xff0c;我们只介绍数据类型说明。其它说明在以后各章中陆续介绍。所…

李宏毅Reinforcement Learning强化学习入门笔记

文章目录Concepts in Reinforcement LearningDifficulties in RLA3C Method Brief IntroductionPolicy-based Approach - Learn an Actor (Policy Gradient Method)1. Decide Function of Actor Model (NN? ...)2. Decide Goodness of this Function3. Choose the best functi…

《BI项目笔记》数据源视图设置

原文:《BI项目笔记》数据源视图设置目的数据源视图是物理源数据库和分析维度与多维数据集之间的逻辑数据模型。在创建数据源视图时&#xff0c;需要在源数据库中指定包含创建维度和多维数据集所需要的数据表格和视图。BIDS与数据库连接&#xff0c;读取表格和视图定义&#xff…

201521123070 《JAVA程序设计》第13周学习总结

1. 本章学习总结 以你喜欢的方式&#xff08;思维导图、OneNote或其他&#xff09;归纳总结多网络相关内容。 2. 书面作业 Q1. 网络基础 1.1 比较ping www.baidu.com与ping cec.jmu.edu.cn&#xff0c;分析返回结果有何不同&#xff1f;为什么会有这样的不同&#xff1f; 1.2 t…

.NET 7 预览版2 的亮点之 NativeAOT 回顾

.NET 中备受追捧和期待已久的功能NativeAOT终于出现在本周的.NET 7 预览版2中&#xff0c;该项目的工作仍在继续&#xff0c;该版本将 NativeAOT 从实验性的 dotnet/runtimelab repo 中移出合并进入稳定的运行时库 dotnet/runtime repo&#xff0c;但尚未在 dotnet SDK 中添加足…

c语言十佳运动员有奖评选系统_2019年沃德十佳内饰解读

​2019年沃德十佳内饰解读​mp.weixin.qq.com在这个世界上&#xff0c;要判定一件事物的成功与否并不容易&#xff0c;仅靠主观判断远远不够&#xff0c;而是需要能够进行量化判断的标准和成果。正如运动员需要金牌和冠军积淀&#xff0c;导演和演员需要奖项傍身一样&#xff0…

Mybatis——返回类型为 集合嵌套集合 应该如何处理

2019独角兽企业重金招聘Python工程师标准>>> 最近在练习时 遇到了类似于 企鹅里的好友分组功能&#xff0c;使用的持久层框架是mybatis 第一次处理这种关系 记录一下 备忘。。 首先是表结构&#xff1a; <user_group > 好友分组 、 <t_group> 用户与好友…

为什么用 windbg 看 !address 显示出的Free是128T 大小?

总是被朋友问&#xff0c;用 !address -summary 显示出上面的 Free ≈ 128T 到底是什么意思&#xff1f;我的空闲内存不可能有这么大,不管怎么说&#xff0c;先上命令。0:009> !address -summary--- Usage Summary ---------------- RgnCount ----------- Total Size ------…

DeepMind 的马尔可夫决策过程(MDP)课堂笔记

DeepMind Teaching by David Silver 视频课程链接&#xff08;Youtube资源&#xff0c;需梯子&#xff09;&#xff1a;https://youtu.be/lfHX2hHRMVQ 文章目录DeepMind Teaching by David Silver1. 马尔可夫过程&#xff08;Markov Processes&#xff09;2. 马尔可夫回报过程…

深入Java集合学习系列:ConcurrentHashMap之实现细节

http://www.iteye.com/topic/344876 ConcurrentHashMap是Java 5中支持高并发、高吞吐量的线程安全HashMap实现。在这之前我对ConcurrentHashMap只有一些肤浅的理解&#xff0c;仅知道它采用了多个锁&#xff0c;大概也足够了。但是在经过一次惨痛的面试经历之后&#xff0c;我觉…

基于小波变换的信号降噪处理及仿真研究_信号处理方法推荐--1(转载自用,侵删)...

综述作者&#xff1a;aresmiki链接&#xff1a;https://www.zhihu.com/question/23701194/answer/167005497来源&#xff1a;知乎著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。非平稳信号处理应该是现在信号处理技术最新的也是最热的研究方…

js温故而知新11(AJAX)——学习廖雪峰的js教程

Asynchronous JavaScript and XML&#xff0c;意思就是用JavaScript执行异步网络请求。 如果仔细观察一个Form的提交&#xff0c;你就会发现&#xff0c;一旦用户点击“Submit”按钮&#xff0c;表单开始提交&#xff0c;浏览器就会刷新页面&#xff0c;然后在新页面里告诉你操…

最流行的 .NET 开源项目合集

Github 上有很多优秀的 .NET 开源项目&#xff0c;它们很适合用来学习 .NET 、设计模式和架构。nopCommerce https://github.com/nopSolutions/nopCommercestar: 7k ⭐nopCommerce 是最受欢迎最好的开源电子商务购物车解决方案之一&#xff0c;它基于 ASP.NET Core&#xff…

GFS(Genetic Fuzzy Systems)—— 基于专家先验规则库和遗传算法相结合的智能体决策算法

文章目录1. FIS 系统&#xff08;Fuzzy Inference Systems&#xff09;1.1 什么是 FIS 系统&#xff1f;1.2 使用 FIS 算法的几个步骤2. GFS 系统&#xff08;GA FIS&#xff09;2.1 什么是基因遗传算法&#xff08;GA&#xff09;?2.2 使用GA算法进化FIS规则库在大规模的多智…

3-unit1 IPv6网络的管理

##########IPv6网络的管理#############学习目标了解IPv6管理IPv6##)IPv6简介Internet Protocol Version 6IPv6是IETF&#xff08;和互联网工程任务组&#xff09;设计的用与替代现行版本IP协议的下一代IP协议。IPv6采用128位2进制数码表示##IPv6示意图:##IPv6表示方式为方便操…

Xamarin效果第一篇之时间轴

一直都想找个时间玩玩移动端,中间也去各种的调研快速的方式去开发;过程中还是太浮躁木有沉下心去学习;好早尝试过Flutter,二点让我不爽:1、配置环境好费劲(VS把我惯坏了)&#xff1b;2、套娃的写法真是恶心;来看看酸爽不:因为一直都是C#开发,所以这次再次摸索Xamarin去开发;前面…

Lync 小技巧-42-动态-IP-统一沟通-环境-IP-变更后-操作

1. 查看-你的-公网IPhttp://www.ip138.com/2. 修改-你的-公网A记录https://www.godaddy.com/3. 修改-你的-拓朴-For-边缘服务器3.1.远程-前端服务器3.2.下载-拓朴3.3.选择-边缘服务器3.4.选择-边缘服务器3.5.修改-公网IP116.230.255.783.5.发布-拓朴3.6.导出-拓朴3.7.复制-拓朴…

Blazor University (1)介绍 - 什么是 Blazor?

原文链接&#xff1a;https://blazor-university.com/overview/what-is-blazor/什么是 Blazor&#xff1f;Blazor 是一个单页应用程序开发框架。Blazor 这个名称是单词 Browser 和 Razor&#xff08;.NET HTML 视图生成引擎&#xff09;的组合/变异。这意味着 Blazor 不必在服务…

jquery特效(1)—点击展示与隐藏全文

下班了~~~我把今天整理的一个jquery小特效发一下&#xff0c;个人觉得比较简单&#xff0c;嗖嗖的就写出来了~~~ 下面先来看最终的动态效果&#xff1a; 一、来看一下主体框架程序&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8&quo…