上一篇文章 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
{
///
/// 初始化脸部检测引擎
///
/// 用户申请SDK时获取的AppId
/// 用户申请 SDK 时获取的 SDK Key
/// 分配给引擎使用的内存地址
/// 分配给引擎使用的内存大小
/// 引擎 handle
/// 期望的脸部检测角度的优先级
/// 用于数值表示的最小人脸尺寸 有效值范围[2,50] 推荐值 16
/// 用户期望引擎最多能检测出的人脸数 有效值范围[1,100]
///
[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);
///
/// 根据输入的图像检测出人脸位置,一般用于静态图像检测
///
/// 引擎 handle
/// 带检测图像信息
/// 人脸检测结果
///
[DllImport("libarcsoft_fsdk_face_detection.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int AFD_FSDK_StillImageFaceDetection(IntPtr pEngine, IntPtr pImgData, ref IntPtr pFaceRes);
///
/// 销毁引擎,释放相应资源
///
/// 引擎 handle
///
[DllImport("libarcsoft_fsdk_face_detection.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int AFD_FSDK_UninitialFaceEngine(IntPtr pEngine);
}
AFRFUnction32.cs类
public class AFRFunction32
{
///
/// 初始化引擎
///
/// 用户申请SDK时获取的AppId
/// 用户申请 SDK 时获取的 SDK Key
/// 分配给引擎使用的内存地址
/// 分配给引擎使用的内存大小
/// 引擎 handle
///
[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);
///
/// 获取脸部特征
///
/// 引擎 handle
/// 输入的图像数据
/// 已检测到到的脸部信息
/// 提取的脸部特征信息
///
[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);
///
/// 脸部特征比较
///
/// 引擎 handle
/// 已有脸部特征信息
/// 被比较的脸部特征信息
/// 相似程度数值
///
[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);
///
/// 销毁引擎,释放相应资源
///
/// 引擎 handle
///
[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()
{
}
///
/// 获取图片中的人脸数目
///
/// 图片流数据
/// 返回人脸数
/// 0为调用成功,其他返回错误码
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;
}
///
/// 比对图片
///
/// 图片1流数据
/// 图片2流数据
/// 返回相似度
/// 0为调用成功,-1为检测图片人脸失败,其他返回错误码
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(博客不允许复制黏贴,骚年们自己敲敲哈)
喜欢这篇文章的骚年留言或者打赏支持一下噢,你们的支持是小西瓜的动力,辉亭!
评论区