手上有一个项目,需要检验使用本程序的,是否本人!因为在程序使用前,我们都已经做过头像现场采集,所以源头呢是不成问题的,那么人脸检测,人脸比对,怎么办呢?度娘了下,目前流行的几个人脸检测,人脸比对核心,大多都是基于互联网的,但我们的项目是基于本地服务器,那就有点麻烦了,后来找到ArcFace.它的核心允许本地调用,那就好办了,立刻去了虹软的开放平台,看论坛,下DEMO;我当时下的是这个:ArcFace C#DEMO

本以为可以一帆风顺的就可以把项目搞定了,不想…噩梦才刚刚开始呢…且听我细细道来:

首先说下我的调用逻辑; 项目里有一个采集端(每个业务窗口),负责采集现场人像,并通过ArcFace人脸检测,特征提取,获取到.dat比对源(ServiceFaceModels),然后存到数据库(blob);

项目里的应用端(用户手机),随机时间的调用摄像头,采集到被比对图片;并对该记录进行标记;

项目时比对端(服务器),定时询问数据库,哪些被标记记录需要比对,然后通过数据库记录,找到该图片,并通过ArcFace人脸检测,特征提取,获取到.dat被比对源(LocalFaceModels) 然后将这两个源在内存中进行比对,得分高于0.7的,就通过;

前两端就不多说了,都是一些常规的操作.重点讲下比对端(服务器);

先说我做的第一个版本,做的是一个控制台程序;

//首先定义了一个调用类; MatchUserFace;它里边包含了初始化,人脸检测,特征提取,人脸比对,以及一些辅助方法;

//然后在Program里定义了一个委托,这个委托的作用,就是能够让我可以带参数进去ArcFace的检测与比对核心;

public delegate bool MatchHandler(string userid, string studyid, string photoid, string photopath);

//最后我的Program里边,就是做一个递归,去不断的问数据库拿被标志需要进行核对的记录,拿到图片后,就进行比对; QueryDataFile(string upstate);

下边这段就是在QueryDataFile();去实现异步调用比对核心;

MatchHandler handler = new MatchHandler(MatchUserFace.GetAndMatchImage);
string Identification = string.Format("USERID:{0} STUDYID:{1} PHID:{2}", userid, studyid, photoid);
IAsyncResult result = handler.BeginInvoke(userid, studyid, photoid, pathstr, RecognizeEngine, DetectEngine, new AsyncCallback(CallbackFunc), Identification);

下边这段就是异步的结果回调;

 static void CallbackFunc(IAsyncResult result)
{
MatchHandler handler = (MatchHandler)((AsyncResult)result).AsyncDelegate;
bool match = handler.EndInvoke(result);
string strmatch = string.Empty;
if (match)
{
strmatch = " 比对结果:OK";
}
else
{ strmatch = " 比对结果:NO";
}
Console.WriteLine(result.AsyncState + strmatch);
GC.Collect();
}

  

写好了,发布到服务器上,还想着中午吃个鸡腿奖励下自己;不想…发布后不到两小时,小弟来说:服务器是不是出问题了,下边所有业务窗口访问速度严重延迟…立马跑到机房去看,一看没毛病呀,所有的服务都好好的,没有卦死..再打开资源监视器一看,靠…那个比对端一下吃3个多G的内存,而且还在不断上升中…立马停掉,然后再问小弟,下边业务是否正常,他回复正常了…那么说,就是我写的这个比对端有问题了!改!!!

第二个版本,

下了机房看代码…左看右看,没有哪不对呀,一步步按步就班的…毫无头绪时,就想,是不是服务器内存不够而已,打申请拿了64G回来.再开程序也是一样吃的很紧,但是下边业务窗口倒是不延时,看来内存增大还是有好处的…呵…;但是源头问题还是没解决,不行的呀!到了晚饭时,一道灵光拍进脑门,我看到代码里我是每异步调用一次,就初始化一次ArcFace的SDK.我就想,是不是这个原因导致呢?修改方法,去试试!! //把那个委托改成如下:

public delegate bool MatchHandler(string userid, string studyid,  string photoid, string photopath, IntPtr RecognizeEngine, IntPtr DetectEngine);

//然后初始化SDK放到了Program里做:

