【C#】ArcFace2 視頻人臉比對教程

請允許我大言不慚,叫做教程,特希望各位能指正。哦,我用的是vs2017。使用虹軟技術

一、準備工作
1.創建項目
watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwMTEwMzU2,size_16,color_FFFFFF,t_70
2.添加EMGU.CV包

3.複製虹軟的dll到項目

,並設屬性“複製到輸出目錄”爲“如果較新則複製

準備工作到此結束,按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張人臉)並解壓到運行目錄
ArcFaceData.zip (463.7 KB, 下載次數: 0)

四、按F5運行
點擊視頻增加當前人臉的特徵數據,基本上800毫秒可以從20萬人臉中找到你。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章