#最近实践周做了个活体检测和人脸识别
##1.1 目的
人脸识别系统以人脸识别技术为核心,是一项新兴的生物识别技术,是当今国际科技领域攻关的高精尖技术。它广泛采用区域特征分析算法,融合了计算机图像处理技术与生物统计学原理于一体,利用计算机图像处理技术从视频中提取人像特征点,利用生物统计学的原理进行分析建立数学模型,具有广阔的发展前景。在本次设计过程中我们详细的了解了人脸识别中活体检测的内容和应用。
##2. 运用及功能
2.1人脸识别技术与应用
人脸识别,是通过采集人脸图像,获取其脸部特征信息后,利用匹配识别技术与数
据库中的人脸数据信息匹配比较,从而实现识别身份的生物特征识别技术[14]。先通过摄像机等设备获取人脸图像,再输入到计算机、数字信号处理、嵌入式等计算分析平台,接着利用数字图像处理技术进行图像预处理,利用人脸检测算法检测图像中是否有人脸的存在,如有,则进行特征提取,最后匹配识别等一系列相关技术的处理。因此,人脸识别系统主要由五部分组成:图像采集,人脸检测、图像预处理、人脸特征提取以及匹配识别。
。
2.2.2功能实现
这里是引用
使用百度AI开放平台,它免费开放一定并发量的该场景活体检测 API:
https://ai.baidu.com/tech/face/faceliveness
第一步,申请百度应用
点击“立即使用”,登录后“创建应用”,可以得到 API Key 与 Secret Key 等信息。
第二步,使用 API 进行活体检测
这里的场景比较简单,摄像头获取的影像可以保存为图片,则功能接口可以这样定义:给定图片(这里使用URL),判断其活体影像的概率。根据百度建议,概率设置为 99.5%,即达到此值或以上认为活体检测通过。
(1)获取 accessToken
accessToken 有效期为 30 天,因此,可以缓存起来使用。此为示例,时长又足够长,所以未加刷新机制。代码如下,其中,clientId 为百度应用中的 API Key,clientSecret 为百度应用中的 Secret Key。
public static class AccessToken
{
// 有效期30天,缓存获取的 access token
public static String TOKEN = null;
// 百度云中开通对应服务应用的 API Key
private static String clientId = “API Key”;
// 百度云中开通对应服务应用的 Secret Key
private static String clientSecret = “Secret Key”;
public static String getAccessToken()
{
if (String.IsNullOrEmpty(TOKEN))
{
String authHost = “https://aip.baidubce.com/oauth/2.0/token”;
HttpClient client = new HttpClient();
List<KeyValuePair<String, String>> paraList = new List<KeyValuePair<string, string>>();
paraList.Add(new KeyValuePair<string, string>(“grant_type”, “client_credentials”));
paraList.Add(new KeyValuePair<string, string>(“client_id”, clientId));
paraList.Add(new KeyValuePair<string, string>(“client_secret”, clientSecret));
HttpResponseMessage response = client.PostAsync(authHost, new FormUrlEncodedContent(paraList)).Result;
String result = response.Content.ReadAsStringAsync().Result;
JObject jr = JObject.Parse(result);
TOKEN = jr.Value(“access_token”);
}
return TOKEN;
}
}
(2)调用 API 取得活体概率
API 的返回结果为 JSON,其中包括了活体概率,这里,方法直接返回 API 的 JSON 结果。
public class FaceLivenessHelper
{
// 在线活体检测
public static string FaceVerify(string imgUrl)
{
string token = AccessToken.getAccessToken();
string host = “https://aip.baidubce.com/rest/2.0/face/v3/faceverify?access_token=” + token;
Encoding encoding = Encoding.Default;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(host);
request.Method = “post”;
request.KeepAlive = true;
// String str = “[{“image”:“sfasq35sadvsvqwr5q…”,“image_type”:“BASE64”,“face_field”:“age,beauty,expression”}]”;
String str = “[{“image”:”" + imgUrl + “”,“image_type”:“URL”,“face_field”:“age,beauty,expression”}]";
byte[] buffer = encoding.GetBytes(str);
request.ContentLength = buffer.Length;
request.GetRequestStream().Write(buffer, 0, buffer.Length);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.Default);
string result = reader.ReadToEnd();
Console.WriteLine(“在线活体检测:”);
Console.WriteLine(result);
return result;
}
}
详细 API 文档见此:https://ai.baidu.com/docs#/Face-Liveness-V3/top
结果中:face_liveness 即表示“活体分数值”。
(3)应用
API 的调用结果中,error_code 为 0 时表示执行成功,此时,会有 result 属性表示计算的相关值,从中取出 face_liveness 即可,其值为 0 ~ 1之间。
string imgUrl = “------”;
string result = FaceLivenessHelper.FaceVerify(imgUrl);
JObject jresult = JObject.Parse(result);
JObject lvresult = jresult.Value(“result”);
// error_code 为 0 时表示执行成功,其它表示失败
if (jresult.Value(“error_code”) == 0)
{
double face_liveness = lvresult.Value(“face_liveness”);
// 活体率达到要求
if (face_liveness >= 0.995)
{
// 通过检测
}
(4)活体阈值参考
检测的图片为二次采集,即通过相机当场拍摄,确保时间及操作条件的约束;
SDK输出的多帧情况,只要这些帧中,任何一张通过了阈值,即可判断为活体,建议可用三帧情况;
推荐分值采用99.5%
关于活体检测faceliveness的判断阈值选择,可参考以下数值信息:
拒绝率(TRR) 误拒率(FRR) 通过率(TAR) 阈值(Threshold)
0.90325733 0.1% 99.9% 0.022403
0.96254072 0.5% 99.5% 0.393241(推荐)
0.97557003 1% 99% 0.649192
0.98990228 2% 98% 0.933801
0.99446254 3% 97% 0.973637
0.99641694 4% 96% 0.988479
0.99739414 5% 95% 0.994058
关于以上数值的概念介绍:
拒绝率(TRR):如99%,代表100次作弊假体攻击,会有99次被拒绝。
误拒率(FRR):如0.5%,指1000次真人请求,会有5次因为活体分数低于阈值被错误拒绝。
通过率(TAR):如99%,指100次真人请求,会有99次因为活体分数高于阈值而通过。
阈值(Threshold):高于此数值,则可判断为活体。
质量检测参考
指标 字段与解释 推荐数值界限
遮挡范围 occlusion,取值范围[0~1],0为无遮挡,1是完全遮挡
含有多个具体子字段,表示脸部多个部位
通常用作判断头发、墨镜、口罩等遮挡 left_eye : 0.6, #左眼被遮挡的阈值
right_eye : 0.6, #右眼被遮挡的阈值
nose : 0.7, #鼻子被遮挡的阈值
mouth : 0.7, #嘴巴被遮挡的阈值
left_check : 0.8, #左脸颊被遮挡的阈值
right_check : 0.8, #右脸颊被遮挡的阈值
chin_contour : 0.6, #下巴被遮挡阈值
模糊度范围 blur,取值范围[0~1],0是最清晰,1是最模糊 小于0.7
光照范围 illumination,取值范围[0~255]
脸部光照的灰度值,0表示光照不好
以及对应客户端SDK中,YUV的Y分量 大于40
姿态角度 Pitch:三维旋转之俯仰角度[-90(上), 90(下)]
Roll:平面内旋转角[-180(逆时针), 180(顺时针)]
Yaw:三维旋转之左右旋转角[-90(左), 90(右)] 分别小于20度
人脸完整度 completeness(0或1),0为人脸溢出图像边界,1为人脸都在图像边界内 视业务逻辑判断
人脸大小 人脸部分的大小
建议长宽像素值范围:8080~200200 人脸部分不小于100*100像素
人脸空间姿态角参考
姿态角分为Pitch、Roll、Yaw,用于表示人脸在空间三维坐标系内的角度,常用于判断识别角度的界限值。
各角度阈值如下:
Pitch:三维旋转之俯仰角度,范围:[-90(上), 90(下)],推荐俯仰角绝对值不大于20度;
Roll:平面内旋转角,范围:[-180(逆时针), 180(顺时针)],推荐旋转角绝对值不大于20度;
Yaw:三维旋转之左右旋转角,范围:[-90(左), 90(右)],推荐旋转角绝对值不大于20度;
各角度范围示意图如下:
从姿态角度来看,这三个值的绝对值越小越好,这样代表人脸足够正视前方,最利于实际注册/识别使用。
##程序代码
namespace FaceCheck
{
public partial class Form1 : Form
{
//static bool index = false;//用作切换图片的标志,false切换到线下图片,true切换到线上图片
/// <summary>
/// 图片转换为Base64
/// </summary>
/// <param name="image"></param>
/// <returns></returns>
private string ImgToBase64String(string image)
{
try
{
Bitmap bmp = new Bitmap(image, true);
MemoryStream ms = new MemoryStream();
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
byte[] arr = new byte[ms.Length];
ms.Position = 0;
ms.Read(arr, 0, (int)ms.Length);
ms.Close();
return Convert.ToBase64String(arr);
}
catch (Exception)
{
return null;
}
}
// 在线活体检测
public static string FaceVerify(string imgUrl)
{
string token = AccessToken.getAccessToken();
string host = "https://aip.baidubce.com/rest/2.0/face/v3/faceverify?access_token=" + token;
Encoding encoding = Encoding.Default;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(host);
request.Method = "post";
request.KeepAlive = true;
///<summary>
///图片换为BASE64格式
/// </summary>
String str = "[{\"image\":\"" + imgUrl + "\",\"image_type\":\"BASE64\",\"face_field\":\"age,beauty,expression\"}]";
///<summary>
///图片换为URL统一资源定位符
/// </summary>
// String str = "[{\"image\":\"" + imgUrl + "\",\"image_type\":\"URL\",\"face_field\":\"age,beauty,expression\"}]";
byte[] buffer = encoding.GetBytes(str);
request.ContentLength = buffer.Length;
request.GetRequestStream().Write(buffer, 0, buffer.Length);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.Default);
string result = reader.ReadToEnd();
Console.WriteLine("在线活体检测:");
Console.WriteLine(result);
return result;
//百度示例
// String str = "[{\"image\":\"sfasq35sadvsvqwr5q...\",\"image_type\":\"BASE64\",\"face_field\":\"age,beauty,expression\"},{\"image\":\"http://xxx.baidu.com/image1.png\",\"image_type\":\"URL\",\"face_field\":\"age,beauty\"}]";
}
public Form1()
{
InitializeComponent();
}
private void button2_Click(object sender, EventArgs e)
{
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
//PictureBox控件显示图片
pictureBox1.Load(openFileDialog1.FileName);
}
}
private void button1_Click(object sender, EventArgs e)
{
if (radioButton_local.Checked)
{
string imgUrl = ImgToBase64String(openFileDialog1.FileName.ToString());
string result = FaceVerify(imgUrl);
JObject jresult = JObject.Parse(result);
JObject lvresult = jresult.Value<JObject>("result");
// JObject flresult = jresult.Value<JObject>("face_list");
// error_code 为 0 时表示执行成功,其它表示失败
if (jresult.Value<int>("error_code") == 0)
{
double face_liveness = lvresult.Value<double>("face_liveness");
string face_livenessx = lvresult.Value<string>("face_liveness");
// string age = lvresult.Value<string>("face_list".Contains("age"));
// double age = lvresult.Value<double>("age");
//string beauty = lvresult.Value<string>("face_list");
face_livenesstest.Text = "活体分数值:" + face_livenessx;
// agetest.Text = "年龄:"+age.Tostring();
// beautytest.Text = "美丑打分:"+beauty;
richTextBox2.Text = jresult.ToString();
// 活体率达到要求
if (face_liveness >= 0.995)
{
// 通过检测
label1.Text = "通过!!!";
}
else
label1.Text = "不通过!!!";
}
}
if (radioButton_online.Checked)
{
//string imgUrl = "http://photocdn.sohu.com/20110926/Img320590872.jpg";
string imgUrl = textBox1.Text;
string result = FaceVerify(imgUrl);
JObject jresult = JObject.Parse(result);
JObject lvresult = jresult.Value<JObject>("result");
// error_code 为 0 时表示执行成功,其它表示失败
if (jresult.Value<int>("error_code") == 0)
{
double face_liveness = lvresult.Value<double>("face_liveness");
string face_livenessx = lvresult.Value<string>("face_liveness");
face_livenesstest.Text = face_livenessx;
richTextBox2.Text = jresult.ToString();
// 活体率达到要求
if (face_liveness >= 0.995)
{
// 通过检测
label1.Text = "通过!!!";
}
else
label1.Text = "不通过!!!";
}
}
}
private void richTextBox2_TextChanged(object sender, EventArgs e)
{
}
/// <summary>
/// 质量检测(可选)活体检测(可选)公安验证(必选)
/// </summary>
// public void PersonVerifyDemo()
// {
// var image = "取决于image_type参数,传入BASE64字符串或URL字符串或FACE_TOKEN字符串";
// var imageType = "BASE64";
// var idCardNumber = "110233112299822211";
// var name = "张三";
// // 调用身份验证,可能会抛出网络等异常,请使用try/catch捕获
// var result = client.PersonVerify(image, imageType, idCardNumber, name);
// Console.WriteLine(result);
// // 如果有可选参数
// var options = new Dictionary<string, object>{
// {"quality_control", "NORMAL"},
// {"liveness_control", "LOW"}
// };
// // 带参数调用身份验证
// result = client.PersonVerify(image, imageType, idCardNumber, name, options);
// Console.WriteLine(result);
// }
// public string ReadImg(string img)
// {
// return Convert.ToBase64String(File.ReadAllBytes(img));
// }
// /// <summary>
// /// 人脸基础信息,人脸质量检测,基于图片的活体检测
// /// </summary>
// public void Demo()
// {
// var faces = new JArray
//{
// new JObject
// {
// {"image", ReadImg("/ym1.jpeg")},
// {"image_type", "BASE64"},
// {"face_field", "age,beauty"},
// },
// new JObject
// {
// {"image", ReadImg("/ym2.jpeg")},
// {"image_type", "BASE64"}
// }
//};
// var result = client.Faceverify(faces);
// Console.WriteLine(result);
// }
}
}
后期再放个成效的视频
全片(高清)