1. 程式人生 > >C# 基於Arcface SDK實現人臉識別和註冊

C# 基於Arcface SDK實現人臉識別和註冊

整個專案使用虹軟技術完成開發

一,準備工作

1.Afoge視訊引數類

using AForge.Video.DirectShow;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FaceRecognization.Common
{
    public class CameraPara
    {
        /// <summary>
        /// 是否有攝像頭
        /// </summary>
        public bool HasVideoDevice { get; set; }
        /// <summary>
        /// 視訊源
        /// </summary>
        public VideoCaptureDevice VideoSource { get; set; }
        /// <summary>
        /// 視訊圖片的寬度
        /// </summary>
        public int FrameWidth { get; set; }
        /// <summary>
        /// 視訊圖片的高度
        /// </summary>
        public int FrameHeight { get; set; }
        /// <summary>
        /// 視訊圖片的位元組數
        /// </summary>
        public int ByteCount { get; set; }
        public CameraPara()
        {
            var videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);

            if (videoDevices.Count == 0)//沒有檢測到攝像頭
            {
                HasVideoDevice = false;
                return;
            }

            VideoSource = new VideoCaptureDevice(videoDevices[0].MonikerString);//連線第一個攝像頭
            var videoResolution = VideoSource.VideoCapabilities.First(ii => ii.FrameSize.Width == VideoSource.VideoCapabilities.Max(jj => jj.FrameSize.Width)); //獲取攝像頭最高的解析度

            FrameWidth = videoResolution.FrameSize.Width;
            FrameHeight = videoResolution.FrameSize.Height;
            ByteCount = videoResolution.BitCount / 8;
            VideoSource.VideoResolution = videoResolution;
            HasVideoDevice = true;
        }

    }
}

