【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;

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

        #endregion


        #region ArceDataStructure
        /// <summary>
        /// 人脸在图片中的位置
        /// </summary>
        [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);
            }
        }
        /// <summary>
        /// 多人脸信息
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        internal struct ASF_MultiFaceInfo
        {

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


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

        }



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


        #endregion

        #region ArcWrapper

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

        /// <summary>
        /// 初始化引擎
        /// </summary>
        /// <param name="detectMode">long会返回scale错误0x16004</param>
        /// <param name="orientPriority"></param>
        /// <param name="scale"></param>
        /// <param name="maxFaceNumber"></param>
        /// <param name="combinedMask"></param>
        /// <param name="pEngine"></param>
        /// <returns></returns>
        [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);
        /// <summary>
        /// 人脸检测
        /// </summary>
        /// <param name="pEngine"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="format"></param>
        /// <param name="pImageData"></param>
        /// <param name="faceInfo"></param>
        /// <returns></returns>
        [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);

        /// <summary>
        /// 单人脸特征提取
        /// </summary>
        /// <param name="pEngine"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="format"></param>
        /// <param name="faceInfo"></param>
        /// <param name="faceFeature"></param>
        /// <returns></returns>
        [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);
        /// <summary>
        /// 脸特征比对
        /// </summary>
        /// <param name="pEngine"></param>
        /// <param name="faceFeature1"></param>
        /// <param name="faceFeature2"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        [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);
        /// <summary>
        /// 销毁引擎
        /// </summary>
        /// <param name="engine"></param>
        /// <returns></returns>
        [DllImport("libarcsoft_face_engine.dll", EntryPoint = "ASFUninitEngine", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        private static extern int ASFUninitEngine(IntPtr engine);
        #endregion

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

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

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

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

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

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

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

        /// <summary>
        /// 图像数据
        /// </summary>
        IntPtr _PImageData;
        /// <summary>
        /// 宽、高、图像数据长度
        /// </summary>
        int _ImageWidth, _ImageHeight, _ImageSize;
        /// <summary>
        /// 是否要保存当前人脸特征
        /// </summary>
        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<ASF_FaceRect>(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<int> needCompareFaceIndexs = new ConcurrentBag<int>();

                            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万人脸中找到你。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容