代码改变世界

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

2019-01-11 13:43  码仔很忙  阅读(1349)  评论(0编辑  收藏  举报

请允许我大言不惭,叫做教程,特希望各位能指正。哦,我用的是vs2017。了解更多详情可以访问虹软人工智能开放平台

一、准备工作

1.创建项目

 

 

 

2.添加EMGU.CV包

 

 

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

 

 

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

 

 

准备工作到此结束,按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万人脸中找到你。