2.人臉識別相關的結構、類 和列舉

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace FaceRecognization.Face
{
    /// <summary>
    /// 人臉庫
    /// </summary>
    public class FaceLib
    {
        public List<Item> Items { get; set; } = new List<Item>();
        public class Item
        {
            /// <summary>
            /// 用於排序
            /// </summary>
            public long OrderId { get; set; }
            /// <summary>
            /// 檔名作為ID
            /// </summary>
            public string ID { get; set; }
            /// <summary>
            /// 人臉模型
            /// </summary>
            public FaceModel FaceModel { get; set; }
        }
    }
    /// <summary>
    /// 人臉識別結果
    /// </summary>
    public class FaceResult
    {
        //public int NotMatchedCount { get; set; }
        public string ID { get; set; }
        public float Score { get; set; }
        public System.Drawing.Rectangle Rectangle { get; set; }
        public int Age { get; set; }
        /// <summary>
        /// 0:男,1:女,其他:未知
        /// </summary>
        public int Gender { get; set; }
        public override string ToString()
        {

            string ret = "";
            if (!string.IsNullOrEmpty(ID))
                ret = ID + ",";
            ret += Age + "歲";
            if (Gender == 0)
                ret += ",男";
            else if (Gender == 1)
                ret += ",女";

            return ret + "," + Score;
        }
    }

    /// <summary>
    /// 人臉跟蹤、檢測、性別年齡評估和獲取人臉資訊的輸入引數
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct ImageData
    {
        public uint u32PixelArrayFormat;
        public int i32Width;
        public int i32Height;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
        public IntPtr[] ppu8Plane;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.I4)]
        public int[] pi32Pitch;
    }
    /// <summary>
    /// 人臉跟蹤的結果
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    internal struct TraceResult
    {
        [MarshalAs(UnmanagedType.I4)]
        public int nFace;
        [MarshalAs(UnmanagedType.I4)]
        public int lfaceOrient;
        public IntPtr rcFace;
    }
    /// <summary>
    /// 人臉檢測的結果
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    internal struct DetectResult
    {
        [MarshalAs(UnmanagedType.I4)]
        public int nFace;
        public IntPtr rcFace;
        public IntPtr lfaceOrient;
    }

    /// <summary>
    /// 人臉在圖片中的位置
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct FaceRect
    {
        public int left;
        public int top;
        public int right;
        public int bottom;
    }
    /// <summary>
    /// 獲取人臉特徵的輸入引數
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    internal struct FaceFeatureInput
    {
        public FaceRect rcFace;
        public int lOrient;
    }
    /// <summary>
    /// 人臉特徵
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct FaceModel
    {
        public IntPtr pbFeature;
        [MarshalAs(UnmanagedType.I4)]
        public int lFeatureSize;
    }
    /// <summary>
    /// 性別和年齡評估的輸入引數
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    internal struct EstimationInput
    {
        public IntPtr pFaceRectArray;
        public IntPtr pFaceOrientArray;
        public int lFaceNumber;
    }
    /// <summary>
    /// 性別和年齡評估的結果
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    internal struct EstimationResult
    {
        public IntPtr pResult;
        public int lFaceNumber;
    }

    /// <summary>
    /// 錯誤程式碼
    /// </summary>
    public enum ErrorCode
    {
        /// <summary>
        /// 正確
        /// </summary>
        Ok = 0,

        /// <summary>
        /// 通用錯誤型別
        /// </summary>
        BasicBase = 0x0001,

        /// <summary>
        /// 錯誤原因不明
        /// </summary>
        Unknown = BasicBase,

        /// <summary>
        /// 無效的引數
        /// </summary>
        InvalidParam = BasicBase + 1,

        /// <summary>
        /// 引擎不支援
        /// </summary>
        Unsupported = BasicBase + 2,

        /// <summary>
        /// 記憶體不足
        /// </summary>
        NoMemory = BasicBase + 3,

        /// <summary>
        /// 狀態錯誤
        /// </summary>
        BadState = BasicBase + 4,

        /// <summary>
        /// 使用者取消相關操作
        /// </summary>
        UserCancel = BasicBase + 5,

        /// <summary>
        /// 操作時間過期
        /// </summary>
        Expired = BasicBase + 6,

        /// <summary>
        /// 使用者暫停操作
        /// </summary>
        UserPause = BasicBase + 7,

        /// <summary>
        /// 緩衝上溢
        /// </summary>
        BufferOverflow = BasicBase + 8,

        /// <summary>
        /// 緩衝下溢
        /// </summary>
        BufferUnderflow = BasicBase + 9,

        /// <summary>
        /// 存貯空間不足
        /// </summary>
        NoDiskspace = BasicBase + 10,

        /// <summary>
        /// 元件不存在
        /// </summary>
        ComponentNotExist = BasicBase + 11,

        /// <summary>
        /// 全域性資料不存在
        /// </summary>
        GlobalDataNotExist = BasicBase + 12,

        /// <summary>
        /// Free SDK通用錯誤型別
        /// </summary>
        SdkBase = 0x7000,

        /// <summary>
        /// 無效的App Id
        /// </summary>
        InvalidAppId = SdkBase + 1,

        /// <summary>
        /// 無效的SDK key
        /// </summary>
        InvalidSdkId = SdkBase + 2,

        /// <summary>
        /// AppId和SDKKey不匹配
        /// </summary>
        InvalidIdPair = SdkBase + 3,

        /// <summary>
        /// SDKKey 和使用的SDK 不匹配
        /// </summary>
        MismatchIdAndSdk = SdkBase + 4,

        /// <summary>
        /// 系統版本不被當前SDK所支援
        /// </summary>
        SystemVersionUnsupported = SdkBase + 5,

        /// <summary>
        /// SDK有效期過期,需要重新下載更新
        /// </summary>
        LicenceExpired = SdkBase + 6,

        /// <summary>
        /// Face Recognition錯誤型別
        /// </summary>
        FaceRecognitionBase = 0x12000,

        /// <summary>
        /// 無效的輸入記憶體
        /// </summary>
        InvalidMemoryInfo = FaceRecognitionBase + 1,

        /// <summary>
        /// 無效的輸入影象引數
        /// </summary>
        InvalidImageInfo = FaceRecognitionBase + 2,

        /// <summary>
        /// 無效的臉部資訊
        /// </summary>
        InvalidFaceInfo = FaceRecognitionBase + 3,

        /// <summary>
        /// 當前裝置無GPU可用
        /// </summary>
        NoGpuAvailable = FaceRecognitionBase + 4,

        /// <summary>
        /// 待比較的兩個人臉特徵的版本不一致
        /// </summary>
        MismatchedFeatureLevel = FaceRecognitionBase + 5
    }
    /// <summary>
    /// 臉部角度的檢測範圍
    /// </summary>
    public enum OrientPriority
    {
        /// <summary>
        /// 檢測 0 度(±45 度)方向
        /// </summary>
        Only0 = 0x1,

        /// <summary>
        /// 檢測 90 度(±45 度)方向
        /// </summary>
        Only90 = 0x2,

        /// <summary>
        /// 檢測 270 度(±45 度)方向
        /// </summary>
        Only270 = 0x3,

        /// <summary>
        /// 檢測 180 度(±45 度)方向
        /// </summary>
        Only180 = 0x4,

        /// <summary>
        /// 檢測 0, 90, 180, 270 四個方向,0 度更優先
        /// </summary>
        Ext0 = 0x5
    }

}

