代码改变世界

人脸识别Demo解析C#

2019-08-19 17:53  码仔很忙  阅读(7003)  评论(1编辑  收藏  举报

 

概述

不管你注意到没有,人脸识别已经走进了生活的角角落落,钉钉已经支持人脸打卡,火车站实名认证已经增加了人脸自助验证通道,更别提各个城市建设的『智能城市』和智慧大脑了。在人脸识别业界,通常由人脸识别提供商和人脸识别应用接入方组成,从头到尾研发人脸识别技术需要极强的专用技术知识和数学算法功底,对于大多数企业来说,选择人工智能AI公司现成的人脸识别技术引擎是一个比较适合的解决方法。虹软公司在2017年开放了人脸识别平台1.0版本,经过三年的技术迭代和更新,目前已经推出了2.2版本,主打离线,免费,适合场景比较广泛。为了方便开发者接入,虹软官方提供了各个语言版本的Demo程序,由于虹软并没有提供C#版本的SDK,因此,他们提供的C#版本的SDK就更有参考价值了。

虹软Demo的下载地址如下:https://github.com/ArcsoftEscErd/ArcfaceDemo_CSharp_2.2 在开始之前,建议你下载它。

什么是人脸识别

人脸识别,是基于人的脸部特征信息进行身份识别的一种生物识别技术。用摄像机或摄像头采集含有人脸的图像或视频流,并自动在图像中检测和跟踪人脸,进而对检测到的人脸进行脸部识别的一系列相关技术,通常也叫做人像识别、面部识别。 而人脸识别的过程可以简的概括为:检测人脸框->提取人脸特征信息->人脸库检索匹配信息三个过程。

人脸识别的应用场景

人脸识别主要用于身份识别。由于视频监控正在快速普及,众多的视频监控应用迫切需要一种远距离、用户非配合状态下的快速身份识别技术,以求远距离快速确认人员身份,实现智能预警。人脸识别技术无疑是最佳的选择,采用快速人脸检测技术可以从监控视频图象中实时查找人脸,并与人脸数据库进行实时比对,从而实现快速身份识别。 在现实生活中,从最常见的人脸门禁,到实名制安检,景区验票,公司或者学校的人脸签到,无人超市等都有广泛的应用。

什么是活体检测

活体检测检测顾名思义就是通过识别活体上的生理信息,来区分用照片、硅胶、塑料等非生命物质伪造的生物特征。人脸识别应用中的活体检测技术用来判断系统采集到的人脸图像是否来源于真实的人脸,以防止照片、视频等伪造的人脸图像被输入到系统造成误判,活体检测在无人值守场景下的人脸识别商业应有中显得至关重要。

虹软人脸识别SDK

目前市面上有很多人脸识别技术方案,从是否需要使用时联网可以分为在线和离线,从接入方式上可以分为本地识别和服务器大数据识别等方式,虹软提供的是基于本地算法特征的离线识别SDK,其基础算法使用C编写,提供全平台的离线支持.

虹软视觉视觉开放平台

虹软人脸识别SDK通过视觉开放平台提供,包含人脸识别场景中最常用到的功能组件,例如:人脸检测,人脸识别,年龄性别检测,活体检测 等,其中人脸检测针对静态和动态检测场景分别进行了算法优化,从中派生的性别和年龄检测扩充了人脸识别的使用场景,活体检测组件可以有效的保证人脸识别应用的安全性。 访问https://ai.arcsoft.com.cn/third/mobile.html?cnblogs 按照网站的提示,可以注册用户和下载SDK包。

虹软的人脸识别Demo简介

虹软的SDK,和大量基于Restful风格的接口不同,没有使用普通的基于HTTP的方式,也并没有提供C#语言的SDK包,仅提供了C语言的SDK,对于C#接入有一定的困难,在发布之初有不少大神自行编写接入Demo程序,后来,虹软官方也出了Demo程序,从2018年1月的第一个版本到现在随着SDK更新的2.2版本,代码结构和注释更为清晰。

Demo效果展示

Demo是标准的C# WINFORM工程样式,通过GitHub,下载下来之后,可以直接使用VS打开. 打开之后,有一个readme.md文件,十分重要,在开始之前,请务必仔细看一下。这里把要点给大家总结一下。

  1. 注册并登录虹软开发者账号,下载Win32/Win64的ArcFace的SDK,建议下载2.2版本的。
  2. 将下载时生成的APPID和KEY填放app.config文件中的对应位置。
  3. 将下载的文件解压,根据本平台将dll解压到对应平台的目录下面 如果上述几步都OK,程序可以正常运行,如果中间有问题,可以参考readme中的内容进行排查。

一般OK之后,系统弹出正常运行的窗口,网上找几张明星照片进行注册,对比。 如下图所示:

可以看到,虹软Demo已经可以正确的识别人脸信息。

Demo中还提供了活体检测功能,如果你的机器没有摄像头,可以外插一个USB的摄像头,点击启用摄像头,打开它。

如果我们用自己的人脸识别,会显示RGB活体,如果是用照片或者视频尝试识别,会显示『RGB假体』

人脸识别Demo代码解析

接下来进入正题,让我们打开工程视图,从代码角度解析一下虹软人脸识别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活体

只需单目RGB摄像头即可完成硬件搭设成本低,静默识别无需用户动作配合,只需要普通的摄像头就可以。人性化程度高,应用场景广泛

IR活体

通过红外成像原理(屏幕无法成像、不同材质反射率不同等)以及深度学习算法,实现高鲁棒性的活体判断,静默式识别,可有效防御图片、视频、屏幕、面具等攻击,可满足双目人脸识别终端产品活体检测应用

有一个简单判断,如果你的摄像头只一个一个彩色镜头,没有IR红外镜头,那么就可以使用RGB活体,如果你的是双目摄像头,并且一个是红外的,那么就可以使用IR活体,从可信度上来说,IR活体的可信度更多,但需要专用的设备。

虹软SDK在2.1之前的版本中,只提供了RGB活体检测功能,如果你需要使用IR活体检测功能,需要使用2.2版本的SDK

RGB活体接口解析

虹软的活体检测内置于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时为假体,程序判断为假体后,就不再进行特征提取和匹配的动作了。

IR活体接口解析

待补充。。。

问题及解决思路分享

非托管内存的管理与内存溢出

在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

找不到dll

VS不同版本对于DLL的放置有一定的要求,选择程序的CPU类型也会影响到最终DLL的使用,一般来说,如果 使用的是32位程序,就放入x86文件夹,如果是x64就放64文件夹。

激活失败

2.2版本的SDK在首次使用时,需要自动联网激活,因此,请在首次使用时,连接互联网。 如果在启动程序时报90118设备不匹配,通常是硬件信息发生了变化,这时候只需要删除SDK目录下面的ArcFace32.dat或者ArcFace64.dat文件,SDK在检测不到这个文件会自动联网激活。

Demo中的代码可以在WPF或者Asp.net中使用吗

这个当然是可以的,我们可以根据官方的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需要分享给其它小伙伴儿,也可以在论坛中上传你的作品。