上一篇文章https://blog.xgcos.com/show/306.html已经介绍了如何注册申请虹软的人脸识别SDK,也总结过虹软的SDK是VC++封装的,说明文档也都是VC++的调用方式,这対使用其他变成语言的小伙伴来说可能就遇到难题了。这篇文章最主要是介绍研究如何通过C#也能调用,并且提供一个人脸识别的DEMO供大家学习,现在进入正题,介绍一下C#是通过什么方式来引用C++的SDK动态链接库。
首先我们先来理解一个关键字DllImport,顾名思义就是把DLL加载到程序的意思。首先看下面一段代码:
[DllImport("libarcsoft_fsdk_face_detection.dll", EntryPoint = "AFD_FSDK_InitialFaceEngine", CallingConvention = CallingConvention.Cdecl)]
这段代码的意思就是引入了libarcsoft_fsdk_face_detection.dll这个动态链接库,然后再通过堆栈可变参数数目的方式调用函数AFD_FSDK_InitialFaceEngine。
通过这样的方式咋们就可以引用动态库中开放的所有函数,所以我们通过这种方式把动态库包装了一遍供C#方便调用,参照以下代码:
AFDFUnction32.cs类
public class AFDFunction32 { /// <summary> /// 初始化脸部检测引擎 /// </summary> /// <param name="appId">用户申请SDK时获取的AppId</param> /// <param name="sdkKey">用户申请 SDK 时获取的 SDK Key</param> /// <param name="pMem">分配给引擎使用的内存地址</param> /// <param name="lMemSize">分配给引擎使用的内存大小</param> /// <param name="pEngine">引擎 handle</param> /// <param name="iOrientPriority">期望的脸部检测角度的优先级</param> /// <param name="nScale">用于数值表示的最小人脸尺寸 有效值范围[2,50] 推荐值 16</param> /// <param name="nMaxFaceNum">用户期望引擎最多能检测出的人脸数 有效值范围[1,100]</param> /// <returns></returns> [DllImport("libarcsoft_fsdk_face_detection.dll", EntryPoint = "AFD_FSDK_InitialFaceEngine", CallingConvention = CallingConvention.Cdecl)] public static extern int AFD_FSDK_InitialFaceEngine(string AppId, string SDKKey, IntPtr pMem, int lMemSize, ref IntPtr pEngine, int iOrientPriority, int nScale, int nMaxFaceNum); /// <summary> /// 根据输入的图像检测出人脸位置,一般用于静态图像检测 /// </summary> /// <param name="pEngine">引擎 handle</param> /// <param name="pImgData">带检测图像信息</param> /// <param name="pFaceRes">人脸检测结果</param> /// <returns></returns> [DllImport("libarcsoft_fsdk_face_detection.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int AFD_FSDK_StillImageFaceDetection(IntPtr pEngine, IntPtr pImgData, ref IntPtr pFaceRes); /// <summary> /// 销毁引擎,释放相应资源 /// </summary> /// <param name="pEngine">引擎 handle</param> /// <returns></returns> [DllImport("libarcsoft_fsdk_face_detection.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int AFD_FSDK_UninitialFaceEngine(IntPtr pEngine); }AFRFUnction32.cs类
public class AFRFunction32 { /// <summary> /// 初始化引擎 /// </summary> /// <param name="appId">用户申请SDK时获取的AppId</param> /// <param name="sdkKey">用户申请 SDK 时获取的 SDK Key</param> /// <param name="pMem">分配给引擎使用的内存地址</param> /// <param name="lMemSize">分配给引擎使用的内存大小</param> /// <param name="pEngine">引擎 handle</param> /// <returns></returns> [DllImport("libarcsoft_fsdk_face_recognition.dll", EntryPoint = "AFR_FSDK_InitialEngine", CallingConvention = CallingConvention.Cdecl)] public static extern int AFR_FSDK_InitialEngine(string AppId, string SDKKey, IntPtr pMem, int lMemSize, ref IntPtr phEngine); /// <summary> /// 获取脸部特征 /// </summary> /// <param name="hEngine">引擎 handle</param> /// <param name="pInputImage">输入的图像数据</param> /// <param name="pFaceRes">已检测到到的脸部信息</param> /// <param name="pFaceModels">提取的脸部特征信息</param> /// <returns></returns> [DllImport("libarcsoft_fsdk_face_recognition.dll", EntryPoint = "AFR_FSDK_ExtractFRFeature", CallingConvention = CallingConvention.Cdecl)] public static extern int AFR_FSDK_ExtractFRFeature(IntPtr hEngine, IntPtr pInputImage, IntPtr pFaceRes, IntPtr pFaceModels); /// <summary> /// 脸部特征比较 /// </summary> /// <param name="hEngine">引擎 handle</param> /// <param name="reffeature">已有脸部特征信息</param> /// <param name="probefeature">被比较的脸部特征信息</param> /// <param name="pfSimilScore">相似程度数值</param> /// <returns></returns> [DllImport("libarcsoft_fsdk_face_recognition.dll", EntryPoint = "AFR_FSDK_FacePairMatching", CallingConvention = CallingConvention.Cdecl)] public static extern int AFR_FSDK_FacePairMatching(IntPtr hEngine, IntPtr reffeature, IntPtr probefeature, ref float pfSimilScore); /// <summary> /// 销毁引擎,释放相应资源 /// </summary> /// <param name="hEngine">引擎 handle</param> /// <returns></returns> [DllImport("libarcsoft_fsdk_face_recognition", EntryPoint = "AFR_FSDK_UninitialEngine", CallingConvention = CallingConvention.Cdecl)] public static extern int AFR_FSDK_UninitialEngine(IntPtr hEngine); }
通过这样的封装,我们就能通过C#调用C++中的函数,但细心一点的骚年发现很多都是IntPtr类型,这个类型日常使用C#都很少用到呀。IntPtr类型称为“平台特定的整数类型”,它们用于本机资源,如窗口句柄。资源的大小取决于使用的硬件和操作系统,但其大小总是足以包含系统的指针。对C++有所了解的骚年都知道C++的指针概念,在C#这种高级的面向对象语言已经舍弃了指针改用对象(虽然还可以用unsafe的方式使用指针),所以C#这个变量可以引用C++中的指针类型,但貌似这种封装还不够通俗,所以小西瓜再封装了一层。
ARCFace.cs类
public class ArcFace { string appId32 = "替换你的APPID"; string sdkFDKey32 = "替换你的FDKEY"; string sdkFRKey32 = "替换你的FRKEY"; int Size = 40 * 1024 * 1024; int nScale = 16; int nMaxFaceNum = 5; public ArcFace() { } /// <summary> /// 获取图片中的人脸数目 /// </summary> /// <param name="imageData">图片流数据</param> /// <param name="faceCount">返回人脸数</param> /// <returns>0为调用成功,其他返回错误码</returns> public int GetImageFaceCount(byte[] imageData, ref int faceCount) { ArcStruct.AFD_FSDK_FACERES faceRes = new ArcStruct.AFD_FSDK_FACERES(); int res = 0; IntPtr pem = new IntPtr(); res = GetStillImageFaceDetection32(BytesToBitmap(imageData), ref faceRes, ref pem); if (res == 0) { faceCount = faceRes.nFace; } else { faceCount = 0; } Marshal.FreeHGlobal(pem); return res; } /// <summary> /// 比对图片 /// </summary> /// <param name="imageData1">图片1流数据</param> /// <param name="imageData2">图片2流数据</param> /// <param name="similscore">返回相似度</param> /// <returns>0为调用成功,-1为检测图片人脸失败,其他返回错误码</returns> public int FaceMatching(Bitmap b1, Bitmap b2, ref float similscore) { similscore = 0f; ArcStruct.AFD_FSDK_FACERES faceRes1 = new ArcStruct.AFD_FSDK_FACERES(); ArcStruct.AFD_FSDK_FACERES faceRes2 = new ArcStruct.AFD_FSDK_FACERES(); IntPtr pem1 = new IntPtr(); IntPtr pem2 = new IntPtr(); int res = GetStillImageFaceDetection32(b1, ref faceRes1, ref pem1); if (res != 0) { Marshal.FreeHGlobal(pem1); return res; } res = GetStillImageFaceDetection32(b2, ref faceRes2, ref pem2); if (res != 0) { Marshal.FreeHGlobal(pem1); Marshal.FreeHGlobal(pem2); return res; } if (faceRes1.nFace == 1 && faceRes2.nFace == 1) { ArcStruct.AFR_FSDK_FaceModel faceModel1 = new ArcStruct.AFR_FSDK_FaceModel(); ArcStruct.AFR_FSDK_FaceModel faceModel2 = new ArcStruct.AFR_FSDK_FaceModel(); IntPtr pemM1 = new IntPtr(); IntPtr pemM2 = new IntPtr(); int mres = GetFaceModel32(b1, faceRes1, ref faceModel1, ref pemM1); if (mres != 0) { Marshal.FreeHGlobal(pem1); Marshal.FreeHGlobal(pem2); Marshal.FreeHGlobal(pemM1); return mres; } mres = GetFaceModel32(b2, faceRes2, ref faceModel2, ref pemM2); if (mres != 0) { Marshal.FreeHGlobal(pem1); Marshal.FreeHGlobal(pem2); Marshal.FreeHGlobal(pemM1); Marshal.FreeHGlobal(pemM2); return mres; } int ret = FacePairMatching32(faceModel1, faceModel2, ref similscore); Marshal.FreeHGlobal(pem1); Marshal.FreeHGlobal(pem2); Marshal.FreeHGlobal(pemM1); Marshal.FreeHGlobal(pemM2); return ret; } else { Marshal.FreeHGlobal(pem1); Marshal.FreeHGlobal(pem2); return -1; } } private Bitmap BytesToBitmap(byte[] Bytes) { MemoryStream stream = null; try { stream = new MemoryStream(Bytes); System.Drawing.Image returnImage = System.Drawing.Image.FromStream(stream); stream.Flush(); Bitmap bmp = (Bitmap)returnImage; return bmp; } catch (ArgumentNullException ex) { throw ex; } catch (ArgumentException ex) { throw ex; } finally { stream.Close(); } } private int GetStillImageFaceDetection32(Bitmap bitmap, ref ArcStruct.AFD_FSDK_FACERES faceRes) { IntPtr detectEngine = IntPtr.Zero; IntPtr pMem = Marshal.AllocHGlobal(Size); int retCode = AFDFunction32.AFD_FSDK_InitialFaceEngine(appId32, sdkFDKey32, pMem, Size, ref detectEngine, (int)ArcStruct.AFD_FSDK_OrientPriority.AFD_FSDK_OPF_0_HIGHER_EXT, nScale, nMaxFaceNum); if (retCode != 0) { return retCode; } ArcStruct.ASVLOFFSCREEN offInput = GetOffInput(bitmap); IntPtr offInputPtr = Marshal.AllocHGlobal(Marshal.SizeOf(offInput)); Marshal.StructureToPtr(offInput, offInputPtr, false); faceRes = new ArcStruct.AFD_FSDK_FACERES(); IntPtr faceResPtr = Marshal.AllocHGlobal(Marshal.SizeOf(faceRes)); int detectResult = AFDFunction32.AFD_FSDK_StillImageFaceDetection(detectEngine, offInputPtr, ref faceResPtr); if (detectResult == 0) { faceRes = (ArcStruct.AFD_FSDK_FACERES)Marshal.PtrToStructure(faceResPtr, typeof(ArcStruct.AFD_FSDK_FACERES)); AFDFunction32.AFD_FSDK_UninitialFaceEngine(detectEngine); Marshal.FreeHGlobal(offInputPtr); Marshal.FreeHGlobal(pMem); return 0; } else { AFDFunction32.AFD_FSDK_UninitialFaceEngine(detectEngine); Marshal.FreeHGlobal(offInputPtr); Marshal.FreeHGlobal(pMem); return detectResult; } } private int GetStillImageFaceDetection32(Bitmap bitmap, ref ArcStruct.AFD_FSDK_FACERES faceRes, ref IntPtr pMem) { IntPtr detectEngine = IntPtr.Zero; pMem = Marshal.AllocHGlobal(Size); int retCode = AFDFunction32.AFD_FSDK_InitialFaceEngine(appId32, sdkFDKey32, pMem, Size, ref detectEngine, (int)ArcStruct.AFD_FSDK_OrientPriority.AFD_FSDK_OPF_0_HIGHER_EXT, nScale, nMaxFaceNum); if (retCode != 0) { return retCode; } ArcStruct.ASVLOFFSCREEN offInput = GetOffInput(bitmap); IntPtr offInputPtr = Marshal.AllocHGlobal(Marshal.SizeOf(offInput)); Marshal.StructureToPtr(offInput, offInputPtr, false); faceRes = new ArcStruct.AFD_FSDK_FACERES(); IntPtr faceResPtr = Marshal.AllocHGlobal(Marshal.SizeOf(faceRes)); int detectResult = AFDFunction32.AFD_FSDK_StillImageFaceDetection(detectEngine, offInputPtr, ref faceResPtr); if (detectResult == 0) { faceRes = (ArcStruct.AFD_FSDK_FACERES)Marshal.PtrToStructure(faceResPtr, typeof(ArcStruct.AFD_FSDK_FACERES)); AFDFunction32.AFD_FSDK_UninitialFaceEngine(detectEngine); Marshal.FreeHGlobal(offInputPtr); return 0; } else { AFDFunction32.AFD_FSDK_UninitialFaceEngine(detectEngine); Marshal.FreeHGlobal(offInputPtr); return detectResult; } } private int GetFaceModel32(Bitmap bitmap, ArcStruct.AFD_FSDK_FACERES faceRes, ref ArcStruct.AFR_FSDK_FaceModel faceModel, ref IntPtr pMem) { IntPtr recognizeEngine = IntPtr.Zero; pMem = Marshal.AllocHGlobal(Size); int retCode = AFRFunction32.AFR_FSDK_InitialEngine(appId32, sdkFRKey32, pMem, Size, ref recognizeEngine); if (retCode != 0) { return retCode; } ArcStruct.AFR_FSDK_FaceInput faceinput = new ArcStruct.AFR_FSDK_FaceInput(); faceinput.lOrient = (int)Marshal.PtrToStructure(faceRes.lfaceOrient, typeof(int)); long longPtr = faceRes.rcFace.ToInt64(); IntPtr rectPtr = new IntPtr(longPtr); ArcStruct.MRECT rect = (ArcStruct.MRECT)Marshal.PtrToStructure(rectPtr, typeof(ArcStruct.MRECT)); faceinput.rcFace = rect; IntPtr faceInputPtr = Marshal.AllocHGlobal(Marshal.SizeOf(faceinput)); Marshal.StructureToPtr(faceinput, faceInputPtr, false); faceModel = new ArcStruct.AFR_FSDK_FaceModel(); IntPtr faceModelPtr = Marshal.AllocHGlobal(Marshal.SizeOf(faceModel)); ArcStruct.ASVLOFFSCREEN offInput = GetOffInput(bitmap); IntPtr offInputPtr = Marshal.AllocHGlobal(Marshal.SizeOf(offInput)); Marshal.StructureToPtr(offInput, offInputPtr, false); int ret = AFRFunction32.AFR_FSDK_ExtractFRFeature(recognizeEngine, offInputPtr, faceInputPtr, faceModelPtr); if (ret == 0) { faceModel = (ArcStruct.AFR_FSDK_FaceModel)Marshal.PtrToStructure(faceModelPtr, typeof(ArcStruct.AFR_FSDK_FaceModel)); Marshal.FreeHGlobal(faceModelPtr); byte[] featureContent = new byte[faceModel.lFeatureSize]; Marshal.Copy(faceModel.pbFeature, featureContent, 0, faceModel.lFeatureSize); AFRFunction32.AFR_FSDK_UninitialEngine(recognizeEngine); return 0; } else { AFRFunction32.AFR_FSDK_UninitialEngine(recognizeEngine); Marshal.FreeHGlobal(faceModelPtr); return ret; } } private int FacePairMatching32(ArcStruct.AFR_FSDK_FaceModel faceModel1, ArcStruct.AFR_FSDK_FaceModel faceModel2, ref float pfSimilScore) { IntPtr recognizeEngine = IntPtr.Zero; IntPtr pMem = Marshal.AllocHGlobal(Size); int retCode = AFRFunction32.AFR_FSDK_InitialEngine(appId32, sdkFRKey32, pMem, Size, ref recognizeEngine); if (retCode != 0) { return retCode; } IntPtr firstPtr = Marshal.AllocHGlobal(Marshal.SizeOf(faceModel1)); Marshal.StructureToPtr(faceModel1, firstPtr, false); IntPtr secondPtr = Marshal.AllocHGlobal(Marshal.SizeOf(faceModel2)); Marshal.StructureToPtr(faceModel2, secondPtr, false); int ret = AFRFunction32.AFR_FSDK_FacePairMatching(recognizeEngine, firstPtr, secondPtr, ref pfSimilScore); AFRFunction32.AFR_FSDK_UninitialEngine(recognizeEngine); Marshal.FreeHGlobal(firstPtr); Marshal.FreeHGlobal(secondPtr); Marshal.FreeHGlobal(pMem); return ret; } private ArcStruct.ASVLOFFSCREEN GetOffInput(Bitmap bitmap) { int width = 0; int height = 0; int pitch = 0; byte[] imageData = readBmp(bitmap, ref width, ref height, ref pitch); IntPtr imageDataPtr = Marshal.AllocHGlobal(imageData.Length); Marshal.Copy(imageData, 0, imageDataPtr, imageData.Length); ArcStruct.ASVLOFFSCREEN offInput = new ArcStruct.ASVLOFFSCREEN(); offInput.u32PixelArrayFormat = 513; offInput.ppu8Plane = new IntPtr[4]; offInput.ppu8Plane[0] = imageDataPtr; offInput.i32Width = width; offInput.i32Height = height; offInput.pi32Pitch = new int[4]; offInput.pi32Pitch[0] = pitch; Marshal.FreeHGlobal(imageDataPtr); return offInput; } private byte[] readBmp(Bitmap image, ref int width, ref int height, ref int pitch) { //将Bitmap锁定到系统内存中,获得BitmapData BitmapData data = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); //位图中第一个像素数据的地址。它也可以看成是位图中的第一个扫描行 IntPtr ptr = data.Scan0; //定义数组长度 int soureBitArrayLength = data.Height * Math.Abs(data.Stride); byte[] sourceBitArray = new byte[soureBitArrayLength]; //将bitmap中的内容拷贝到ptr_bgr数组中 Marshal.Copy(ptr, sourceBitArray, 0, soureBitArrayLength); width = data.Width; height = data.Height; pitch = Math.Abs(data.Stride); int line = width * 3; int bgr_len = line * height; byte[] destBitArray = new byte[bgr_len]; for (int i = 0; i < height; ++i) { Array.Copy(sourceBitArray, i * pitch, destBitArray, i * line, line); } pitch = line; image.UnlockBits(data); return destBitArray; } }
看到了熟悉的C#的变量,只需要调用方法GetImageFaceCount和FaceMatching就可以实现获取人脸数目和人脸比对,具体实现逻辑骚年们可以看看类中的具体逻辑,接下来把Struct数据类型类也发出来给大家参考
ARCStruct.cs类
public class ArcStruct { //定义人脸检查结果中人脸的角度 public enum AFD_FSDK_OrientCode { AFD_FSDK_FOC_0 = 1, AFD_FSDK_FOC_90 = 2, AFD_FSDK_FOC_270 = 3, AFD_FSDK_FOC_180 = 4, AFD_FSDK_FOC_30 = 5, AFD_FSDK_FOC_60 = 6, AFD_FSDK_FOC_120 = 7, AFD_FSDK_FOC_150 = 8, AFD_FSDK_FOC_210 = 9, AFD_FSDK_FOC_240 = 10, AFD_FSDK_FOC_300 = 11, AFD_FSDK_FOC_330 = 12 } public enum AFD_FSDK_OrientPriority { AFD_FSDK_OPF_0_ONLY = 1, AFD_FSDK_OPF_90_ONLY = 2, AFD_FSDK_OPF_270_ONLY = 3, AFD_FSDK_OPF_180_ONLY = 4, AFD_FSDK_OPF_0_HIGHER_EXT = 5 } public struct AFD_FSDK_FACERES { public int nFace; public IntPtr rcFace; public IntPtr lfaceOrient; } public struct MRECT { public int left; public int top; public int right; public int bottom; } //定义FD的版本号 public struct AFD_FSDK_Version { public int lCodebase; public int lMajor; public int lMinor; public int lBuild; public IntPtr Version; public IntPtr BuildDate; public IntPtr CopyRight; } public struct ASVLOFFSCREEN { public int u32PixelArrayFormat; public int i32Width; public int i32Height; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = System.Runtime.InteropServices.UnmanagedType.SysUInt)] public System.IntPtr[] ppu8Plane; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = System.Runtime.InteropServices.UnmanagedType.I4)] public int[] pi32Pitch; } public struct AFR_FSDK_FaceInput { public MRECT rcFace; public int lOrient; } public struct AFR_FSDK_FaceModel { public IntPtr pbFeature; public int lFeatureSize; } }
好了,这样封装就大功告成,咋们可以免费做自己的人脸识别Demo,小西瓜也做了一个DEMO出来供大家参考,下面就是DEMO使用的具体截图
好了,立马给小伙伴们提供DEMO的下载地址:
百度网盘:https://pan.baidu.com/s/1YVjxnEXNU2gl8gWKLvbf1g
网盘密码:tfzq (博客不允许复制黏贴,骚年们自己敲敲哈)
助手解压密码:blog.xgcos.com(博客不允许复制黏贴,骚年们自己敲敲哈)
喜欢这篇文章的骚年留言或者打赏支持一下噢,你们的支持是小西瓜的动力,辉亭!
欢迎分享,(联系QQ/微信:337407980)