3.虹軟SDK的dll封裝

注意要把相應的dll複製的debug\Libs資料夾或release\Libs資料夾

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace FaceRecognization.Face
{
    internal class Detect
    {
        private const string DllPath = "Libs/libarcsoft_fsdk_face_detection.dll";
        /// <summary>
        /// 
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="sdkKey"></param>
        /// <param name="memory"></param>
        /// <param name="memroySize"></param>
        /// <param name="engine"></param>
        /// <param name="orientPriority"></param>
        /// <param name="scale">最小人臉尺寸有效值範圍[2,50] 推薦值 16。該尺寸是人臉相對於所在圖片的長邊的佔比。例如,如果使用者想檢測到的最小人臉尺寸是圖片長度的 1/8,那麼這個 nScale 就應該設定為8</param>
        /// <param name="maxFaceNumber">使用者期望引擎最多能檢測出的人臉數有效值範圍[1,100]</param>
        /// <returns></returns>
        [DllImport(DllPath, EntryPoint = "AFD_FSDK_InitialFaceEngine", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        public static extern int Init(string appId, string sdkKey, byte[] memory, int memroySize, out IntPtr engine, int orientPriority, int scale, int maxFaceNumber);
        [DllImport(DllPath, EntryPoint = "AFD_FSDK_StillImageFaceDetection", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        public static extern int Detection(IntPtr engine, ref ImageData imgData, out IntPtr pDetectResult);
        [DllImport(DllPath, EntryPoint = "AFD_FSDK_UninitialFaceEngine", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        public static extern int Close(IntPtr engine);
    }
    internal class Trace
    {
        private const string DllPath = "Libs/libarcsoft_fsdk_face_tracking.dll";
        /// <summary>
        /// 
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="sdkKey"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferSize"></param>
        /// <param name="engine"></param>
        /// <param name="orientPriority"></param>
        /// <param name="scale">最小人臉尺寸有效值範圍[2,16] 推薦值 16。該尺寸是人臉相對於所在圖片的長邊的佔比。例如,如果使用者想檢測到的最小人臉尺寸是圖片長度的 1/8,那麼這個 nScale 就應該設定為8</param>
        /// <param name="faceNumber">使用者期望引擎最多能檢測出的人臉數有效值範圍[1,20]</param>
        /// <returns></returns>        
        [DllImport(DllPath, EntryPoint = "AFT_FSDK_InitialFaceEngine", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        public static extern int Init(string appId, string sdkKey, byte[] buffer, int bufferSize, out IntPtr engine, int orientPriority, int scale, int faceNumber);
        [DllImport(DllPath, EntryPoint = "AFT_FSDK_FaceFeatureDetect", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        public static extern int Detection(IntPtr engine, ref ImageData imgData, out IntPtr pTraceResult);
        [DllImport(DllPath, EntryPoint = "AFT_FSDK_UninitialFaceEngine", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        public static extern int Close(IntPtr engine);
    }

    internal class Match
    {
        private const string DllPath = "Libs/libarcsoft_fsdk_face_recognition.dll";
        [DllImport(DllPath, EntryPoint = "AFR_FSDK_InitialEngine", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        public static extern int Init(string appId, string sdkKey, byte[] buffer, int bufferSize, out IntPtr engine);
        [DllImport(DllPath, EntryPoint = "AFR_FSDK_ExtractFRFeature", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        public static extern int ExtractFeature(IntPtr engine, ref ImageData imageData, ref FaceFeatureInput faceFeatureInput, out FaceModel pFaceModels);
        [DllImport(DllPath, EntryPoint = "AFR_FSDK_FacePairMatching", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        public static extern int FacePairMatch(IntPtr engine, ref FaceModel faceModel1, ref FaceModel faceModel2, out float score);
        [DllImport(DllPath, EntryPoint = "AFR_FSDK_UninitialEngine", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        public static extern int Close(IntPtr engine);
    }
    internal class Age
    {
        private const string DllPosition = "libs/libarcsoft_fsdk_age_estimation.dll";

        [DllImport(DllPosition, EntryPoint = "ASAE_FSDK_InitAgeEngine", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        public static extern int Init(string appId, string sdkKey, byte[] buffer, int bufferSize, out IntPtr engine);
        [DllImport(DllPosition, EntryPoint = "ASAE_FSDK_AgeEstimation_StaticImage", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        public static extern int EstimationStatic(IntPtr engine, ref ImageData imgData, ref EstimationInput estimationInputInput, out EstimationResult pAgeResult);
        [DllImport(DllPosition, EntryPoint = "ASAE_FSDK_AgeEstimation_Preview", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        public static extern int EstimationPreview(IntPtr engine, ref ImageData imgData, ref EstimationInput estimationInputInput, out EstimationResult pAgeResult);
        [DllImport(DllPosition, EntryPoint = "ASAE_FSDK_UninitAgeEngine", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        public static extern int Close(IntPtr engine);
    }
    internal class Gender
    {
        private const string DllPosition = "libs/libarcsoft_fsdk_gender_estimation.dll";

        [DllImport(DllPosition, EntryPoint = "ASGE_FSDK_InitGenderEngine", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        public static extern int Init(string appId, string sdkKey, byte[] buffer, int bufferSize, out IntPtr engine);
        [DllImport(DllPosition, EntryPoint = "ASGE_FSDK_GenderEstimation_StaticImage", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        public static extern int EstimationStatic(IntPtr engine, ref ImageData imgData, ref EstimationInput estimationInputInput, out EstimationResult pGenderResult);
        [DllImport(DllPosition, EntryPoint = "ASGE_FSDK_GenderEstimation_Preview", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        public static extern int EstimationPreview(IntPtr engine, ref ImageData imgData, ref EstimationInput estimationInputInput, out EstimationResult pGenderResesult);
        [DllImport(DllPosition, EntryPoint = "ASGE_FSDK_UninitGenderEngine", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        public static extern int Close(IntPtr engine);
    }
}

二、正式開始

1.新增一個Form,將Name改為“Main”,新增Load和FormClosing事件

2.拖一個Afoge.Controls.VideoSourcePlayer到Main,將Name改為“VideoPlayer”,新增Click事件;

3.拖一個PictureBox到Main,懶得改名字了,就叫“pictureBox1”;

4.拖一個Lable到Main,將Text改為“ID”;

5.拖一個TextBox到Main,將Name改為“TextBoxID”;

6.拖一個Button到Main,將Name改為ButtonRegister,將Text改為“註冊”,新增Click事件。

介面如下圖,從左到右,從上到下6個控制元件: 在這裡插入圖片描述 對應的程式碼為:

using AForge.Video.DirectShow;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using FaceRecognization.Common;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Drawing.Imaging;

namespace FaceRecognization
{
    public partial class Main : Form
    {
        #region Property
        /// <summary>
        /// 儲存人臉資料的資料夾
        /// </summary>
        const string FeaturePath = "d:\\FeatureData";
        /// <summary>
        /// 虹軟SDK的AppId
        /// </summary>
        const string FaceAppId = "BKgqTWQPQQbomfqvyd2VJzTbzo5C4T5w4tzgN3GL6euK";
        /// <summary>
        /// 虹軟SDK人臉跟蹤的Key
        /// </summary>
        const string FaceTraceKey = "2Yqm2EcsJyBbJjSrirPSNoyHDRKCrS53XgUDeRRxtKyR";
        /// <summary>
        /// 虹軟SDK人臉檢測的Key
        /// </summary>
        const string FaceDetectKey = "2Yqm2EcsJyBbJjSrirPSNoyQNpaSJz19noCteLQ88SoG";
        /// <summary>
        /// 虹軟SDK人臉比對的Key
        /// </summary>
        const string FaceMatchKey = "2Yqm2EcsJyBbJjSrirPSNoyu2Rd4j1ydfwxwFX9vPmtY";
        /// <summary>
        /// 虹軟SDK年齡識別的Key
        /// </summary>
        const string FaceAgeKey = "2Yqm2EcsJyBbJjSrirPSNoz9ME9R4xnKU9yecD8Axu1D";
        /// <summary>
        /// 虹軟SDK性別識別的Key
        /// </summary>
        const string FaceGenderKey = "2Yqm2EcsJyBbJjSrirPSNozGWdQedYzhEUMw5FBegKVR";
        /// <summary>
        /// 快取大小
        /// </summary>
        const int BufferSize = 40 * 1024 * 1024;
        /// <summary>
        /// 人臉跟蹤的快取
        /// </summary>
        //byte[] _FaceTraceBuffer = new byte[BufferSize];
        /// <summary>
        /// 人臉檢測的快取
        /// </summary>
        byte[] _FaceDetectBuffer = new byte[BufferSize];
        /// <summary>
        /// 人臉比對的快取
        /// </summary>
        byte[] _FaceMatchBuffer = new byte[BufferSize];
        /// <summary>
        /// 年齡識別的快取
        /// </summary>
        //byte[] _FaceAgeBuffer = new byte[BufferSize];
        /// <summary>
        /// 性別識別的快取
        /// </summary>
        //byte[] _FaceGenderBuffer = new byte[BufferSize];
        /// <summary>
        /// 人臉跟蹤的引擎
        /// </summary>
        //IntPtr _FaceTraceEnginer = IntPtr.Zero;
        /// <summary>
        /// 人臉檢測的引擎
        /// </summary>
        IntPtr _FaceDetectEnginer = IntPtr.Zero;
        /// <summary>
        /// 人臉比對的引擎
        /// </summary>
        IntPtr _FaceMatchEngine = IntPtr.Zero;
        /// <summary>
        /// 年齡識別的引擎
        /// </summary>
        //IntPtr _FaceAgeEngine = IntPtr.Zero;
        /// <summary>
        /// 性別識別的引擎
        /// </summary>
        //IntPtr _FaceGenderEngine = IntPtr.Zero;
        /// <summary>
        /// 人臉庫字典
        /// </summary>
        Face.FaceLib _FaceLib = new Face.FaceLib();
        /// <summary>
        /// 攝像頭引數
        /// </summary>
        CameraPara _CameraPara = null;
        double _RateW, _RateH;
        private readonly ReaderWriterLockSlim _CacheLock = new ReaderWriterLockSlim();
        Face.FaceResult _FaceResult = new Face.FaceResult();
        System.Threading.CancellationTokenSource _CancellationTokenSource = new System.Threading.CancellationTokenSource();
        bool _RegisterClicked = false;
        byte[] _RegisterFeatureData = null;
        #endregion
        public Main()
        {
            InitializeComponent();
        }


        private void Main_Load(object sender, EventArgs e)
        {

            if (!Directory.Exists(FeaturePath))
                Directory.CreateDirectory(FeaturePath);

            foreach (var file in Directory.GetFiles(FeaturePath))
            {
                var info = new FileInfo(file);
                var data = File.ReadAllBytes(file);
                var faceModel = new Face.FaceModel
                {
                    lFeatureSize = data.Length,
                    pbFeature = Marshal.AllocHGlobal(data.Length)
                };

                Marshal.Copy(data, 0, faceModel.pbFeature, data.Length);
                _FaceLib.Items.Add(new Face.FaceLib.Item() { OrderId = 0, ID = info.Name.Replace(info.Extension, ""), FaceModel = faceModel });
            }
            _CameraPara = new Common.CameraPara();
            if (!_CameraPara.HasVideoDevice)
            {
                MessageBox.Show("沒有檢測到攝像頭");
                this.Close();
                return;
            }

            this.VideoPlayer.VideoSource = _CameraPara.VideoSource;
            this.VideoPlayer.Start();

            _RateH = 1.0 * this.VideoPlayer.Height / this._CameraPara.FrameHeight;
            _RateW = 1.0 * this.VideoPlayer.Width / this._CameraPara.FrameWidth;

            //var initResult = (Face.ErrorCode)Face.Trace.Init(FaceAppId, FaceTraceKey, _FaceTraceBuffer, BufferSize, out _FaceTraceEnginer, (int)Face.OrientPriority.Only0, 16, 1);
            //if (initResult != Face.ErrorCode.Ok)
            //{
            //    MessageBox.Show("初始化人臉跟蹤引擎失敗,錯誤程式碼為:" + initResult);
            //    this.Close();
            //    return;
            //}

            var initResult = (Face.ErrorCode)Face.Detect.Init(FaceAppId, FaceDetectKey, _FaceDetectBuffer, BufferSize, out _FaceDetectEnginer, (int)Face.OrientPriority.Only0, 16, 1);
            if (initResult != Face.ErrorCode.Ok)
            {
                MessageBox.Show("初始化人臉檢測引擎失敗,錯誤程式碼為:" + initResult);
                this.Close();
                return;
            }

            initResult = (Face.ErrorCode)Face.Match.Init(FaceAppId, FaceMatchKey, _FaceMatchBuffer, BufferSize, out _FaceMatchEngine);
            if (initResult != Face.ErrorCode.Ok)
            {
                MessageBox.Show("初始化人臉比對引擎失敗,錯誤程式碼為:" + initResult);
                this.Close();
                return;
            }

            //initResult = (Face.ErrorCode)Face.Age.Init(FaceAppId, FaceAgeKey, _FaceAgeBuffer, BufferSize, out _FaceAgeEngine);
            //if (initResult != Face.ErrorCode.Ok)
            //{
            //    MessageBox.Show("初始化年齡識別引擎失敗,錯誤程式碼為:" + initResult);
            //    this.Close();
            //    return;
            //}

            //initResult = (Face.ErrorCode)Face.Gender.Init(FaceAppId, FaceGenderKey, _FaceGenderBuffer, BufferSize, out _FaceGenderEngine);
            //if (initResult != Face.ErrorCode.Ok)
            //{
            //    MessageBox.Show("初始化性別識別引擎失敗,錯誤程式碼為:" + initResult);
            //    this.Close();
            //    return;
            //}

            Task.Factory.StartNew(() =>
            {
                Task.Delay(1000).Wait();
                while (!_CancellationTokenSource.IsCancellationRequested)
                {
                    #region 200毫秒左右
                    MatchFrame();
                    #endregion
                }
            }, _CancellationTokenSource.Token);

        }

        private void Main_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (_CameraPara.HasVideoDevice)
            {
                _CancellationTokenSource.Cancel();
                System.Threading.Thread.Sleep(500);
                this.VideoPlayer.Stop();

                if (_FaceMatchEngine != IntPtr.Zero)
                    Face.Match.Close(_FaceMatchEngine);
                //if (_FaceTraceEnginer != IntPtr.Zero)
                //    Face.Trace.Close(_FaceTraceEnginer);
                if (_FaceDetectEnginer != IntPtr.Zero)
                    Face.Detect.Close(_FaceDetectEnginer);

                //if (_FaceAgeEngine != IntPtr.Zero)
                //    Face.Age.Close(_FaceAgeEngine);
                //if (_FaceGenderEngine != IntPtr.Zero)
                //    Face.Gender.Close(_FaceGenderEngine);

            }

        }



        private void MatchFrame()
        {
            #region 獲取圖片 1毫秒
            var bitmap = this.VideoPlayer.GetCurrentVideoFrame();
            #endregion


            Stopwatch sw = new Stopwatch();
            sw.Start();
            #region 圖片轉換 0.7-2微妙
            var bmpData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            var imageData = new Face.ImageData
            {
                u32PixelArrayFormat = 513,//Rgb24,
                i32Width = bitmap.Width,
                i32Height = bitmap.Height,
                pi32Pitch = new int[4],
                ppu8Plane = new IntPtr[4]
            };
            imageData.pi32Pitch[0] = bmpData.Stride;
            imageData.ppu8Plane[0] = bmpData.Scan0;

            sw.Stop();
            _FaceResult.Score = sw.ElapsedTicks;


            #endregion
            try
            {
                #region 人臉檢測 5-8毫秒
                var ret = (Face.ErrorCode)Face.Detect.Detection(_FaceDetectEnginer, ref imageData, out var pDetectResult);
                if (ret != Face.ErrorCode.Ok)
                    return;
                var detectResult = Marshal.PtrToStructure<Face.DetectResult>(pDetectResult);
                if (detectResult.nFace == 0)
                    return;
                var faceRect = Marshal.PtrToStructure<Face.FaceRect>(detectResult.rcFace);
                _FaceResult.Rectangle = new Rectangle((int)(faceRect.left * _RateW), (int)(faceRect.top * _RateH), (int)((faceRect.right - faceRect.left) * _RateW), (int)((faceRect.bottom - faceRect.top) * _RateH));
                var faceOrient = Marshal.PtrToStructure<int>(detectResult.lfaceOrient);
                #endregion


                #region 性別識別基本準確 年齡識別誤差太大,沒什麼應用場景
                //Face.ExtraFaceInput faceInput = new Face.ExtraFaceInput()
                //{
                //    lFaceNumber = facesDetect.nFace,
                //    pFaceRectArray = Marshal.AllocHGlobal(Marshal.SizeOf(faceRect)),
                //    pFaceOrientArray = Marshal.AllocHGlobal(Marshal.SizeOf(faceOrient))
                //};
                //Marshal.StructureToPtr(faceRect, faceInput.pFaceRectArray, false);
                //Marshal.StructureToPtr(faceOrient, faceInput.pFaceOrientArray, false);

                //var ageResult = Face.Age.ASAE_FSDK_AgeEstimation_Preview(_FaceAgeEngine, ref imageData, ref faceInput, out var pAgeRes);
                //var ages = pAgeRes.pResult.ToStructArray<int>(pAgeRes.lFaceNumber);
                //var genderResult = Face.Gender.ASGE_FSDK_GenderEstimation_Preview(_FaceGenderEngine, ref imageData, ref faceInput, out var pGenderRes);
                //var genders = pGenderRes.pResult.ToStructArray<int>(pGenderRes.lFaceNumber);
                //_FaceResult.Age = ages[0];
                //_FaceResult.Gender = genders[0];

                //Marshal.FreeHGlobal(faceInput.pFaceOrientArray);
                //Marshal.FreeHGlobal(faceInput.pFaceRectArray);
                #endregion

                #region 獲取人臉特徵 160-180毫秒
                var faceFeatureInput = new Face.FaceFeatureInput
                {
                    rcFace = faceRect,
                    lOrient = faceOrient
                };

                ret = (Face.ErrorCode)Face.Match.ExtractFeature(_FaceMatchEngine, ref imageData, ref faceFeatureInput, out var faceModel);
                #endregion

                if (ret == Face.ErrorCode.Ok)
                {

                    if (_RegisterClicked)
                    {
                        _RegisterFeatureData = new byte[faceModel.lFeatureSize];
                        Marshal.Copy(faceModel.pbFeature, _RegisterFeatureData, 0, faceModel.lFeatureSize);
                    }

                    #region 人臉識別(100張人臉) 17-20毫秒
                    foreach (var item in _FaceLib.Items.OrderByDescending(ii => ii.OrderId))
                    {
                        var fm = item.FaceModel;
                        Face.Match.FacePairMatch(_FaceMatchEngine, ref fm, ref faceModel, out float score);
                        if (score > 0.5)
                        {
                            item.OrderId = DateTime.Now.Ticks;
                            _FaceResult.ID = item.ID;
                            break;
                        }
                    }
                    #endregion

                }

            }
            finally
            {
                bitmap.UnlockBits(bmpData);
                if (_RegisterClicked)
                {
                    this.pictureBox1.Invoke(new Action(() =>
                    {
                        this.pictureBox1.Image = bitmap;
                    }));
                    _RegisterClicked = false;
                }
                else
                {
                    bitmap.Dispose();
                }
            }

        }

        private void VideoPlayer_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.DrawRectangle(Pens.White, _FaceResult.Rectangle);
            e.Graphics.DrawString(_FaceResult.ID , this.Font, Brushes.White, _FaceResult.Rectangle.Left, _FaceResult.Rectangle.Top - 20);
        }

        private void VideoPlayer_Click(object sender, EventArgs e)
        {
            _RegisterFeatureData = null;
            _RegisterClicked = true;
            this.TextBoxID.Text = _FaceResult.ID;
        }
        private void ButtonRegister_Click(object sender, EventArgs e)
        {
            if (_RegisterFeatureData == null)
            {
                MessageBox.Show("沒有人臉資料,請面對攝像頭並點選視訊");
                return;
            }
            if (string.IsNullOrEmpty(this.TextBoxID.Text))
            {
                MessageBox.Show("請輸入Id");
                this.TextBoxID.Focus();
                return;
            }
            var fileName = FeaturePath + "\\" + this.TextBoxID.Text + ".dat";
            if (System.IO.File.Exists(fileName))
            {
                if (MessageBox.Show($"您要替換[{this.TextBoxID.Text}]的人臉資料嗎?", "諮詢", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) == DialogResult.No)
                    return;
            }
            System.IO.File.WriteAllBytes(fileName, _RegisterFeatureData);
            var faceModel = new Face.FaceModel
            {
                lFeatureSize = _RegisterFeatureData.Length,
                pbFeature = Marshal.AllocHGlobal(_RegisterFeatureData.Length)
            };

            Marshal.Copy(_RegisterFeatureData, 0, faceModel.pbFeature, _RegisterFeatureData.Length);
            _FaceLib.Items.Add(new Face.FaceLib.Item() { OrderId = DateTime.Now.Ticks, ID = this.TextBoxID.Text, FaceModel = faceModel });

        }




    }
}

三、執行

按F5執行,能給最大的人臉畫框,如比對通過顯示框上面顯示ID

1.點選 視訊;

2.輸入 ID