【C#】ArcFace2 视频人脸比对教程

请允许我大言不惭,叫做教程,特希望各位能指正。哦,我用的是vs2017。
一、准备工作
1.创建项目


image.png

2.添加EMGU.CV包


image.png

,并设属性“复制到输出目录”为“如果较新则复制”


image.png

3.添加程序集System.ServiceModel的引用(Emgu视频捕捉需要)


image.png

准备工作到此结束,按F7切换到代码,然后进入第二步。

using Emgu.CV;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ArcFace2Demo
{
    public partial class Form1 : Form
    {
        #region ArcFaceConst
        const uint ASF_DETECT_MODE_VIDEO = 0x00000000;  //Video模式,一般用于多帧连续检测
        const uint ASF_DETECT_MODE_IMAGE = 0xFFFFFFFF;  //Image模式,一般用于静态图的单次检测

        const uint ASF_NONE = 0x00000000;
        const uint ASF_FACE_DETECT = 0x00000001; //此处detect可以是tracking或者detection两个引擎之一,具体的选择由detect mode 确定
        const uint ASF_FACERECOGNITION = 0x00000004;
        const uint ASF_AGE = 0x00000008;
        const uint ASF_GENDER = 0x00000010;
        const uint ASF_FACE3DANGLE = 0x00000020;

        /// 
        /// 结构ASF_FaceRect的长度
        /// 32位程序是16,64位程序需要改为32
        /// 
        const int SizeOfASF_FaceRect = 16;

        #endregion


        #region ArceDataStructure
        /// 
        /// 人脸在图片中的位置
        /// 
        [StructLayout(LayoutKind.Sequential)]
        internal struct ASF_FaceRect
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
            public Rectangle GetRectangle()
            {
                return new Rectangle(Left, Top, Right - Left, Bottom - Top);
            }
        }
        /// 
        /// 多人脸信息
        /// 
        [StructLayout(LayoutKind.Sequential)]
        internal struct ASF_MultiFaceInfo
        {

            public IntPtr PFaceRect;
            public IntPtr PFaceOrient;
            [MarshalAs(UnmanagedType.I4)]
            public int FaceNum;
        }


        /// 
        /// 单人脸信息
        /// 
        [StructLayout(LayoutKind.Sequential)]
        internal struct ASF_SingleFaceInfo
        {
            public ASF_FaceRect FaceRect;
            public int FaceOrient;

        }



        /// 
        /// 人脸特征
        /// 
        [StructLayout(LayoutKind.Sequential)]
        internal struct ASF_FaceFeature
        {
            public IntPtr PFeature;
            [MarshalAs(UnmanagedType.I4)]
            public int FeatureSize;
        }


        #endregion

        #region ArcWrapper

        /// 
        /// 激活SDK
        /// 
        /// 
        /// 
        /// 0:激活成功,0x16002表示已经激活
        [DllImport("libarcsoft_face_engine.dll", EntryPoint = "ASFActivation", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        private static extern int ASFActivation(string appId, string sdkKey);

        /// 
        /// 初始化引擎
        /// 
        /// long会返回scale错误0x16004
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        [DllImport("libarcsoft_face_engine.dll", EntryPoint = "ASFInitEngine", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        private static extern int ASFInitEngine(uint detectMode, int orientPriority, int scale, int maxFaceNumber, uint combinedMask, out IntPtr pEngine);
        /// 
        /// 人脸检测
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        [DllImport("libarcsoft_face_engine.dll", EntryPoint = "ASFDetectFaces", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        private static extern int ASFDetectFaces(IntPtr pEngine, int width, int height, int format, IntPtr pImageData, out ASF_MultiFaceInfo faceInfo);

        /// 
        /// 单人脸特征提取
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        [DllImport("libarcsoft_face_engine.dll", EntryPoint = "ASFFaceFeatureExtract", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        private static extern int ASFFaceFeatureExtract(IntPtr pEngine, int width, int height, int format, IntPtr pImageData, ref ASF_SingleFaceInfo faceInfo, out ASF_FaceFeature faceFeature);
        /// 
        /// 脸特征比对
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        [DllImport("libarcsoft_face_engine.dll", EntryPoint = "ASFFaceFeatureCompare", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        private static extern int ASFFaceFeatureCompare(IntPtr pEngine, ref ASF_FaceFeature faceFeature1, ref ASF_FaceFeature faceFeature2, out float result);
        /// 
        /// 销毁引擎
        /// 
        /// 
        /// 
        [DllImport("libarcsoft_face_engine.dll", EntryPoint = "ASFUninitEngine", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        private static extern int ASFUninitEngine(IntPtr engine);
        #endregion

        /// 
        /// 特征库
        /// 
        IntPtr _PFeatureLib;
        /// 
        /// 特征库人脸数量
        /// 
        int _FeatureLibFaceCount = 0;
        /// 
        /// 特征库人脸ID列表
        /// 
        List _FeatureLibIDList = new List();

        /// 
        /// 人脸特征结构
        /// 
        ASF_FaceFeature _FaceFeature = new ASF_FaceFeature { FeatureSize = 1032 };

        /// 
        /// 人脸识别的结果
        /// 
        class FaceResult
        {
            /// 
            /// 人脸框矩形
            /// 
            public Rectangle Rectangle { get; set; }
            /// 
            /// 人脸ID
            /// 
            public string ID { get; set; }
            /// 
            /// 比对结果
            /// 
            public float Score { get; set; }

            public override string ToString()
            {
                return [        DISCUZ_CODE_0        ]quot;ID:{ID}\r\n结果:{Score}";
            }
        }
        /// 
        /// 多人脸识别结果集
        /// 
        ConcurrentDictionary _FaceResults = new ConcurrentDictionary();
        /// 
        /// 检测到的人脸数量
        /// 
        int _DetectedFaceCount = 0;

        /// 
        /// 视频捕获
        /// 
        VideoCapture _VideoCapture;
        Mat _Frame = new Mat();

        /// 
        /// 虹软人脸引擎
        /// 
        IntPtr _PEngine = IntPtr.Zero;

        /// 
        /// 比对一次总耗时
        /// 
        long _TotalElapsedMilliseconds = 0;
        /// 
        /// 识别任务
        /// 
        Task _TaskMatch;
        /// 
        /// 向识别任务发送取消指令的东东
        /// 
        CancellationTokenSource _CTS = new CancellationTokenSource();

        /// 
        /// 图像数据
        /// 
        IntPtr _PImageData;
        /// 
        /// 宽、高、图像数据长度
        /// 
        int _ImageWidth, _ImageHeight, _ImageSize;
        /// 
        /// 是否要保存当前人脸特征
        /// 
        bool _SaveFlag = false;

        PictureBox _PictureBox;

        public Form1()
        {
            InitializeComponent();

            _PictureBox = new PictureBox();
            _PictureBox.SizeMode = PictureBoxSizeMode.StretchImage;
            _PictureBox.Dock = DockStyle.Fill;
            this.Controls.Add(_PictureBox);

            this.Load += Form1_Load;
            this.FormClosing += Form1_FormClosing;
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (_TaskMatch != null)
            {
                _CTS.Cancel();
                while (_TaskMatch.Status == TaskStatus.Running)
                    Task.Delay(1000).Wait();
            }
            _VideoCapture.Stop();

            if (_PEngine != IntPtr.Zero)
                ASFUninitEngine(_PEngine);

            if (_PFeatureLib != IntPtr.Zero)
                Marshal.FreeCoTaskMem(_PFeatureLib);

            if (_PImageData != IntPtr.Zero)
                Marshal.FreeCoTaskMem(_PImageData);
        }

        private unsafe void Form1_Load(object sender, EventArgs e)
        {
            var ret = ASFActivation("BKgqTWQPQQbomfqvyd2VJzTUqPp3JD8zjAzDcqsL1jLa", "2nkDTmnkpS53cpSY42fFS9nEUzg8x4MDGkAubSsebtm1");
            if (ret != 0 && ret != 0x16002)
            {
                MessageBox.Show("SDK激活失败:0x" + ret.ToString("x2"));
                return;
            }
            ret = ASFInitEngine(ASF_DETECT_MODE_IMAGE, 1, 32, 10, ASF_FACE_DETECT | ASF_FACERECOGNITION, out _PEngine);
            if (ret != 0)
            {
                MessageBox.Show([        DISCUZ_CODE_0        ]quot;人脸识别引擎初始化失败:" + ret.ToString("x2"));
                return;
            }
            //初始化识别结果集
            for (int i = 0; i < 10; i++)
                _FaceResults[i] = new FaceResult();
            //初始化特征库
            _PFeatureLib = Marshal.AllocCoTaskMem(1032 * 1000 + 1032 * 10000 * 20);
            var bytes = File.ReadAllBytes("Feature.dat");
            var ids = File.ReadAllLines("Id.txt");
            for (int i = 0; i < 20 * 20; i++)
            {
                Marshal.Copy(bytes, 0, IntPtr.Add(_PFeatureLib, _FeatureLibFaceCount * 1032), bytes.Length);
                _FeatureLibIDList.AddRange(ids);
                _FeatureLibFaceCount += ids.Length;
            }

            _VideoCapture = new VideoCapture();


            //_VideoCapture.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameWidth, 1024);
            //_VideoCapture.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameHeight, 768);
            _VideoCapture.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.Fps, 10);
            _VideoCapture.Start();

            _VideoCapture.ImageGrabbed += (object oo, EventArgs es) =>
            {
                _VideoCapture.Retrieve(_Frame, 1);
                using (Graphics g = Graphics.FromImage(_Frame.Bitmap))
                {
                    g.DrawString([        DISCUZ_CODE_0        ]quot;比对总耗时{_TotalElapsedMilliseconds}毫秒", this.Font, Brushes.White, 0, 0);
                    for (int i = 0; i < _DetectedFaceCount; i++)
                    {
                        if (_FaceResults.TryGetValue(i, out var faceResult))
                        {
                            g.DrawRectangle(Pens.Red, faceResult.Rectangle);
                            g.DrawString(faceResult.ToString(), this.Font, Brushes.White, faceResult.Rectangle.Location);
                        }
                    }
                }
                this._PictureBox.Image = _Frame.Bitmap;
            };

            _PictureBox.Click += (object oo, EventArgs es) =>
            {
                if (MessageBox.Show("您确定要保存人脸特征数据吗?", "确认信息", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.Yes)
                    _SaveFlag = true;
            };

            _ImageSize = _VideoCapture.Width * _VideoCapture.Height * 3;
            _PImageData = Marshal.AllocCoTaskMem(_ImageSize);
            _ImageWidth = _VideoCapture.Width;
            _ImageHeight = _VideoCapture.Height;



            _TaskMatch = Task.Run(() =>
            {
                Task.Delay(1000).Wait();

                while (!_CTS.IsCancellationRequested)
                {
                    try
                    {
                        Stopwatch sw = new Stopwatch();
                        sw.Restart();

                        Marshal.Copy(_Frame.GetData(), 0, _PImageData, _ImageSize);
                        ret = ASFDetectFaces(_PEngine, _ImageWidth, _ImageHeight, 513, _PImageData, out var faceInfo);

                        if (ret != 0 || faceInfo.FaceNum == 0)
                        {
                            _DetectedFaceCount = 0;
                            continue;
                        }

                        for (int detectedFaceIndex = 0; detectedFaceIndex < faceInfo.FaceNum; detectedFaceIndex++)
                        {

                            float score = 0;
                            string id = "";
                            ASF_SingleFaceInfo singleFaceInfo = new ASF_SingleFaceInfo
                            {
                                FaceRect = Marshal.PtrToStructure(IntPtr.Add(faceInfo.PFaceRect, SizeOfASF_FaceRect * detectedFaceIndex)),
                                FaceOrient = 1// Marshal.ReadInt32(IntPtr.Add(faceInfo.PFaceOrient, i * 4))
                            };


                            ret = ASFFaceFeatureExtract(_PEngine, _ImageWidth, _ImageHeight, 513, _PImageData, ref singleFaceInfo, out var faceFeature);
                            if (ret != 0)
                                continue;
                            _FaceResults[detectedFaceIndex].Rectangle = singleFaceInfo.FaceRect.GetRectangle();


                            if (_SaveFlag)
                            {
                                byte[] bufferSave = new byte[1032];
                                Marshal.Copy(faceFeature.PFeature, bufferSave, 0, 1032);
                                var newId = DateTime.Now.Ticks.ToString();

                                FileStream fs = new FileStream("Feature.dat", FileMode.Append);
                                fs.Write(bufferSave, 0, 1032);
                                fs.Close();

                                var streamWriter = File.AppendText("Id.txt");
                                streamWriter.Write("\r\n" + newId);
                                streamWriter.Close();

                                Marshal.Copy(bufferSave, 0, IntPtr.Add(_PFeatureLib, 1032 * _FeatureLibFaceCount), 1032);
                                _FeatureLibIDList.Add(newId);
                                _FeatureLibFaceCount++;

                                if (detectedFaceIndex == faceInfo.FaceNum - 1)
                                {
                                    MessageBox.Show("保存特征数据成功!");
                                    _SaveFlag = false;
                                }
                                continue;
                            }



                            ConcurrentBag needCompareFaceIndexs = new ConcurrentBag();

                            Parallel.For(0, _FeatureLibFaceCount, faceIndex =>
                            {

                                byte* pLib = ((byte*)_PFeatureLib) + 1032 * faceIndex + 8;
                                byte* pCurrent = ((byte*)faceFeature.PFeature) + 8;
                                int count = 0;
                                for (int j = 0; j < 1024; j++)
                                {
                                    if (*pLib++ == *pCurrent++)
                                        count++;
                                }
                                if (count > 80)
                                    needCompareFaceIndexs.Add(faceIndex);
                            });

                            foreach (var index in needCompareFaceIndexs)//650ms
                            {
                                _FaceFeature.PFeature = IntPtr.Add(_PFeatureLib, index * 1032);
                                ASFFaceFeatureCompare(_PEngine, ref faceFeature, ref _FaceFeature, out var r);

                                if (r > 0.8 && r > score)
                                {
                                    score = r;
                                    id = _FeatureLibIDList[index];
                                }
                            }

                            _FaceResults[detectedFaceIndex].Score = score;
                            _FaceResults[detectedFaceIndex].ID = id;
                        }


                        _DetectedFaceCount = faceInfo.FaceNum;

                        sw.Stop();
                        _TotalElapsedMilliseconds = sw.ElapsedMilliseconds;

                    }
                    catch (Exception ex)
                    {

                    }
                }
            }, _CTS.Token);

        }


    }
}

三、下载测试用特征数据(500张人脸)并解压到运行目录


image

ArcFaceData.zip

四、按F5运行
点击视频增加当前人脸的特征数据,基本上800毫秒可以从20万人脸中找到你。

你可能感兴趣的:(【C#】ArcFace2 视频人脸比对教程)