无论你注意到没有,人脸识别已经走进了生活的角角落落,钉钉已经支持人脸打卡,火车站实名认证已经增长了人脸自助验证通道,更别提各个城市建设的『智能城市』和智慧大脑了。在人脸识别业界,一般由人脸识别提供商和人脸识别应用接入方组成,从头至尾研发人脸识别技术须要极强的专用技术知识和数学算法功底,对于大多数企业来讲,选择人工智能AI公司现成的人脸识别技术引擎是一个比较适合的解决方法。虹软公司在2017年开放了人脸识别平台1.0版本,通过三年的技术迭代和更新,目前已经推出了2.2版本,主打离线,免费,适合场景比较普遍。为了方便开发者接入,虹软官方提供了各个语言版本的Demo程序,因为虹软并无提供C#版本的SDK,所以,他们提供的C#版本的SDK就更有参考价值了。javascript
虹软Demo的下载地址以下:https://github.com/ArcsoftEscErd/ArcfaceDemo_CSharp_2.2 在开始以前,建议你下载它。php
人脸识别,是基于人的脸部特征信息进行身份识别的一种生物识别技术。用摄像机或摄像头采集含有人脸的图像或视频流,并自动在图像中检测和跟踪人脸,进而对检测到的人脸进行脸部识别的一系列相关技术,一般也叫作人像识别、面部识别。 而人脸识别的过程能够简的归纳为:检测人脸框->提取人脸特征信息->人脸库检索匹配信息三个过程。html
人脸识别主要用于身份识别。因为视频监控正在快速普及,众多的视频监控应用迫切须要一种远距离、用户非配合状态下的快速身份识别技术,以求远距离快速确认人员身份,实现智能预警。人脸识别技术无疑是最佳的选择,采用快速人脸检测技术能够从监控视频图象中实时查找人脸,并与人脸数据库进行实时比对,从而实现快速身份识别。 在现实生活中,从最多见的人脸门禁,到实名制安检,景区验票,公司或者学校的人脸签到,无人超市等都有普遍的应用。java
活体检测检测顾名思义就是经过识别活体上的生理信息,来区分用照片、硅胶、塑料等非生命物质伪造的生物特征。人脸识别应用中的活体检测技术用来判断系统采集到的人脸图像是否来源于真实的人脸,以防止照片、视频等伪造的人脸图像被输入到系统形成误判,活体检测在无人值守场景下的人脸识别商业应有中显得相当重要。git
目前市面上有不少人脸识别技术方案,从是否须要使用时联网能够分为在线和离线,从接入方式上能够分为本地识别和服务器大数据识别等方式,虹软提供的是基于本地算法特征的离线识别SDK,其基础算法使用C编写,提供全平台的离线支持.github
虹软人脸识别SDK经过视觉开放平台提供,包含人脸识别场景中最经常使用到的功能组件,例如:人脸检测,人脸识别,年龄性别检测,活体检测 等,其中人脸检测针对静态和动态检测场景分别进行了算法优化,从中派生的性别和年龄检测扩充了人脸识别的使用场景,活体检测组件能够有效的保证人脸识别应用的安全性。 访问https://ai.arcsoft.com.cn/third/mobile.html?cnblogs 按照网站的提示,能够注册用户和下载SDK包。算法
虹软的SDK,和大量基于Restful风格的接口不一样,没有使用普通的基于HTTP的方式,也并无提供C#语言的SDK包,仅提供了C语言的SDK,对于C#接入有必定的困难,在发布之初有很多大神自行编写接入Demo程序,后来,虹软官方也出了Demo程序,从2018年1月的第一个版本到如今随着SDK更新的2.2版本,代码结构和注释更为清晰。数据库
Demo是标准的C# WINFORM工程样式,经过GitHub,下载下来以后,能够直接使用VS打开. 打开以后,有一个readme.md文件,十分重要,在开始以前,请务必仔细看一下。这里把要点给你们总结一下。api
通常OK以后,系统弹出正常运行的窗口,网上找几张明星照片进行注册,对比。 以下图所示:安全
能够看到,虹软Demo已经能够正确的识别人脸信息。
Demo中还提供了活体检测功能,若是你的机器没有摄像头,能够外插一个USB的摄像头,点击启用摄像头,打开它。
若是咱们用本身的人脸识别,会显示RGB活体,若是是用照片或者视频尝试识别,会显示『RGB假体』
接下来进入正题,让咱们打开工程视图,从代码角度解析一下虹软人脸识别Demo的代码结构及主要流程。
从上图中能够发现代码结构仍是很清晰
目录 | 说明 |
---|---|
Entity | 用于放置一些实体类 |
lib | 放置的第三库,主要是用于获取视频帧的内容 |
SDKModels | SDK的字段模型类,主要是和SDK进行交互,普通使用时无需关注 |
SDKUtils | 针对SDK功能的C#封装,建议使用Utils中的二次封装类 |
Utils | 提供的一些工具类,这些类将复杂的SDK操做变得简单,咱们能够在项目中直接使用这些类 |
全部的界面功能都在FaceForm.cs中,咱们打开代码视图,代码各个区域的代码结构清晰,咱们来看一下主要部分的功能。
参数定义部分主要是针对一些参数进行定义,有相应的注释,咱们须要关注的是图片大小和类似度。
private long maxSize = 1024 * 1024 * 2;
这个参数定义了能够识别的最大图片大小,能够根据须要进行调整。
private float threshold = 0.8f;
这个参数定义了置信度,也就是当类似度达到多少时,咱们认为是一我的
初始化部分的一个重要的方法 InitEngines(),做用是用来初始化人脸识别引擎。
这部分的代码首先获取配置文件的信息,而后读取这些信息,并进行引擎的激活操做,若是出现错误,则弹出提示信息。
这里须要注意的是,因为C#是支持多CPU架构的,虹软SDK的32和64位的版本对应的dll并不相同,因此须要咱们自行判断当前是运行在哪一个模式下的。
var is64CPU = Environment.Is64BitProcess;
在判断CPU以后,尝试加载对应的DLL,并调用激活过程。
int retCode = 0; try { retCode = ASFFunctions.ASFActivation(appId, is64CPU ? sdkKey64 : sdkKey32); } catch (Exception ex) { //禁用相关功能按钮 ControlsEnable(false, chooseMultiImgBtn, matchBtn, btnClearFaceList, chooseImgBtn); if (ex.Message.Contains("没法加载 DLL")) { MessageBox.Show("请将sdk相关DLL放入bin对应的x86或x64下的文件夹中!"); } else { MessageBox.Show("激活引擎失败!"); } return; }
虹软SDK须要激活才能使用,在激活时,必须保证你的设备能够链接到互联网。若是没法链接会激活失败。
接下来的代码,对于引擎的功能进行配置,在大多数状况下,咱们保持默认配置便可。若是须要调整,能够重点关注下面的参数
//人脸在图片中所占比例,若是须要调整检测人脸尺寸请修改此值,有效数值为2-32 int detectFaceScaleVal = 16; //最大须要检测的人脸个数 int detectFaceMaxNum = 5;
detectFaceScaleVal 为人脸占用图片的比例,简单的说,就是一张脸在图片中的比例,这个数值越大,可以检测到的人脸越小。detectFaceMaxNum 就是检测到的最大人脸数,检测人脸越多,程序须要占用的内存也就越多。
接下来的参数combinedMask,定义了引擎的能力,建议默认保持全开,若是对性能有所要求,能够只开启必要的功能。
//引擎初始化时须要初始化的检测功能组合 int combinedMask = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_AGE | FaceEngineMask.ASF_GENDER | FaceEngineMask.ASF_FACE3DANGLE;
调用 ASFFunctions.ASFInitEngine 就能够初始化引擎
retCode = ASFFunctions.ASFInitEngine(detectMode, imageDetectFaceOrientPriority,
detectFaceScaleVal, detectFaceMaxNum, combinedMask, ref pImageEngine);
retCode返回值为0时表明初始化成功。
按照相同的方法初始化其它引擎,包括 人脸检测FR引擎,RGB专用FR引擎,IR专用RGB引擎,它们只是参数不一样,在实际使用中,咱们能够根据须要进行微调。
其它相似的操做,能够在此页面中查看,因为虹软Demo已经对操做进行了详细的封装,展示在FaceForm.cs中的代码都是一些和控件交互的代码,再详细分析意义并不大,接下来咱们分析一些比较细致的代码,也就是潜藏在FaceUtil类中的一些函数的实现.
检测人脸信息有两种方式,从照片中检测和从视频中检测,先看从照片中检测
public static ASF_MultiFaceInfo DetectFace(IntPtr pEngine, Image image) { lock (locks) { ASF_MultiFaceInfo multiFaceInfo = new ASF_MultiFaceInfo(); if (image != null) { /*若是照片大小过大,则进行缩放并对齐*/ if (image.Width > 1536 || image.Height > 1536) { image = ImageUtil.ScaleImage(image, 1536, 1536); } else { /*若是照片大小正常,直接进行对齐*/ image = ImageUtil.ScaleImage(image, image.Width, image.Height); } if(image == null) { return multiFaceInfo; } /*转化为SDK专用格式,后面须要手工释放内存*/ ImageInfo imageInfo = ImageUtil.ReadBMP(image); if(imageInfo == null) { return multiFaceInfo; } /*调用引擎*/ multiFaceInfo = DetectFace(pEngine, imageInfo); /*释放图片占用的内存*/ MemoryUtil.Free(imageInfo.imgData); return multiFaceInfo; } else { return multiFaceInfo; } } }
注意上述代码中两个比较重要的 ScaleImage 和 ReadBMP 方法,其中ScaleImage方法是将图片处理成虹软人脸引擎建议的格式,须要图片的宽度为4的整数倍。
public static ImageInfo ReadBMP(Image image) { ImageInfo imageInfo = new ImageInfo(); Image<Bgr, byte> my_Image = null; try { //图像灰度转化 my_Image = new Image<Bgr, byte>(new Bitmap(image)); imageInfo.format = ASF_ImagePixelFormat.ASVL_PAF_RGB24_B8G8R8; imageInfo.width = my_Image.Width; imageInfo.height = my_Image.Height; imageInfo.imgData = MemoryUtil.Malloc(my_Image.Bytes.Length); MemoryUtil.Copy(my_Image.Bytes, 0, imageInfo.imgData, my_Image.Bytes.Length); return imageInfo; } catch (Exception ex) { Console.WriteLine(ex.Message); } finally { if (my_Image != null) { my_Image.Dispose(); } } return null; }
这里须要注意,此方法中调用了 MemoryUtil.Malloc 方法分配了非托管内存,在后面须要调用 MemoryUtil.Free() 方法释放内存。
检测结果返回为 ASF_MultiFaceInfo 结构体,其中的faceRects 为人脸结果集,faceNum 为人脸数目,经过下面的代码,可获得人脸识别的位置信息
MRECT rect = MemoryUtil.PtrToStructure<MRECT>(multiFaceInfo.faceRects);
FaceUtil类中还提供了 年领检测和性能检测的方法。AgeEstimation和GenderEstimation,其基本操做方式也是先申请内存,而后调用原生的Native的对应方法,再释放内存的过程。
public static ASF_AgeInfo AgeEstimation(IntPtr pEngine, ImageInfo imageInfo, ASF_MultiFaceInfo multiFaceInfo, out int retCode) { retCode = -1; IntPtr pMultiFaceInfo = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_MultiFaceInfo>()); MemoryUtil.StructureToPtr(multiFaceInfo, pMultiFaceInfo); if (multiFaceInfo.faceNum == 0) { return new ASF_AgeInfo(); } //人脸信息处理 retCode = ASFFunctions.ASFProcess(pEngine, imageInfo.width, imageInfo.height, imageInfo.format, imageInfo.imgData, pMultiFaceInfo, FaceEngineMask.ASF_AGE); if (retCode == 0) { //获取年龄信息 IntPtr pAgeInfo = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_AgeInfo>()); retCode = ASFFunctions.ASFGetAge(pEngine, pAgeInfo); Console.WriteLine("Get Age Result:" + retCode); ASF_AgeInfo ageInfo = MemoryUtil.PtrToStructure<ASF_AgeInfo>(pAgeInfo); //释放内存 MemoryUtil.Free(pMultiFaceInfo); MemoryUtil.Free(pAgeInfo); return ageInfo; } else { return new ASF_AgeInfo(); } }
须要注意的是,要使用性别和年龄检测,必须在人脸检测SDK初始化的时候开启对应的功能,也就是说combinedMask值中必须包含 FFaceEngineMask.ASF_AGE |FaceEngineMask.ASF_GENDER;
上一步获取人脸框后,就能够调用人脸识别引擎获取人脸特征信息了,将照片信息传入人脸识别引擎,返回人脸模型信息
IntPtr pFaceModel = ExtractFeature(pEngine, imageInfo, multiFaceInfo, out singleFaceInfo);
咱们来看看 ExtractFeature 方法,这里的Demo写的比较复杂,并且几个方法都是同名的方法,咱们来详细分析一下
首先找到IntPtr ExtractFeature(IntPtr pEngine, Image image, out ASF_SingleFaceInfo singleFaceInfo)
方法.
因为人脸识别的第一步是先检测到人脸框的位置,所以这个方法就是对传入的图片进行一个预先处理的分析,而且调用了人脸检测的方法检测人脸。
.... 其它代码,主要是对传入图片进行分析,转换大小,若是为空或者图片不合法,直接返回空的特征。
ASF_MultiFaceInfo multiFaceInfo = DetectFace(pEngine, imageInfo);
singleFaceInfo = new ASF_SingleFaceInfo(); IntPtr pFaceModel = ExtractFeature(pEngine, imageInfo, multiFaceInfo, out singleFaceInfo); return pFaceModel;
咱们按照调用顺序看一下 IntPtr ExtractFeature(IntPtr pEngine, ImageInfo imageInfo, ASF_MultiFaceInfo multiFaceInfo, out ASF_SingleFaceInfo singleFaceInfo)
方法,
public static IntPtr ExtractFeature(IntPtr pEngine, ImageInfo imageInfo, ASF_MultiFaceInfo multiFaceInfo, out ASF_SingleFaceInfo singleFaceInfo) { /*定义要返回的单我的脸信息结构体*/ singleFaceInfo = new ASF_SingleFaceInfo(); /*若是没有人脸框,直接返回空特征*/ if (multiFaceInfo.faceRects == null) { ASF_FaceFeature emptyFeature = new ASF_FaceFeature(); IntPtr pEmptyFeature = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_FaceFeature>()); MemoryUtil.StructureToPtr(emptyFeature, pEmptyFeature); return pEmptyFeature; } /*将FaceDetect中的人脸框和人脸角度赋值到out对象*/ singleFaceInfo.faceRect = MemoryUtil.PtrToStructure<MRECT>(multiFaceInfo.faceRects); singleFaceInfo.faceOrient = MemoryUtil.PtrToStructure<int>(multiFaceInfo.faceOrients); /*将单我的脸对象转化成非托管结构体*/ IntPtr pSingleFaceInfo = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_SingleFaceInfo>()); MemoryUtil.StructureToPtr(singleFaceInfo, pSingleFaceInfo); IntPtr pFaceFeature = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_FaceFeature>()); /*调用人脸识别接口提取人脸特征*/ int retCode = ASFFunctions.ASFFaceFeatureExtract(pEngine, imageInfo.width, imageInfo.height, imageInfo.format, imageInfo.imgData, pSingleFaceInfo, pFaceFeature); Console.WriteLine("FR Extract Feature result:" + retCode); if (retCode != 0) { /*异常处理,注:因为使用了非托管对象,须要释放内存*/ MemoryUtil.Free(pSingleFaceInfo); MemoryUtil.Free(pFaceFeature); ASF_FaceFeature emptyFeature = new ASF_FaceFeature(); IntPtr pEmptyFeature = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_FaceFeature>()); MemoryUtil.StructureToPtr(emptyFeature, pEmptyFeature); return pEmptyFeature; } //处理返回值,这里又是一堆互操做访问 ASF_FaceFeature faceFeature = MemoryUtil.PtrToStructure<ASF_FaceFeature>(pFaceFeature); byte[] feature = new byte[faceFeature.featureSize]; MemoryUtil.Copy(faceFeature.feature, feature, 0, faceFeature.featureSize); ASF_FaceFeature localFeature = new ASF_FaceFeature(); localFeature.feature = MemoryUtil.Malloc(feature.Length); MemoryUtil.Copy(feature, 0, localFeature.feature, feature.Length); localFeature.featureSize = feature.Length; IntPtr pLocalFeature = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_FaceFeature>()); MemoryUtil.StructureToPtr(localFeature, pLocalFeature); //最后,别忘记释放内存 MemoryUtil.Free(pSingleFaceInfo); MemoryUtil.Free(pFaceFeature); /*返回提取到的人脸特征数据*/ return pLocalFeature; }
人脸检索时要先创建本地人脸素材库,上一步提取到的人脸特征是一串二进制的数据,实际使用时,咱们能够将特征存储到数据库或者本地文件中,Demo为了演示方便,直接放置在imagesFeatureList变量中.
在进行人脸检索时,获取到了待检索的人脸特征后,调用ASFFunctions.ASFFaceFeatureCompare
方法就能够完成检索了。
for (int i = 0; i < imagesFeatureList.Count; i++) { IntPtr feature = imagesFeatureList[i]; float similarity = 0f; int ret = ASFFunctions.ASFFaceFeatureCompare(pImageEngine, image1Feature, feature, ref similarity); //增长异常值处理 if(similarity.ToString().IndexOf("E") > -1) { similarity = 0f; } AppendText(string.Format("与{0}号比对结果:{1}\r\n", i, similarity)); imageList.Items[i].Text = string.Format("{0}号({1})", i, similarity); if (similarity > compareSimilarity) { compareSimilarity = similarity; compareNum = i; } }
ASFFunctions.ASFFaceFeatureCompare 方法实际上调用的是SDK的对应方法,其返回值 simiarity为类似度。 Demo将获取到的人脸和人脸库中全部人脸都进行了对比,找出最为接近的一个特征。 在实际应用中,若是找到了一个符合咱们置信度要求的特征,就能够直接退出循环了。
小提示:在实际应用中,若是人脸库的基数很大,能够开启多个FR实例进行检索,也能够启用人脸检测中的 性别 ,年龄数据缩小查询的范围
若是说从照片中检测人脸是人脸识别的基础,那么从视频中检测人脸则人脸识别最为实际的应用,实际的人脸实时检测系统都是基于视频检测的,活体检测也是基于视频模式下的人脸检测。 简单说来讲,从视频中检测人脸的方式就是从视频中抓取包含人脸的帧并分析识别的过程。在FaceForm的videoSource_Paint
方法中对这一过程进行了详细的描述。
从摄像头(RGB摄像头)中抓取一帧 调用适用于视频的人脸检测引擎,检测到人脸 根据返回的人脸位置画人脸框,利用上一帧的检测结果标识识别到的人脸信息 根据返回的人脸信息调用活体检测功能 判断是活体的状况下调用人脸识别引擎提取人脸特征 将人脸特征和人脸库的信息进行匹配 记录结果 等待捕获下一帧
第一个要注意的点是pVideoEngine
ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(pVideoEngine, bitmap);
这个pVideoEngine是首先,是视频模式下的人脸检测引擎,它是在InitEngines()方法中启用的
uint detectModeVideo = DetectionMode.ASF_DETECT_MODE_VIDEO; int combinedMaskVideo = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION; retCode = ASFFunctions.ASFInitEngine(detectModeVideo, videoDetectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMaskVideo, ref pVideoEngine);
这里调用的是pVideoEngine,和基于图片的pImageEngine使用的是不一样的引擎,两个引擎的区别是pVideoEngine在初始化的时候,使用的是视频模式,在视频检测的场景下,建议使用视频模式,在基于图片处理的状况下,建议使用图像模式。 这里由于 视频(摄像头)状况下 ,每秒产生数据有25-30帧,因为算法实现上的不一样,每秒只能作20次左右的图像模式的检测,因此视频状况下,用图像模式来作人脸检测是不适合的,由于算力不够;但视频模式能够每秒运行100次 ,并且在2.2版本,视频模式还增长了TrackID参数输出,更容易判断同一我的。而单张图片检测状况下,通常都采用图像模式来作人脸检测,图像模式的检测更为细致,并且对多人脸和大图片的支持较好,因为是单张检测,图片模式的性能也不存在问题,通常状况1s内作5-10张就知足产品要求了。
这里须要注意的一点,Demo中也指出来了,必定要保证同一时刻只检测一帧,不能同时检测多帧,以避免页面在显示时出现卡顿。另外,提取特征值和信息比对是比较耗时的,须要另外开线程以免主线程界面的卡顿。
目前经常使用的活体识别算法主要有 交互式 活体检测和非交互式的两种方式,咱们在登陆支付宝时候张张嘴,摇摇头都属于交互式,若是不须要其它动做,就属于非交互式,虹软的SDK针对识别摄像头的不一样,提供了基于RGB摄像头和基于红外深度摄像头的两种非交互算法。
只需单目RGB摄像头便可完成硬件搭设成本低,静默识别无需用户动做配合,只须要普通的摄像头就能够。人性化程度高,应用场景普遍
经过红外成像原理(屏幕没法成像、不一样材质反射率不一样等)以及深度学习算法,实现高鲁棒性的活体判断,静默式识别,可有效防护图片、视频、屏幕、面具等攻击,可知足双目人脸识别终端产品活体检测应用
有一个简单判断,若是你的摄像头只一个一个彩色镜头,没有IR红外镜头,那么就可使用RGB活体,若是你的是双目摄像头,而且一个是红外的,那么就可使用IR活体,从可信度上来讲,IR活体的可信度更多,但须要专用的设备。
虹软SDK在2.1以前的版本中,只提供了RGB活体检测功能,若是你须要使用IR活体检测功能,须要使用2.2版本的SDK。
虹软的活体检测内置于FR(面部识别)引擎之中,要使用活体检测功能,就必须先启用它,在Demo的InitEngines()方法中,咱们能够看到关于FR引擎初始化的方法.
根据使用摄像头不一样,启用不一样的活体检测引擎,下面这段代码启用了RGB模式的FR引擎
//RGB视频专用FR引擎 detectFaceMaxNum = 1; combinedMask = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_LIVENESS; retCode = ASFFunctions.ASFInitEngine(detectMode, imageDetectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMask, ref pVideoRGBImageEngine);
其中FaceEngineMask.ASF_LIVENESS
为普通的RGB活体,若是是红外双射,则为FaceEngineMask.ASF_IR_LIVENESS
进行人脸检测的最好时机是在分析人脸特征以前,抓取到人脸框以后,这个时候只须要调用faceUtil的LivenessInfo_RGB方法就能够。其返回的liveInfo中isLive是否为一判断是否活体。 LivenessInfo_RGB 在内部调用了SDK的ASFProcess 方法
retCode = ASFFunctions.ASFProcess(pEngine, imageInfo.width, imageInfo.height, imageInfo.format, imageInfo.imgData, pMultiFaceInfo, FaceEngineMask.ASF_LIVENESS);
if (retCode == 0) { //获取活体检测结果 IntPtr pLivenessInfo = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_LivenessInfo>()); retCode = ASFFunctions.ASFGetLivenessScore(pEngine, pLivenessInfo); Console.WriteLine("Get Liveness Result:" + retCode); ASF_LivenessInfo livenessInfo = MemoryUtil.PtrToStructure<ASF_LivenessInfo>(pLivenessInfo); //释放内存 MemoryUtil.Free(pMultiFaceInfo); MemoryUtil.Free(pLivenessInfo); return livenessInfo; }
其返回值livenessInfo 中的 isLive 定义了人脸活体的结果,当为1时为活体,为-1时为假体,程序判断为假体后,就再也不进行特征提取和匹配的动做了。
待补充。。。
在C#程序中,咱们常常和托管内存打交道,常用new 来新建对象。可是虹软提供的SDK是基于原生代码的,采用C语言编写,其在使用时须要分配和使用非托管内存,参数也使用C的结构体类型,为了方便使用,Demo程序提供了MemoryUtil类,它经过对Marshal类相应方法的封装,提供了直接调用C方法的便捷使用方式.
在使用Demo代码编写本身的程序时,要注意到有些FaceUtils的方法调用了Malloc方法分配了内存,但没有释放内存,而是在其它的方法中释放.有一个非托管内存管理原则很重要:调用Marshal.AllocHGlobal必须调用 Marshal.FreeHGlobal(ptr)来手动释放内存,即便调用GC.Collect();方法也没法释放,致使内存泄露。
Marshal 类是.net 互操做访问中须要使用到的最重要的一个类,它提供了一个方法集合,这些方法用于分配非托管内存、复制非托管内存块、将托管类型转换为非托管类型,此外还提供了在与非托管代码交互时使用的其余杂项方法。具体能够参考MSDN的文档 https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.interopservices.marshal?redirectedfrom=MSDN&view=netframework-4.8#methods
VS不一样版本对于DLL的放置有必定的要求,选择程序的CPU类型也会影响到最终DLL的使用,通常来讲,若是 使用的是32位程序,就放入x86文件夹,若是是x64就放64文件夹。
2.2版本的SDK在首次使用时,须要自动联网激活,所以,请在首次使用时,链接互联网。 若是在启动程序时报90118设备不匹配,一般是硬件信息发生了变化,这时候只须要删除SDK目录下面的ArcFace32.dat或者ArcFace64.dat文件,SDK在检测不到这个文件会自动联网激活。
这个固然是能够的,咱们能够根据官方的Demo按照本身的业务逻辑改形成asp.net 应用或者WPF应用。 Demo已经对大部的功能进行了封装,在了解业务逻辑以后,能够直接使用FaceUtil中的方法。不过当使用WPF或者asp.net时,可能会遇到堆栈内存不足,这是由于.net默认的堆栈大小为256K或者如下,而SDK须要使用512KB以上。只须要在新建线程时调整堆栈大小就能够解决了,参见下面的方法
new Thread(new ThreadStart(delegate { ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(pEngine, imageInfo); }), 1024 * 512).Start();
虹软开放平台论坛提供了官方的信息交流平台,能够访问https://ai.arcsoft.com.cn/bbs/index.php 了解更多信息,上面有技术人员蹲守解决你的问题,若是你有好的Demo须要分享给其它小伙伴儿,也能够在论坛中上传你的做品。