如何识别多个人脸

在开始之前,先解决一个疑问,这个SDK可以识别多个人脸吗。答案当然是可以的。在上一章节中我们实现了识别单个人脸的功能。

如果要识别多个人脸,需要进行下面的设置。

定义人脸的识别数目范围

int nMaxFaceNum = 50;/*定义人脸识别的数目,有效范围为1-50*/

修改人脸识别的程序。

在上一章节中, 我们的方法是只取到识别到的第一个人脸,因此我们只需要一个显示人脸的地方就可以了。要识别多个人脸,首先就是修改视图。

界面截图

然后,修改程序为循环。

//识别每一幅图像

for (int i = 0; i < faceRes.nFace; i++)

{

MRECT rect = (MRECT)Marshal.PtrToStructure(faceRes.rcFace + Marshal.SizeOf(typeof(MRECT)) * i, typeof(MRECT));

Image image = CutFace(bitmap, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);

if (i == 0)

{

/*第一个识别到的人脸保存在原位置*/

this.pictureBox2.Image = image;

this.pictureBox2.Tag = faceImageName[i];

}

else

{

/*后面识别到的人脸按顺序并排显示在下面,使用临时创建PictureBox控件的方式显示图片内容*/

PictureBox tempPicture = new PictureBox();

tempPicture.Width = 100;

tempPicture.Height = 120;

tempPicture.SizeMode = PictureBoxSizeMode.Zoom;

tempPicture.Location = new System.Drawing.Point(10 + ((i-1) % 7) * 120, 10 + ((i-1) / 7) * 120);

tempPicture.Image = image;

tempPicture.Tag =faceImageName[i];

this.panel1.Controls.Add(tempPicture);

}

一步步实现人脸识别

先来看一下我们这节的效果

效果图

本节我们主要讲解如何根据识别到的人脸信息提取人脸数据特征,并在此基础上讲解一下如何做人脸识别

在人脸识别领域,首先是检测是否有人脸,人脸的区域是哪里,然后对这个区域进行特征点提取,在提取结束后,告诉计算机,这个人脸是谁。

image

计算机把这些特征信息和人脸的名称保存下来,就形成了人脸库,在识别人脸时,计算机通过一定的算法,检索库中是否有匹配到的人脸结果,给出相似度数据。当人脸的相似度数据达到一定的数值时,就可以认为同一张人脸。

相似度通常是一个0-1的小数。一般来说,数值越大,表示两个人越相近。

注:不同人脸引擎的人脸相似度不具有可比性,例如,我们从Face ++ 拿到的同一个人的人脸相似度可能会在0.8-0.9,虹软的只能在0.6-0.8之间,这并不能说明Face ++ ,它们只是算法的标准不同,例如,虹软在不同人脸0.1-0.2的时候,Face++达到了0.3-0.5

人脸检测并建立人脸库的过程如下

建立人脸库

通过人脸检测或者人脸跟踪,获取到人脸信息并识别人脸的过程如下:

从人脸库中识别

本次教程我们以目录结构作为人脸的存档方式,每张人脸对应一张人脸标识和一个人脸特征。人脸标识和特征使用同一个文件名称来关联,例如人脸a.jpg的特征用a.dat来表示。

好,我们开始我们的课程

集成人脸识别SDK库

我们本次使用到的虹软的SDK包中,提供了人脸识别的库,它的名字叫face_recongnition.dll,我们找到它的SDK文档。

来建立各个结构体和API的C#映射。

首先是结构体

从本节开始,我们不再讲解原始SDK文档中的数据结构和C#数组结构如何映射的,也不再讲解P/Invoke的知识,如果需要了解相关知识,请参考我们上篇文档的相关内容。

AFR_FSDK_FaceInput

public struct AFR_FSDK_FaceInput

{

public MRECT rcFace;

public int lOrient;

}

这个结构体是FD识别的输出结构体,我们在上一章节标记人脸时使用了此结构体。

AFR_FSDK_FaceModel

public struct AFR_FSDK_FaceModel

{

public IntPtr pbFeature;

public int lFeatureSize;

}

这个结构体是人脸模型数据,也就是我们说的人脸特征。人脸识别就基于这个结构。

参数名

说明

pbFeature

提取到的脸部特征

lFeatureSize

特征信息长度

其中pbFeature是人脸数据,虹软当前版本的人脸数据为一个20K大小的二进制数组,在使用时,我们把它保存为byte[]数组。

AFR_FSDK_Version

public struct AFR_FSDK_Version

{

public int lCodebase;

public int lMajor;

public int lMinor;

public int lBuild;

public int lFeatureLevel;

public string Version;

public string BuildDate;

public string CopyRight;

}

定义识别方法类

我们将SDK中的对应方法提取到C#类中,和上面的章节保持一致,我们称之为AFRFunction。

public class AFRFunction

{

/**

*Init Engine

*/

[System.Runtime.InteropServices.DllImportAttribute("libarcsoft_fsdk_face_recognition.dll", EntryPoint = "AFR_FSDK_InitialEngine", CallingConvention = CallingConvention.Cdecl)]

public static extern int AFR_FSDK_InitialEngine(string AppId, string SDKKey, System.IntPtr pMem, int lMemSize, ref System.IntPtr phEngine);

/**

* 提取人脸特征值

*/

[System.Runtime.InteropServices.DllImportAttribute("libarcsoft_fsdk_face_recognition.dll", EntryPoint = "AFR_FSDK_ExtractFRFeature", CallingConvention = CallingConvention.Cdecl)]

public static extern int AFR_FSDK_ExtractFRFeature(System.IntPtr hEngine, System.IntPtr pInputImage, System.IntPtr pFaceRes, System.IntPtr pFaceModels);

/*

* 比较两个人脸特征值之间的相似度

**/

[System.Runtime.InteropServices.DllImportAttribute("libarcsoft_fsdk_face_recognition.dll", EntryPoint = "AFR_FSDK_FacePairMatching", CallingConvention = CallingConvention.Cdecl)]

public static extern int AFR_FSDK_FacePairMatching(System.IntPtr hEngine, ref System.IntPtr reffeature, ref System.IntPtr probefeature, ref float pfSimilScore);

/**

*销毁引擎

*/

[System.Runtime.InteropServices.DllImportAttribute("libarcsoft_fsdk_face_recognition.dll", EntryPoint = "AFR_FSDK_UninitialEngine", CallingConvention = CallingConvention.Cdecl)]

public static extern int AFR_FSDK_UninitialEngine(System.IntPtr hEngine);

/**

*获取人脸的版本号

*/

[System.Runtime.InteropServices.DllImportAttribute("libarcsoft_fsdk_face_recognition.dll", EntryPoint = "AFR_FSDK_GetVersion", CallingConvention = CallingConvention.Cdecl)]

public static extern System.IntPtr AFR_FSDK_GetVersion(System.IntPtr hEngine);

}

开始之前的准备

定义人脸库的位置

本次我们使用简单的基于目录存储人脸库

private String FaceLibraryPath = "G:\\Test\\";

定义人脸识别引擎的变量

IntPtr detectEngine = IntPtr.Zero;

//新增人脸识别引擎的定义

IntPtr recognizeEngine = IntPtr.Zero;

在构造函数中我们对人脸识别引擎进行初始化

int detectSize = 40 * 1024 * 1024;

int nScale = 50;

int nMaxFaceNum = 50;

IntPtr pMem = Marshal.AllocHGlobal(detectSize);

IntPtr pMemRecongnize = Marshal.AllocHGlobal(detectSize);

注意:detectSize为人脸识别的内存大小,一般来说,你可以根据你的应用程序的规模来设置一个适当的数值,数值过小会报内存不足的ERROR。

int retCode2 = AFR.AFRFunction.AFR_FSDK_InitialEngine(appId, sdkFRKey, pMemRecongnize, detectSize, ref recognizeEngine);

if (retCode2 != 0)

{

MessageBox.Show("引擎FR初始化失败:错误码为:" + retCode2);

this.Close();

}

这里需要注意FR Key,虹软这次开源了1:1和1:N的SDK,不同的SDK,其对应的KEY是不一样的。

提取人脸特征值

我们来提取人脸特征值。打开我们的checkAndMarkFace方法。

人脸特征值是一个二进制的byte数组,其内容对虹软来说是属于技术机密,里面保存了人脸的特征。这里的特征可以在人脸相似度比较时用到,人脸的特征包含了人脸的关键点信息。可惜的是,虹软这方面并没有开源。同样的,人脸的相似度比较算法也没有开源。不过不开源也有不开源的好处,至少我们用起来不用担心这里面的细节。

首先,我们定义一个变量数组,用于保存图片名称的数组。 这里我们简单的对每个识别到的人脸,用GUID命名。

在我们上一节的,输出识别到的人脸数据之前,我们增加一下我们的业务逻辑。找到下面的代码

if (faceRes.nFace > 0)

我们在后面增加定义

//定义用到保存识别到的图片的名称的数组

List faceImageName = new List(faceRes.nFace);

for (int i = 0; i < faceRes.nFace; i++)

{

faceImageName.Add(Guid.NewGuid().ToString());

}

在识别到的每个人脸以后,我们把识别到的人脸保存下来

Image image = CutFace(bitmap, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);

image.Save(FaceLibraryPath+faceImageName[i]+".jpg",ImageFormat.Jpeg);

如何进行人脸特征值的读取

人脸特征值依赖于人脸识别的结果,其原理是利用识别到的人脸区域信息,在原图中对人脸部分进行运算,输出人脸的特征数据。

通过前面的定义,可以知道人脸特征提取函数的需要的参数信息如下

recognizeEngine:人脸识别引擎

offInputPtr:输入的图像信息,和FD的信息相同。同为ASVLOFFSCREEN结构体,我们可以直接使用上一步已经定义好的这个变量。

faceInputPtr:人脸区域信息,包括人脸的角度信息,以及人脸的坐标范围,对应的参数类型为MRECT,也就是在FD中识别到的人脸的区域坐标,

输出参数为faceModel结构体。包括长度信息和人脸特征数组

我们来一步步解决。

定义faceInput结构体并指定它的引用互操作类型

AFR_FSDK_FaceInput faceinput = new AFR_FSDK_FaceInput();

faceinput.lOrient =(int)Marshal.PtrToStructure( faceRes.lfaceOrient,typeof(int));

MRECT rect = (MRECT)Marshal.PtrToStructure(faceRes.rcFace + Marshal.SizeOf(typeof(MRECT)) * i, typeof(MRECT));

faceinput.rcFace = rect;

IntPtr faceInputPtr = Marshal.AllocHGlobal(Marshal.SizeOf(faceinput));

Marshal.StructureToPtr(faceinput, faceInputPtr, false);

定义faceModel变量用于保存识别到的特征值信息

AFR_FSDK_FaceModel faceModel = new AFR_FSDK_FaceModel();

IntPtr faceModelPtr = Marshal.AllocHGlobal(Marshal.SizeOf(faceModel));

调用FR引擎进行特征信息提取

int ret = AFRFunction.AFR_FSDK_ExtractFRFeature(recognizeEngine, offInputPtr, faceInputPtr,

faceModelPtr);

如果ret=0,则提取成功,我们再调用Marshal的方法将对应的信息取出来

faceModel = (AFR_FSDK_FaceModel) Marshal.PtrToStructure(faceModelPtr, typeof (AFR_FSDK_FaceModel));

Marshal.FreeHGlobal(faceModelPtr);

byte[] featureContent = new byte[faceModel.lFeatureSize];

Marshal.Copy(faceModel.pbFeature, featureContent, 0, faceModel.lFeatureSize);

保存获取到的结果,为了后面的匹配方便,和图片命名保持一致

System.IO.File.WriteAllBytes(FaceLibraryPath+faceImageName[i]+".dat",featureContent);

通过图像库识别图像中的特征

现在我们要做的是人脸识别功能呢,我们想要的功能是,打开一张照片,如果里面有人脸,那么我们就识别这个人脸是否已经在我们的人脸库中出现过,如果已经出现 ,就显示人脸的图像编号。

依然打开项目,增加一个按钮。识别人脸,并增加一个pictureBox用于保存匹配到的人脸的对应的人脸信息。双击刚才新加的按钮进入事件处理代码编辑窗口。

为了不增加重新提取特征脸的工作量,我们将上一步获取到的特征脸重用。在上一步中,对识别到的人脸的第一个保存在了pictureBox中,并把相关的特征信息保存在对应命名的dat文件中。在保存时,使用

this.pictureBox2.Tag = faceImageName[i];

保存图像特征数据的文件名,因此在这里我们使用

string faceFeaturePath = pictureBox2.Tag as string;

获取图像文件名。

这里我们需要读文件,读取这个特征信息。

C# 读取二进制文件和写二进制文件都相当的方便,你可以使用C#的序列化操作把变量保为dat文件,然后使用反操作把文件重新读取以初始化对象。这里使用的是简单的二进制读取的方法,当然你也可以尝试序列化来完成这个操作。

byte[] sourceFeature = System.IO.File.ReadAllBytes(FaceLibraryPath + faceFeaturePath + ".dat");

接下来我们要使用人脸匹配的方法来进行匹配。这里使用的方法是AFR_FSDK_FacePairMatching

方法。再来看一下这个方法的定义

参数名称

输入输出

说明

hEngine

[in]

引擎 handle

reffeature

[in]

已有脸部特征信息

probefeature

[in]

被比较的脸部特征信息

pfSimilScore

[out]

相似程度数值

我们先来定义被比较的脸部信息。这里原来的参数名称有点拗口,我们使用localFaceModel来定义本地的

AFR_FSDK_FaceModel localFaceModels = new AFR_FSDK_FaceModel();

IntPtr sourceFeaturePtr = Marshal.AllocHGlobal(sourceFeature.Length);

Marshal.Copy(sourceFeature, 0, sourceFeaturePtr, sourceFeature.Length);

localFaceModels.lFeatureSize = sourceFeature.Length;

localFaceModels.pbFeature = sourceFeaturePtr;

由于使用了文件保存人脸特征信息,因此我们的人脸遍历算法就变得很简单了。我们这里使用1:1的方法。

我们直接使用存储的人脸信息来进行搜索,方法自然是先遍历读取所有特征数据,提取特征值并进行比较

foreach (var b in System.IO.Directory.GetFiles(FaceLibraryPath,"*.dat"))

{

byte[] libaryFeature = System.IO.File.ReadAllBytes(b);

float result=0f;

//TODO:构造AFR_FSDK_FaceModel,调用API,获取比较结果

if (result>0.7&&result<0.99)

{

// MessageBox.Show(b);

Image image = Image.FromFile(b.Replace(".dat",".jpg"));

this.pictureBox3.Image = new Bitmap(image);

MessageBox.Show(result.ToString());

break;

}

}

我们来完成TODO的部分

首先我们定义库Model和本地Model的结构体指针

定义库的指针

IntPtr libaryFeaturePtr = Marshal.AllocHGlobal(libaryFeature.Length);

Marshal.Copy(libaryFeature, 0, libaryFeaturePtr, libaryFeature.Length);

AFR_FSDK_FaceModel libraryFaceModel = new AFR_FSDK_FaceModel();

libraryFaceModel.lFeatureSize = libaryFeature.Length;

libraryFaceModel.pbFeature = libaryFeaturePtr;

IntPtr firstPtr = Marshal.AllocHGlobal(Marshal.SizeOf(localFaceModels));

Marshal.StructureToPtr(localFaceModels, firstPtr, false);

定义本地Model的指针

IntPtr firstPtr = Marshal.AllocHGlobal(Marshal.SizeOf(localFaceModels));

Marshal.StructureToPtr(localFaceModels, firstPtr, false);

调用方法输出匹配结果

int ret = AFRFunction.AFR_FSDK_FacePairMatching(recognizeEngine, firstPtr, secondPtr, ref result);

从这里可以看出,人脸识别并没有特别高深的地方,其基础理论依然是特征值匹配搜索的理论,

虽然这里面的难点是特征值的提取和匹配算法,但因为虹软已经免费给我们提供了对应的SDK,我们只需要调用相关的接口就可能了。如果要提高人脸匹配的速度,除了可以联系虹软寻找技术支持以外,也可以利用我们在其它算法方面的积累来尝试解决方案。

后记

本次我们学习了人脸特征的提取和人脸特征的保存,实际上,在业务系统中,人脸通常是保存在数据库中的,并且在匹配的时候,为了性能考虑,更多的是把特征保存在内存中,20K的特征值如果在2GB的业务系统中,可以很轻松的保存10W+的特征信息。人脸检测和识别是CPU密集型和内存密集型的应用,保持良好的计算机配置有助于提高识别的性能,离线SDK的良好扩展性也为我们提高系统的性能提供了可行性。

这两章节都是从静态图像出发的人脸检测和人脸识别,从下一章节开始,我们将先从视频的人脸识别讲起。然后结合摄像头的实时图像采集,我们来讲解一下简单的人脸识别门禁系统的实现。请继续关注。

php识别人脸并提取特征值,C#人脸识别入门篇--提取人脸特征值及人脸识别相关推荐

  1. C#人脸识别入门篇--提取人脸特征值及人脸识别

    转自:http://blog.csdn.net/feishixin/article/details/77397027 如何识别多个人脸 在开始之前,先解决一个疑问,这个SDK可以识别多个人脸吗.答案当 ...

  2. python opencv 人脸比对_Python3.5+openCv在Windows下利用LBP算法进行人脸识别并匹配

    之前的人脸识别匹配需要大量图片进行建模,然后通过概率匹配,结果不是很准确,同时也不符合一般需求.一般需求是人员通过摄像头拍摄一张照片,然后将照片保存进行命名,之后如果再次通过摄像头进行验证时候,通过算 ...

  3. pytorch实现人脸识别_一步一步带你完成深度学习与对象检测之人脸识别

    前期文章我们分享了opencv的人脸检测 人工智能-OpenCV+Python实现人脸识别 以及dlib的人脸检测与人脸识别 人工智能-Dlib+Python实现人脸识别 通过往期的分享,我们了解到人 ...

  4. CVPR 2021 | 腾讯优图20篇论文入选,含人脸识别、时序动作定位、视频动作分割等领域...

    编辑丨腾讯优图AI开放平台 计算机视觉世界三大顶会之一的CVPR 2021论文接收结果出炉!本次大会收到来自全球共7015篇有效投稿,最终有1663篇突出重围被录取,录用率约为23.7%.本次,腾讯优 ...

  5. 初学入门 | 一种快速准确的人脸检测、识别和验证系统

    即将迎来了2019世界人工智能大会,相信这个会议又一次推动人工智能的发展,有兴趣的同学可以去参加感受一下人工智能的热度,绝不会低于这个夏天的高温. 今天"计算机视觉战队"为大家分析 ...

  6. android 人脸识别_小模型,高精度!小视科技推出极致轻量型人脸识别SDK

    AI 落地为王时代的到来,使得人脸识别的精度和速度备受人们关注.近年来,随着模型压缩.量化技术的进步,模型运行速度也越来越快.对于一些特定的人脸检测任务,轻量型的人脸识别SDK即可满足需求. ▲图源网 ...

  7. 基于人脸识别的课堂签到管理系统(三)---实时时间显示以及百度AI人脸识别

    基于人脸识别的课堂签到管理系统(三)---实时时间显示以及百度AI人脸识别 一.前言概述 二.实时时间显示 三.百度AI人脸识别 3.1 获取access_token 3.2 发送请求,通过网络请求方 ...

  8. C#人脸识别入门篇(Step by step 人脸识别)

    C#人脸识别入门篇-Step ByStep人脸识别 引言 如今,基于人脸的技术和话题可以说是炙手可热,基于大数据和人工智能的人脸识别更是突破了我们的想象力的极限,如果应用中不能集成人脸识别,那就太跟不 ...

  9. 双胞胎能互相人脸识别解锁支付软件?那是你不了解点面科技的虹膜识别技术

    双胞胎能互相人脸识别解锁支付软件?那是你不了解点面科技的虹膜识别技术 记得有一档综艺节目上,一对双胞胎姐妹花曾经说过一个段子.姐姐吐槽说想要体会独立的感觉,特别是经济独立,因为妹妹天天花她的钱.而妹妹 ...

最新文章

  1. John the Ripper
  2. 网络编程学习笔记(使用select函数的TCP和UDP回射服务器程序)
  3. Codeforces Beta Round #7 C. Line (扩展欧几里德)
  4. tpm php,TPM系列
  5. 下载丨8月数据库技术通讯:不合理业务设计导致CPU飙升
  6. STM32F103单片机modbus通信示例
  7. Spring boot 出现的时间
  8. SAP License:关于未分摊差异的几种处理办法
  9. Azure角色管理技巧和工具
  10. 5. 卷2(进程间通信)---Posiz 消息队列
  11. 公募基金行业与146家公募基金管理机构手册
  12. PXC 57 二进制安装
  13. 【分享】“飞鹅打印机“ 在集简云平台集成应用的常见问题与解决方案
  14. 【未解决】【Linux环境】IDEA下搜狗输入法无法光标跟随
  15. linux下这输入法切换大小写,Life With Arch:让Capslock成为输入法切换快捷键
  16. 一个STAF的RC21的问题的解决和思考
  17. Vivo升级android版本,vivo手机升级Android Q教程:很简单,X27与NEX都支持
  18. Java 创建pdf
  19. golang的运维开发
  20. python爬虫一:必应图片(从网页源代码中找出图片链接然后下载)

热门文章

  1. 德系车,美系车,日系车都有什么品牌
  2. 计算机考试 虚拟机,虚拟机在全国计算机等级考试机试中的应用.doc
  3. scp(安全拷贝)和rsync(增量复制)
  4. C语言学习之路--C语言中的格式输出
  5. 关于Typora的下载和使用
  6. JavaScript循环语句的性能问题
  7. R语言对豆瓣电影top250进行分析
  8. 使用 PTC Mathcad Prime 软件绘制电源谐振Q值曲线
  9. latex:解决无法找到.sty的问题
  10. 深度学习中的归一化方法简介(BN、LN、IN、GN)