string appId = "4yHjnxK94FCK6L7HaJieWawSLubnANXXXXX";
string sdkFDKey = "7S6Xp4mtroLnjTt7qDYnd2dqHXXXXX";
string sdkFRKey = "7S6Xp4mtroLnjTt7qDYnd2dxSgXXXXX";
int retCode = AFDFunction.AFD_FSDK_InitialFaceEngine(appId, sdkFDKey, pMem, detectSize, ref DetectEngine, 5, nScale, nMaxFaceNum);
int retCode2 = AFRFunction.AFR_FSDK_InitialEngine(appId, sdkFRKey, pMemRecongnize, detectSize, ref RecognizeEngine); 

//最后把异步调用的方法改成如下:

  MatchHandler handler = new MatchHandler(MatchUserFace.GetAndMatchImage);
string Identification = string.Format("USERID:{0} STUDYID:{1} PHID:{2}", userid, studyid, photoid);
IAsyncResult result = handler.BeginInvoke(userid, studyid, photoid, pathstr, RecognizeEngine, DetectEngine, new AsyncCallback(CallbackFunc), Identification);

再次发布到服务器.然后再到资源监视器去看,哟…线程数不高了而且增长的还不快…好开心!!以为搞好了;就回宿舍睡觉去了!!不想…睡得迷糊的时候,我们的客服小妹妹的电话就打到我这了,我说什么事,她说现在大面积反映用户比对不了?what?我说不可能吧,是不是当地电信故障呀?我自己拿手机试了下,真的不行呀!!!快速赶回办公室远程看了下服务器,我的乖乖…比对端卦了!!!我再看日志,日志没有捕捉到程序异常,只是捕到了个:Value cannot be null.Parameter name: source;我吃你大米了,我刨你家玉米地了,为啥要这么对我!重启比对端,然后都可以正常运作了…我决定在这监视这个比对端,在资源监视器我到是发现了一个:w3wp.exe它在不断的涨内存(这是要划重点的)想想这可已经是深夜了.果不出其然,运行了大概两个多小时后,程序又卦了.我的乖乖,为啥会这样呢,一时半会也想不出办法呀!我也总不能呆在服务器旁它停了,我就重启吧! 第二天致电虹软,反映了程序会运行一段时间就会卦掉,虹软这边也提出了很多宝贵意见,

