C#调用虹软人脸识别SDK研究


上一篇文章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#的变量,只需要调用方法GetImageFaceCountFaceMatching就可以实现获取人脸数目和人脸比对,具体实现逻辑骚年们可以看看类中的具体逻辑,接下来把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使用的具体截图

C#调用虹软人脸识别SDK研究


好了,立马给小伙伴们提供DEMO的下载地址:

百度网盘:https://pan.baidu.com/s/1YVjxnEXNU2gl8gWKLvbf1g

网盘密码:tfzq (博客不允许复制黏贴,骚年们自己敲敲哈)

助手解压密码:blog.xgcos.com(博客不允许复制黏贴,骚年们自己敲敲哈)

喜欢这篇文章的骚年留言或者打赏支持一下噢,你们的支持是小西瓜的动力,辉亭!




欢迎分享,(联系QQ/微信:337407980)

打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

评论