上一篇文章
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类
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;
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;
Marshal.FreeHGlobal(pem1);
Marshal.FreeHGlobal(pem2);
return -1;
private Bitmap BytesToBitmap(byte[] Bytes)
MemoryStream stream = null;
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;
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;
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;
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 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使用的具体截图