效率有点低,你们看看哪里开能够节省时间?
源代码:https://github.com/catzhou2002/ArcFaceDemo
整个项目使用虹软技术完成开发java
说实话,为了提升识别效率,我也是竭尽所能,干了很多自认为的优化,若有兴趣听我说说。git
第一部分 单线程时候的各类折腾github
1、折腾LPASVLOFFSCREEN
话说这个LPASVLOFFSCREEN的结果文档里面没有说明,或者是我没找到。
我也不知道从哪里复制来的,主要折腾的是ppu8Plane[0]地址,通常操做是数组
一、锁定图片内存
二、ppu8Plane[0]分配制定长度的内存
三、把图片内存中的字节复制到一个临时数组
四、而后用Marshal.Copy复制到指定的地址
五、解锁图片内存
我改为:
一、锁定图片内存
二、ppu8Plane[0]指向图片地址
三、等不须要LPASVLOFFSCREEN时(人脸检测、获取特征值、性别判断、年龄估算等结束后)解锁图片内存微信
就晚一点解锁,省了好多事情,耗时由4毫秒没成2微妙。当时就发了个帖:C# Bitmap转ASVLOFFSCREEN的最佳方式?多线程
后来以为这名字实在记不住,也不C#,改为了ImageData,整个转换过程以下:编辑器
ar bmpData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); var imageData = new ImageData { PixelArrayFormat = 513,//Rgb24, Width = bitmap.Width, Height = bitmap.Height, Pitch = new int[4] { bmpData.Stride, 0, 0, 0 }, ppu8Plane = new IntPtr[4] { bmpData.Scan0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero } }; bitmap.UnlockBits(bmpData);
其实若是是视频图片的话,图片的宽度和高度都是固定的,想了想,没折腾。ide
2、单线程时将获取到的FaceModel直接作人脸比对的参数测试
ExtractFeature(_FaceMatchEngine, ref imageData, ref faceFeatureInput, out var <font color="#ff8c00">faceModel</font>); FacePairMatch(_FaceMatchEngine, ref fm, ref <font color="#ff8c00">faceModel</font>, out float score);
通常操做是faceModel里面的字节复制到临时字节数组,而后建立新的FaceModel,分配内存,在将临时字节数组复制到FaceModel。优化
3、人脸库直接用FaceModel
/// <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> <font color="#ff8c00"> public FaceModel FaceModel { get; set; }</font> } }
4、比对结果>0.5就算成功
5、人脸库增长OrderId
识别成功后再次比对就很快,应该是首发命中。
6、将人脸比对和结果显示分开
一开始没想太多,将人脸比对和结果显示放在新视频帧事件里面,流程是:
新视频帧(30帧/秒)
获取检测和识别的结果(人脸框和ID)
显示检测和识别的结果
结果视频卡顿,获取人脸特征的200毫秒成为瓶颈,改为:
人脸比对
Task.Factory.StartNew(() => { Task.Delay(1000).Wait(); while (!_CancellationTokenSource.IsCancellationRequested) { #region 200毫秒左右 MatchFrame(); #endregion } }, _CancellationTokenSource.Token);
结果显示
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); }
测试了一下,效果还能够,就在博客园发表了
C# 虹软SDK视频人脸识别和注册
,还顺手弄了个打赏二维码。
发表完以为这么辛苦写出来的文章,必须到首页去亮个相,9天后终于学会发表到博客园首页了,因而删除了打赏二维码,去首页亮了个相。
话说首页和非首页效果着实不同,截图为证:

第二部分 多线程的折腾
1、肯定4线程为最佳
各类测试后得出的结论,也不知道对不对,也不知道为何,哎。
因网友的要求,同步到了github
2、删除了单线程
有了更快的,就不要慢的了。
3、n张脸如何分配给4个线程获取特征值?
动了很多脑筋,Interlocked.Increment是关键。
最终有改了下面的内容
若是只有一张脸(窃觉得一张脸的几率比较高),也用Task,影响效率,增长了 if (detectResult.FaceCount ==
1)
Intptr之间复制字节用CopyMemory比较快
两三张脸的时候开4个线程很差,改为 new Task[TaskNum < detectResult.FaceCount ?
TaskNum : detectResult.FaceCount]
4、识别结果(集)的折腾
弄了个结果集,按最大人脸数设了个List( Items = new List();)
增长了FaceFeatureInput FFI,省的每次都去建立
并将人脸方向设成1(Orient = 1)(由于是视频图片,其余方向的人脸,呵呵),人脸检测后都不要去获取人脸方向的值
增长并初始化了FaceModel(FaceModel FaceModel = new FaceModel() { Size =
22020, PFeature = Marshal.AllocCoTaskMem(22020) };),获取到的特征字节直接复制过来即可
5、保存特征值到人脸库的时候同时保存头像
由于虹软说了,sdk升级的时候,特征值也有可能变化。那咱先把头像保存起来,到时候从新生成一下。
主要的操做是把矩形放大一点(Inflate((int)(r.Width * 0.5), (int)(r.Height * 0.5))),咱保存的头像怎么着得是我的头吧。
(想来条分割线,竟然只有华丽的分割线,算了。顺便吐槽一下,这个论坛的编辑器实在是让人无语_)
各类折腾后,黔驴技穷了,10,000人脸的库得出10张不认识的脸的结论,须要10秒钟。固然,换好一点的电脑能够提升效率,如个人台式机(i5-7500),输入图片只有1张脸的时候,遍历
1万张人脸仅需390毫秒
5万张人脸也就1525毫秒
10万张人脸说我内存不够,多是个人程序是32位的缘故,换成64位的sdk估计3秒钟也能搞定(太麻烦,不折腾了)
结论是:虹软中型sdk用于考勤、小区门禁、写字楼门禁等场所彻底没问题。
下一步我打算(其实已经差很少完成,我公司的项目——酒店自助机)改为单脸多线程识别,增长如下功能:
40次检测人脸数为0,则确认为没人,识别频率下降是否换人了?同一我的三、4次识别不出ID后,确认为陌生人,不在遍历刷身份证获取照片人脸比对后存入人脸库另外想跟企业微信结合开发开发门禁、CRM什么的,有兴趣的朋友一块儿交流交流?