C# 离线人脸识别 ArcSoft
生活随笔
收集整理的這篇文章主要介紹了
C# 离线人脸识别 ArcSoft
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
人臉識(shí)別&比對(duì)發(fā)展到今天,已經(jīng)是一個(gè)非常成熟的技術(shù)了,而且應(yīng)用在生活的方方面面,比如手機(jī)、車站、天網(wǎng)等。
虹軟人臉識(shí)別服務(wù)是免費(fèi)的。最重要的是它還支持離線識(shí)別,并且提供Android、iOS、C++、C#版SDK,現(xiàn)在已經(jīng)升級(jí)到全新的3.0版本,支持活體識(shí)別。包含人臉檢測(cè),人臉對(duì)比,人臉檢索功能.
過(guò)程如下:
1、 傳入一張單人臉照片;
2、調(diào)用檢測(cè)人臉函數(shù)ASFDetectFaces,成功返回人臉信息的指針;
3、使用 Marshal.ReadByte(intPtr,offset) 函數(shù)讀出字節(jié)數(shù),發(fā)現(xiàn)前16個(gè)字節(jié)是人臉框范圍。
/// <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位數(shù) var is64CPU = Environment.Is64BitProcess; if (string.IsNullOrWhiteSpace(appId) || string.IsNullOrWhiteSpace(is64CPU?sdkKey64: sdkKey32)) { //禁用相關(guān)功能按鈕 ControlsEnable(false, chooseMultiImgBtn, matchBtn, btnClearFaceList, chooseImgBtn); MessageBox.Show(string.Format("請(qǐng)?jiān)贏pp.config配置文件中先配置APP_ID和SDKKEY{0}!", is64CPU ? "64" : "32")); return; } //在線激活引擎 如出現(xiàn)錯(cuò)誤,1.請(qǐng)先確認(rèn)從官網(wǎng)下載的sdk庫(kù)已放到對(duì)應(yīng)的bin中,2.當(dāng)前選擇的CPU為x86或者x64 int retCode = 0; try { retCode = ASFFunctions.ASFActivation(appId, is64CPU ? sdkKey64 : sdkKey32); } catch (Exception ex) { //禁用相關(guān)功能按鈕 ControlsEnable(false, chooseMultiImgBtn, matchBtn, btnClearFaceList, chooseImgBtn); if (ex.Message.Contains("無(wú)法加載 DLL")) { MessageBox.Show("請(qǐng)將sdk相關(guān)DLL放入bin對(duì)應(yīng)的x86或x64下的文件夾中!"); } else { MessageBox.Show("激活引擎失敗!"); } return; } Console.WriteLine("Activate Result:" + retCode); //初始化引擎 uint detectMode = DetectionMode.ASF_DETECT_MODE_IMAGE; //Video模式下檢測(cè)臉部的角度優(yōu)先值 int videoDetectFaceOrientPriority = ASF_OrientPriority.ASF_OP_0_HIGHER_EXT; //Image模式下檢測(cè)臉部的角度優(yōu)先值 int imageDetectFaceOrientPriority = ASF_OrientPriority.ASF_OP_0_ONLY; //人臉在圖片中所占比例,如果需要調(diào)整檢測(cè)人臉尺寸請(qǐng)修改此值,有效數(shù)值為2-32 int detectFaceScaleVal = 16; //最大需要檢測(cè)的人臉個(gè)數(shù) int detectFaceMaxNum = 5; //引擎初始化時(shí)需要初始化的檢測(cè)功能組合 int combinedMask = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_AGE | FaceEngineMask.ASF_GENDER | FaceEngineMask.ASF_FACE3DANGLE; //初始化引擎,正常值為0,其他返回值請(qǐng)參考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("引擎初始化失敗!錯(cuò)誤碼為:{0}\n", retCode)); if (retCode != 0) { //禁用相關(guān)功能按鈕 ControlsEnable(false, chooseMultiImgBtn, matchBtn, btnClearFaceList, chooseImgBtn); } //初始化視頻模式下人臉檢測(cè)引擎 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); //如果沒(méi)有可用攝像頭,“啟用攝像頭”按鈕禁用,否則使可用 if (filterInfoCollection.Count == 0) { btnStartVideo.Enabled = false; } else { btnStartVideo.Enabled = true; } } #endregion #region 注冊(cè)人臉按鈕事件 private object locker = new object(); /// <summary> /// 人臉庫(kù)圖片選擇按鈕事件 /// </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]); } } //人臉檢測(cè)以及提取人臉特征 ThreadPool.QueueUserWorkItem(new WaitCallback(delegate { //禁止點(diǎn)擊按鈕 Invoke(new Action(delegate { chooseMultiImgBtn.Enabled = false; matchBtn.Enabled = false; btnClearFaceList.Enabled = false; chooseImgBtn.Enabled = false; btnStartVideo.Enabled = false; })); //人臉檢測(cè)和剪裁 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); } //人臉檢測(cè) ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(pImageEngine, image); //判斷檢測(cè)結(jié)果 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) + "號(hào)", 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}號(hào)未檢測(cè)到人臉\r\n", i)); } else { AppendText(string.Format("已提取{0}號(hào)人臉特征值,[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(); } } //允許點(diǎn)擊按鈕 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 清空人臉庫(kù)按鈕事件 /// <summary> /// 清除人臉庫(kù)事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnClearFaceList_Click(object sender, EventArgs e) { //清除數(shù)據(jù) imageLists.Images.Clear(); imageList.Items.Clear(); foreach (IntPtr intptr in imagesFeatureList) { MemoryUtil.Free(intptr); } imagesFeatureList.Clear(); imagePathList.Clear(); } #endregion #region 選擇識(shí)別圖按鈕事件 /// <summary> /// “選擇識(shí)別圖片”按鈕事件 /// </summary> private void ChooseImg(object sender, EventArgs e) { lblCompareInfo.Text = ""; //判斷引擎是否初始化成功 if (pImageEngine == IntPtr.Zero) { //禁用相關(guān)功能按鈕 ControlsEnable(false, chooseMultiImgBtn, matchBtn, btnClearFaceList, chooseImgBtn); MessageBox.Show("請(qǐng)先初始化引擎!"); 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; //檢測(cè)圖片格式 if (!checkImage(image1Path)) { return; } DateTime detectStartTime = DateTime.Now; AppendText(string.Format("------------------------------開(kāi)始檢測(cè),時(shí)間:{0}------------------------------\n", detectStartTime.ToString("yyyy-MM-dd HH:mm:ss:ms"))); //獲取文件,拒絕過(guò)大的圖片 FileInfo fileInfo = new FileInfo(image1Path); if (fileInfo.Length > maxSize) { MessageBox.Show("圖像文件最大為2MB,請(qǐng)壓縮后再導(dǎo)入!"); AppendText(string.Format("------------------------------檢測(cè)結(jié)束,時(shí)間:{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("圖像數(shù)據(jù)獲取失敗,請(qǐng)稍后重試!"); AppendText(string.Format("------------------------------檢測(cè)結(jié)束,時(shí)間:{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("圖像數(shù)據(jù)獲取失敗,請(qǐng)稍后重試!"); AppendText(string.Format("------------------------------檢測(cè)結(jié)束,時(shí)間:{0}------------------------------\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ms"))); AppendText("\n"); return; } //調(diào)整圖像寬度,需要寬度為4的倍數(shù) if (srcImage.Width % 4 != 0) { srcImage = ImageUtil.ScaleImage(srcImage, srcImage.Width - (srcImage.Width % 4), srcImage.Height); } //調(diào)整圖片數(shù)據(jù),非常重要 ImageInfo imageInfo = ImageUtil.ReadBMP(srcImage); if (imageInfo == null) { MessageBox.Show("圖像數(shù)據(jù)獲取失敗,請(qǐng)稍后重試!"); AppendText(string.Format("------------------------------檢測(cè)結(jié)束,時(shí)間:{0}------------------------------\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ms"))); AppendText("\n"); return; } //人臉檢測(cè) ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(pImageEngine, imageInfo); //年齡檢測(cè) int retCode_Age = -1; ASF_AgeInfo ageInfo = FaceUtil.AgeEstimation(pImageEngine, imageInfo, multiFaceInfo, out retCode_Age); //性別檢測(cè) int retCode_Gender = -1; ASF_GenderInfo genderInfo = FaceUtil.GenderEstimation(pImageEngine, imageInfo, multiFaceInfo, out retCode_Gender); //3DAngle檢測(cè) 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} - 未檢測(cè)出人臉!\n\n", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"))); AppendText(string.Format("------------------------------檢測(cè)結(jié)束,時(shí)間:{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; //標(biāo)記出檢測(cè)到的人臉 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("年齡檢測(cè)失敗,返回{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("性別檢測(cè)失敗,返回{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檢測(cè)失敗,返回{0}!\n\n", retCode_3DAngle)); } else { //角度狀態(tài) 非0表示人臉不可信 face3DStatus = MemoryUtil.PtrToStructure<int>(face3DAngleInfo.status + MemoryUtil.SizeOf<int>() * i); //roll為側(cè)傾角,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} - 人臉坐標(biāo):[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} - 人臉數(shù)量:{1}\n\n", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), multiFaceInfo.faceNum)); DateTime detectEndTime = DateTime.Now; AppendText(string.Format("------------------------------檢測(cè)結(jié)束,時(shí)間:{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); //清空上次的匹配結(jié)果 for (int i = 0; i < imagesFeatureList.Count; i++) { imageList.Items[i].Text = string.Format("{0}號(hào)", i); } //獲取縮放比例 float scaleRate = ImageUtil.getWidthAndHeight(srcImage.Width, srcImage.Height, picImageCompare.Width, picImageCompare.Height); //縮放圖片 srcImage = ImageUtil.ScaleImage(srcImage, picImageCompare.Width, picImageCompare.Height); //添加標(biāo)記 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); //顯示標(biāo)記后的圖像 picImageCompare.Image = srcImage; } } #endregion #region 開(kāi)始匹配按鈕事件 /// <summary> /// 匹配事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void matchBtn_Click(object sender, EventArgs e) { if (imagesFeatureList.Count == 0) { MessageBox.Show("請(qǐng)注冊(cè)人臉!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (image1Feature == IntPtr.Zero) { if (picImageCompare.Image == null) { MessageBox.Show("請(qǐng)選擇識(shí)別圖!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error); } else { MessageBox.Show("比對(duì)失敗,識(shí)別圖未提取到特征值!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error); } return; } //標(biāo)記已經(jīng)做了匹配比對(duì),在開(kāi)啟視頻的時(shí)候要清除比對(duì)結(jié)果 isCompare = true; float compareSimilarity = 0f; int compareNum = 0; AppendText(string.Format("------------------------------開(kāi)始比對(duì),時(shí)間:{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}號(hào)比對(duì)結(jié)果:{1}\r\n", i, similarity)); imageList.Items[i].Text = string.Format("{0}號(hào)({1})", i, similarity); if (similarity > compareSimilarity) { compareSimilarity = similarity; compareNum = i; } } if (compareSimilarity > 0) { lblCompareInfo.Text = " " + compareNum + "號(hào)," + compareSimilarity; } AppendText(string.Format("------------------------------比對(duì)結(jié)束,時(shí)間:{0}------------------------------\r\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ms"))); } #endregion #region 視頻檢測(cè)相關(guān)(<攝像頭按鈕點(diǎn)擊事件、攝像頭Paint事件、特征比對(duì)、攝像頭播放完成事件>) /// <summary> /// 攝像頭按鈕點(diǎn)擊事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnStartVideo_Click(object sender, EventArgs e) { //在點(diǎn)擊開(kāi)始的時(shí)候再坐下初始化檢測(cè),防止程序啟動(dòng)時(shí)有攝像頭,在點(diǎn)擊攝像頭按鈕之前將攝像頭拔掉的情況 initVideo(); //必須保證有可用攝像頭 if (filterInfoCollection.Count == 0) { MessageBox.Show("未檢測(cè)到攝像頭,請(qǐng)確保已安裝攝像頭或驅(qū)動(dòng)!"); return; } if (rgbVideoSource.IsRunning || irVideoSource.IsRunning) { btnStartVideo.Text = "啟用攝像頭"; //關(guān)閉攝像頭 if (irVideoSource.IsRunning) { irVideoSource.SignalToStop(); irVideoSource.Hide(); } if (rgbVideoSource.IsRunning) { rgbVideoSource.SignalToStop(); rgbVideoSource.Hide(); } //“選擇識(shí)別圖”、“開(kāi)始匹配”按鈕可用,閾值控件禁用 chooseImgBtn.Enabled = true; matchBtn.Enabled = true; txtThreshold.Enabled = false; } else { if (isCompare) { //比對(duì)結(jié)果清除 for (int i = 0; i < imagesFeatureList.Count; i++) { imageList.Items[i].Text = string.Format("{0}號(hào)", i); } lblCompareInfo.Text = ""; isCompare = false; } //“選擇識(shí)別圖”、“開(kāi)始匹配”按鈕禁用,閾值控件可用,顯示攝像頭控件 txtThreshold.Enabled = true; rgbVideoSource.Show(); irVideoSource.Show(); chooseImgBtn.Enabled = false; matchBtn.Enabled = false; btnStartVideo.Text = "關(guān)閉攝像頭"; //獲取filterInfoCollection的總數(shù) int maxCameraCount = filterInfoCollection.Count; //如果配置了兩個(gè)不同的攝像頭索引 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(); //雙攝標(biāo)志設(shè)為true isDoubleShot = true; } else { //僅打開(kāi)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事件,圖像顯示到窗體上,得到每一幀圖像,并進(jìn)行處理 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void videoSource_Paint(object sender, PaintEventArgs e) { if (rgbVideoSource.IsRunning) { //得到當(dāng)前RGB攝像頭下的圖片 Bitmap bitmap = rgbVideoSource.GetCurrentVideoFrame(); if (bitmap == null) { return; } //檢測(cè)人臉,得到Rect框 ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(pVideoEngine, bitmap); //得到最大人臉 ASF_SingleFaceInfo maxFace = FaceUtil.GetMaxFace(multiFaceInfo); //得到Rect MRECT rect = maxFace.faceRect; //檢測(cè)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; //根據(jù)Rect進(jìn)行畫框 g.DrawRectangle(Pens.Red, x, y, width, height); if (trackRGBUnit.message != "" && x > 0 && y > 0) { //將上一幀檢測(cè)結(jié)果顯示到頁(yè)面上 g.DrawString(trackRGBUnit.message, font, trackRGBUnit.message.Contains("活體") ? blueBrush : yellowBrush, x, y - 15); } //保證只檢測(cè)一幀,防止頁(yè)面卡頓以及出現(xiàn)其他內(nèi)存被占用情況 if (isRGBLock == false) { isRGBLock = true; //異步處理提取特征值和比對(duì),不然頁(yè)面會(huì)比較卡 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; //調(diào)整圖片數(shù)據(jù),非常重要 ImageInfo imageInfo = ImageUtil.ReadBMP(bitmap); if (imageInfo == null) { return; } int retCode_Liveness = -1; //RGB活體檢測(cè) ASF_LivenessInfo liveInfo = FaceUtil.LivenessInfo_RGB(pVideoRGBImageEngine, imageInfo, multiFaceInfo, out retCode_Liveness); //判斷檢測(cè)結(jié)果 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; //得到比對(duì)結(jié)果 int result = compareFeature(feature, out similarity); MemoryUtil.Free(feature); if (result > -1) { //將比對(duì)結(jié)果放到顯示消息中,用于最新顯示 trackRGBUnit.message = string.Format(" {0}號(hào) {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人臉框,對(duì)比人臉框后進(jìn)行IR活體檢測(cè) /// </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; //檢測(cè)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; //根據(jù)Rect進(jìn)行畫框 g.DrawRectangle(Pens.Red, x, y, width, height); if (trackIRUnit.message != "" && x > 0 && y > 0) { //將上一幀檢測(cè)結(jié)果顯示到頁(yè)面上 g.DrawString(trackIRUnit.message, font, trackIRUnit.message.Contains("活體") ? blueBrush : yellowBrush, x, y - 15); } //保證只檢測(cè)一幀,防止頁(yè)面卡頓以及出現(xiàn)其他內(nèi)存被占用情況 if (isIRLock == false) { isIRLock = true; //異步處理提取特征值和比對(duì),不然頁(yè)面會(huì)比較卡 ThreadPool.QueueUserWorkItem(new WaitCallback(delegate { if (rect.left != 0 && rect.right != 0 && rect.top != 0 && rect.bottom != 0) { bool isLiveness = false; try { //得到當(dāng)前攝像頭下的圖片 if (irBitmap != null) { //檢測(cè)人臉,得到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圖片檢測(cè)的人臉框與IR攝像頭檢測(cè)的人臉框偏移量是否在誤差允許范圍內(nèi) 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; //將圖片進(jìn)行灰度轉(zhuǎn)換,然后獲取圖片數(shù)據(jù) ImageInfo irImageInfo = ImageUtil.ReadBMP_IR(irBitmap); if (irImageInfo == null) { return; } //IR活體檢測(cè) ASF_LivenessInfo liveInfo = FaceUtil.LivenessInfo_IR(pVideoIRImageEngine, irImageInfo, irMultiFaceInfo, out retCode_Liveness); //判斷檢測(cè)結(jié)果 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比較結(jié)果 /// </summary> /// <param name="feature"></param> /// <returns></returns> private int compareFeature(IntPtr feature, out float similarity) { int result = -1; similarity = 0f; //如果人臉庫(kù)不為空,則進(jìn)行人臉匹配 if (imagesFeatureList != null && imagesFeatureList.Count > 0) { for (int i = 0; i < imagesFeatureList.Count; i++) { //調(diào)用人臉匹配方法,進(jìn)行匹配 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 閾值相關(guān) /// <summary> /// 閾值文本框鍵按下事件,檢測(cè)輸入內(nèi)容是否正確,不正確不能輸入 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void txtThreshold_KeyPress(object sender, KeyPressEventArgs e) { //阻止從鍵盤輸入鍵 e.Handled = true; //是數(shù)值鍵,回退鍵,.能輸入,其他不能輸入 if (char.IsDigit(e.KeyChar) || e.KeyChar == 8 || e.KeyChar == '.') { //渠道當(dāng)前文本框的內(nèi)容 string thresholdStr = txtThreshold.Text.Trim(); int countStr = 0; int startIndex = 0; //如果當(dāng)前輸入的內(nèi)容是否是“.” if (e.KeyChar == '.') { countStr = 1; } //檢測(cè)當(dāng)前內(nèi)容是否含有.的個(gè)數(shù) if (thresholdStr.IndexOf('.', startIndex) > -1) { countStr += 1; } //如果輸入的內(nèi)容已經(jīng)超過(guò)12個(gè)字符, if (e.KeyChar != 8 && (thresholdStr.Length > 12 || countStr > 1)) { return; } e.Handled = false; } } /// <summary> /// 閾值文本框鍵抬起事件,檢測(cè)閾值是否正確,不正確改為0.8f /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void txtThreshold_KeyUp(object sender, KeyEventArgs e) { //如果輸入的內(nèi)容不正確改為默認(rèn)值 if (!float.TryParse(txtThreshold.Text.Trim(), out threshold)) { threshold = 0.8f; } } #endregion #region 窗體關(guān)閉 /// <summary> /// 窗體關(guān)閉事件 /// </summary> private void Form_Closed(object sender, FormClosedEventArgs e) { try { if (rgbVideoSource.IsRunning) { btnStartVideo_Click(sender, e); //關(guān)閉攝像頭 } //銷毀引擎 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> /// 恢復(fù)使用/禁用控件列表控件 /// </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> /// 校驗(yàn)圖片 /// </summary> /// <param name="imagePath"></param> /// <returns></returns> private bool checkImage(string imagePath) { if (imagePath == null) { AppendText("圖片不存在,請(qǐng)確認(rèn)后再導(dǎo)入\r\n"); return false; } try { //判斷圖片是否正常,如將其他文件把后綴改為.jpg,這樣就會(huì)報(bào)錯(cuò) Image image = ImageUtil.readFromFile(imagePath); if(image == null) { throw new Exception(); }else { image.Dispose(); } } catch { AppendText(string.Format("{0} 圖片格式有問(wèn)題,請(qǐng)確認(rèn)后再導(dǎo)入\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} 圖片大小超過(guò)2M,請(qǐng)壓縮后再導(dǎo)入\r\n", fileCheck.Name)); return false; } else if (fileCheck.Length < 2) { AppendText(string.Format("{0} 圖像質(zhì)量太小,請(qǐng)重新選擇\r\n", fileCheck.Name)); return false; } return true; } /// <summary> /// 追加公用方法 /// </summary> /// <param name="message"></param> private void AppendText(string message) { logBox.AppendText(message); } /// <summary> /// 判斷參數(shù)0與參數(shù)1是否在誤差允許范圍內(nèi) /// </summary> /// <param name="arg0">參數(shù)0</param> /// <param name="arg1">參數(shù)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 }總結(jié)
以上是生活随笔為你收集整理的C# 离线人脸识别 ArcSoft的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Avalonia跨平台入门第二十三篇之滚
- 下一篇: c# char unsigned_dll