1.先着眼把捕捉到的那个错误,查出来,看看是否处理好了,程序还会不会卦;那我就在程序里增加了日志打印,还真就发现了几个在DEMO里没有处理到的问题:

  1. 每个Marshal.AllocHGlobal,用完以后,一定要释放;
  2. AFD_FSDK_StillImageFaceDetection;AFR_FSDK_ExtractFRFeature;这两个函数要判断返回值是否等于0; 所以 MatchUserFace 调用类我作了如下修改
    private static byte[] detectAndExtractFeature(Image imageParam, out Image facerect,
IntPtr RecognizeEngine, IntPtr DetectEngine)
{
byte[] feature = null; facerect = null; try
{
int width = 0; int height = 0; int pitch = 0;
Bitmap bitmap = new Bitmap(imageParam);
byte[] imageData = getBGR(bitmap, ref width, ref height, ref pitch);
IntPtr imageDataPtr = Marshal.AllocHGlobal(imageData.Length);
Marshal.Copy(imageData, 0, imageDataPtr, imageData.Length); ASVLOFFSCREEN offInput = new ASVLOFFSCREEN();
offInput.u32PixelArrayFormat = 513;
offInput.ppu8Plane = new IntPtr[4];
offInput.ppu8Plane[0] = imageDataPtr;
offInput.i32Width = width;
offInput.i32Height = height;
offInput.pi32Pitch = new int[4];
offInput.pi32Pitch[0] = pitch;
AFD_FSDK_FACERES faceRes = new AFD_FSDK_FACERES();
IntPtr offInputPtr = Marshal.AllocHGlobal(Marshal.SizeOf(offInput));
Marshal.StructureToPtr(offInput, offInputPtr, false);
IntPtr faceResPtr = Marshal.AllocHGlobal(Marshal.SizeOf(faceRes)); //人脸检测
int detectResult = AFDFunction.AFD_FSDK_StillImageFaceDetection(DetectEngine, offInputPtr, ref faceResPtr);
if (detectResult == 0)
{
try
{
object obj = Marshal.PtrToStructure(faceResPtr, typeof(AFD_FSDK_FACERES));
faceRes = (AFD_FSDK_FACERES)obj;
for (int i = 0; i < faceRes.nFace; i++)
{
MRECT rect = (MRECT)Marshal.PtrToStructure(faceRes.rcFace + Marshal.SizeOf(typeof(MRECT)) * i, typeof(MRECT));
int orient = (int)Marshal.PtrToStructure(faceRes.lfaceOrient + Marshal.SizeOf(typeof(int)) * i, typeof(int));
if (i == 0)
{
facerect = CutFace(bitmap, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
}
}
}
catch (Exception ex)
{ LogNetWriter.Error("人脸检测时出错:" + ex.Message);
} } if (faceRes.nFace > 0)
{
try
{
AFR_FSDK_FaceInput faceResult = new AFR_FSDK_FaceInput();
int orient = (int)Marshal.PtrToStructure(faceRes.lfaceOrient, typeof(int));
faceResult.lOrient = orient;
faceResult.rcFace = new MRECT();
MRECT rect = (MRECT)Marshal.PtrToStructure(faceRes.rcFace, typeof(MRECT));
faceResult.rcFace = rect;
IntPtr faceResultPtr = Marshal.AllocHGlobal(Marshal.SizeOf(faceResult));
Marshal.StructureToPtr(faceResult, faceResultPtr, false); AFR_FSDK_FaceModel localFaceModels = new AFR_FSDK_FaceModel();
IntPtr localFaceModelsPtr = Marshal.AllocHGlobal(Marshal.SizeOf(localFaceModels));
int extractResult = AFRFunction.AFR_FSDK_ExtractFRFeature(RecognizeEngine, offInputPtr, faceResultPtr, localFaceModelsPtr);
if (extractResult == 0)
{
Marshal.FreeHGlobal(faceResultPtr);
Marshal.FreeHGlobal(offInputPtr); object objFeature = Marshal.PtrToStructure(localFaceModelsPtr, typeof(AFR_FSDK_FaceModel)); Marshal.FreeHGlobal(localFaceModelsPtr); localFaceModels = (AFR_FSDK_FaceModel)objFeature;
feature = new byte[localFaceModels.lFeatureSize];
Marshal.Copy(localFaceModels.pbFeature, feature, 0, localFaceModels.lFeatureSize); localFaceModels = new AFR_FSDK_FaceModel();
}
}
catch (Exception ex)
{ LogNetWriter.Error("提取特征时出错:" + ex.Message);
} } bitmap.Dispose();
imageData = null;
Marshal.FreeHGlobal(imageDataPtr);
//Marshal.FreeHGlobal(faceResPtr);
offInput = new ASVLOFFSCREEN();
faceRes = new AFD_FSDK_FACERES();
}
catch (Exception ex)
{
LogNetWriter.Error("识别人脸并提取人脸特征出错:" + ex.Message);
}
return feature;
}

当然了,比对的时候也作了一些修改,就是当比对完了以后,就做了指针释放;

  Marshal.FreeHGlobal(firstFeaturePtr);
Marshal.FreeHGlobal(secondFeaturePtr);
Marshal.FreeHGlobal(firstPtr);
Marshal.FreeHGlobal(secondPtr);

经过这一次修改后,再发布到服务器,哟…不错哦..运行的时间久了…但还是会卦,而且那个w3wp.exe还是会不断的拉内存;这个版本的运行时间可以达到4小左右了;我就想总得有个解决办法吧;再次致电虹软,再次反映这个问题,虹软这边给我的建议就是不要去进行多线程,我想想也对,要把逻辑简单化,我就把识别核心打包成一个EXE.然后在Program里调用这个EXE.意思就是每当我有需要识别的图片,我就调一个EXE.然后EXE处理完以后,就自我释放了… 于是我改了第三版:

 //这里就是一条线程在做处理
string strmatch = string.Empty;
ControlExeClass _ControlExeClass = new Model.ControlExeClass();
//这个方法是调一个EXE,EXE的内容是:ControlExeClass.cs;
//做的任务就是把图片进行人脸检测,人脸特征提取,人脸识别;
bool bo = _ControlExeClass.ControlExe(userid, studyid, photoid, pathstr);
if (bo)
{
iCheck_OK++;
label5.Text = iCheck_OK.ToString();
strmatch = " 比对结果:OK";
}
else
{ strmatch = " 比对结果:NO";
}
string dates = " 比对时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); textBox1.Text += " USERID:" + userid + " STUDYID:" + studyid + " PHID:" + photoid + strmatch + dates + Environment.NewLine;

  

然后那个EXE就是沿用MatchUserFace调用类,在EXE的主线程里完成调用;还别说,用了这个方法后,内存不拉升了,而且w3wp.exe上涨,也只是在EXE工作的一刹那上来,EXE干完活后,它就会生成一个新的w3wp.exe,旧的w3wp.exe那个会被注销掉…哗…想想就开心,终于如愿解决了问题,但….当一个人觉得越顺利时,往往大麻烦就会来了.正如我觉得上天不会对我那么好一样,运行了大概一天后,程序还是卦了.苍天呀,大地呀,我到底做错了什么…. 正在我一筹莫展时,我就老记恨这个w3wp.exe,到底是什么东东,好,度娘下彻底了解下它. 度娘是这么形容它的: w3wp.exe是在IIS(因特网信息服务器)与应用程序池相关联的一个进程,如果你有多个应用程序池,就会有对应的多个w3wp.exe的进程实例运行。这个进程用来分配大量的系统资源。 好,既然说我的IIS里的应用程序池,那我就对我的应用程序池进行固定内存回收不就好了嘛;我就对线程池做了一个固定内存回收,当达到400000KB时就做一次回收. 这一下设置做下去后,的确是立竿见影的,当EXE工作时w3wp.exe就从来没高过400000KB;我想这一下应该彻底解决了吧;可是….程序还是卦了….我是真的不得上天倦顾呀… 一连几天毫无头绪,胡子长一脸了,也没心思刮,领导这边还想刮我骨头呢…唉…上下压力都好大呀.搞得我肚子也不舒服,就去厕所蹲了个坑,还别说,这个坑,含金量特高.又一道灵光打进了我的脑门,我想呀,是不是我的递归出现了问题呢???我就回去看了下代码,我的递归逻辑是没有问题的呀,一步步有板有眼,这是怎么回事呢,我又度娘了下,关于C#的递归,是这么形容的:一个算法中,由于递归调用次数过多,堆栈是会溢出。递归使用的内存大小累计达4G,系统就会进行内存回收. 至于何时收,怎么收,就是windows的事情了.乖乖…既然有这么一个限定,我不用不就好了嘛,我就用死询还不好吗? 所以第4版修改如下:

 private static void CycleData()
{
while (true)
{
if (_DoWork)
{
break;
}
else
{
QueryDataFile("U");
Thread.Sleep(1500);
} Thread.Sleep(2000);
} }

  

至此所有问题解决!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!哦…..忘说了,我后来没有单独调用EXE这种方法了,改成了第一版的控制台程序,其结果是一样的;

现在…呵呵…我可是十分轻松着座在大班椅上,喝着奶茶,身边座着小秘,我说,她打的这篇文章…呵….开玩笑了,文章里每个字都是我自己亲手敲的,同时也十分感谢虹软能提供这么优秀的SDK供我使用,更要感谢虹软的技术支持,给我莫大的帮助; 最后总结几点:

1.SDK可以只初始化一次,然后ref传参进结构体,就可以一直用下去;

2.每个Marshal.AllocHGlobal,用完以后,一定要释放;

3.可以异步回调进行;

4.AFRFunction.AFR_FSDK_ExtractFRFeature; AFDFunction.AFD_FSDK_StillImageFaceDetection; 这两个函数要判断返回值是否等于0;

5.最最最重要一点,严禁使用递归去调用;宁愿用死询代替;(因为这个就是导致我程序死掉的主因),因为递归要是深度太大,而且次数过多,累计内存使用达4G以上,系统就会做一次线程与内存回收,至于怎么收,何时收就是不定时的,所以一定不要用递归,这个是我在C#官方看到对于递归的解释;

6.如果是使用windows服务器进行虹软SDK的;建议IIS线程池做一个固定内存回收机制; 最后上传一下几个示例片段吧,因为个中涉及到一些数据库操作,我整个工程就不上传了

人脸识别ArcFace C#DEMO 开发应用全过程的更多相关文章

  1. 基于Arcface 免费离线人脸识别 2.0 Demo C#

    本来打算做个C#版demo,但没用成功.使用虹软最新人脸识别技术开发完成 过程如下: 1. 传入一张单人脸照片: 2.调用检测人脸函数ASFDetectFaces,成功返回人脸信息的指针: 3.使用 ...

  2. C# ArcFace 免费人脸识别 2.0 demo

    **配置过程:** 1. 到[虹软官网](https://ai.arcsoft.com.cn/index.htm?utm_source=csdn&utm_medium=referral)下载S ...

  3. 人脸识别 ArcFace Demo [Windows]

    Arcsoft ArcfaceDemo for Windows, VS2013 C++   使用虹软技术开发完成 使用步骤: 1.下载SDK包,32位Windows平台将五个SDK包里lib中的文件到 ...

  4. C++ 虹软人脸识别 ArcFace 2.0 Demo

    环境配置: 开发环境:Win10 + VS 2013 SDK版本:ArcFace v2.0 OpenCV版本:2.4.9 平台配置: x64.x86下Release.Debug SDK 下载地址:戳这 ...

  5. 虹软人脸识别——官方 Qt Demo 移植到 Linux

    一.前言 最近需要在 Linux 平台下开发一个人脸识别相关的应用,用到了虹软的人脸识别 SDK.之前在 Windows 平台用过,感觉不错,SDK 里面还带了 Demo 可以快速看到效果.打开 Li ...

  6. 人脸识别最新开发经验demo分享

    本来打算做个C#版demo,但没用成功,基于虹软的免费人脸识别技术 过程如下: 1. 传入一张单人脸照片: 2.调用检测人脸函数ASFDetectFaces,成功返回人脸信息的指针: 3.使用 Mar ...

  7. Android静态图片人脸识别的完整demo(附完整源码)

    Demo功能:利用android自带的人脸识别进行识别,标记出眼睛和人脸位置.点击按键后进行人脸识别,完毕后显示到imageview上. 第一部分:布局文件activity_main.xml < ...

  8. 基于百度AI人脸识别技术的Demo

    编写demo之前首先浏览官方API:http://ai.baidu.com/docs#/Face-API/top 下面是源码: package com.examsafety.test; import ...

  9. 人脸识别分析小Demo

    人脸识别分析 调用 腾讯AI人脸识别接口 测试应用 纯py文件测试照片 # -*- coding: utf-8 -*- import json from tencentcloud.common imp ...

随机推荐

  1. 强化学习(十九) AlphaGo Zero强化学习原理

    在强化学习(十八) 基于模拟的搜索与蒙特卡罗树搜索(MCTS)中,我们讨论了MCTS的原理和在棋类中的基本应用.这里我们在前一节MCTS的基础上,讨论下DeepMind的AlphaGo Zero强化学 ...

  2. LeetCode二叉树的前序、中序、后序遍历(递归实现)

    本文用递归算法实现二叉树的前序.中序和后序遍历,提供Java版的基本模板,在模板上稍作修改,即可解决LeetCode144. Binary Tree Preorder Traversal(二叉树前序遍 ...

  3. SQLServer之创建数据库架构

    创建数据库架构注意事项 包含 CREATE SCHEMA AUTHORIZATION 但未指定名称的语句仅允许用于向后兼容性. 该语句未引起错误,但未创建一个架构. CREATE SCHEMA 可以在 ...

  4. Install Windows 2016 on VirtualBox

    Download ISO file for Windows 2016 (180 days free).  https://www.microsoft.com/en-us/evalcenter/eval ...

  5. C# 接口《通俗解释》

    接口 为什么要用接口?好处在哪里? 如果你的工作是一个修水管的,一天客户找上你让你帮装水管,但是有个要求,就是客户喜欢管子是三角形的. 你立马买了三角形的水管回来,在墙上弄个三角形的口子,客户付了钱, ...

  6. ASP.NET的版本?

    问题源于这么一本书: <ASP.NET 4 解密(卷1)>,这本书大约是六七年前买的了,根据其名字,它讲述的是ASP.NET 4,那么ASP.NET现在究竟是什么版本?与.NET Fram ...

  7. 2019腾讯WXG移动客户端暑期实习面经

    微信这个比较迷,二面完官网流程灰了,但是过了一周多突然来三面,下午面完三面晚上HR面,第三天offer call, 莫名其妙过了 之前以为已经挂了,所以没有写面经,现在距一面已经快一个月了,只能还记得 ...

  8. Identity Server 4 预备知识 -- OAuth 2.0 简介

    OAuth 2.0 简介 OAuth有一些定义: OAuth 2.0是一个委托协议, 它可以让那些控制资源的人允许某个应用以代表他们来访问他们控制的资源, 注意是代表这些人, 而不是假冒或模仿这些人. ...

  9. mysql的学习笔记(七)

    1.自定义函数,函数可以返回任意类型的值,同样可接说这些类型的参数. CREATE FUNCTION function_name RETURNS {STRING|INTER|REAL|DECIMAL} ...

  10. .NET Core微服务之基于Ocelot实现API网关服务(续)

    Tip: 此篇已加入.NET Core微服务基础系列文章索引 一.负载均衡与请求缓存 1.1 负载均衡 为了验证负载均衡,这里我们配置了两个Consul Client节点,其中ClientServic ...