博客主页:https://xiaoy.blog.csdn.net
本文由 呆呆敲代码的小Y 原创,首发于 CSDN
学习专栏推荐:Unity系统学习专栏
游戏制作专栏推荐:游戏制作
Unity实战100例专栏推荐:Unity 实战100例 教程
欢迎点赞 收藏 ⭐留言 如有错误敬请指正!
未来很长,值得我们全力奔赴更美好的生活✨
------------------❤️分割线❤️-------------------------
Unity实现简单地人脸识别
功能。百度AI开放平台
、科大讯飞人脸识别
、支付宝蚂蚁人脸认证
、虹软人脸识别算法
等等。文章部分文档链接如下:
既然选择了使用 百度AI开放平台的SDK,那我们就需要去官网下载SDK啦!
首先我们需要前往 百度AI开放平台,要登录百度账号。
如果之前有账号则直接登录,没有的话点击去注册一个就好了,这里不多赘述。
然后点击 开放能力
-> 人脸与人体
-> 人脸搜索
点击立即使用会跳转到控制台界面,然后点击 公有云服务 -> 应用列表。
若此时没有应用,则进行创建。
找不到的小伙伴可以直接点击该链接,看看能不能直达创建应用的网址:https://console.bce.baidu.com/ai/#/ai/face/app/create
点击完之后该应用就创建完毕了,APPID
和 API KEY
以及 Secret Key
要保存好,后面在Unity中也会用到!
接下来在 百度AI开放平台 的首页点击 开发与教学 -> SDK下载
选择 人脸识别 -> C# HTTP SDK
点击下载
如果改变了不跟我文中位置一致,大家可以在首页自己找找看,只要找到SDK下载的地方就行,一般不会太隐蔽。
也可以点击SDK下载地址直接转入:https://ai.baidu.com/sdk#bfr
下载SDK后,解压到本地文件夹中。
之后打开 net35
文件夹,其中 AipSdk.dll
和 Newtonsoft.Json.dll
这两个dll文件,后边会放到Unity3d工程中使用。
如果想使用 人脸离线识别 功能则需要单独去申请。本文教程暂时没有用到,不感兴趣的话可以跳过此内容。
我这里选择了 Android版
,最后打算使用一个Android设备进行测试。
大家可以根据自己的需求选择,操作步骤整体差异不大。
可以看到有一个 立即使用 和 技术文档 。
点击立即使用后会进入下一个页面,让我们填写一些信息 申请SDK。
那我们就简单填写一下就好了,填写完之后需要等一段时间审核,我这里第一次的时候等了大概十几分钟就申请好了。
新建一个Unity打开后 打开 File>BuildSettings -> PlayerSettings -> OtherSettings
改变Api Compatibility Level
为 .NET2.0
,若本来就是则不需要修改。
在Assets
下新建一个Plugins
文件夹,注意不要拼错单词哦~
将上面讲到的两个文件AipSdk.dll
和Newtonsoft.Json.dll
放入该文件夹。
打开人脸识别的C#官方文档:https://ai.baidu.com/ai-doc/FACE/Ck37c1ri0
文档中的步骤挺详细,可以结合文章与文档共同参考 ~
定义一个客户端,此时要用到之前创建的应用的 APPID
、 APIKey
和 SecretKey
代码如下:
using Baidu.Aip.Face;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FaceRecognitionDemo: MonoBehaviour
{
// 设置APPID/AK/SK
public string APP_ID = "你的 App ID";
public string API_KEY = "你的 Api Key";
public string SECRET_KEY = "你的 Secret Key";
Face client;
void Start()
{
client = new Face(API_KEY, SECRET_KEY);
client.Timeout = 60000; // 修改超时时间
}
}
接下来要进行人脸注册,简单意思就是我们要将几张某个人的照片传到人脸库中,用于下面的人脸识别时使用。
我们先注册几个人脸,官网教程中各个参数说明很清楚,想要了解更多内容也可以在官网仔细查看,我们一步一步来。
{
"face_token": "2fa64a88a9d5118916f9a303782a97d3",
"location": {
"left": 117,
"top": 131,
"width": 172,
"height": 170,
"rotation": 4
}
}
注册人脸代码如下:
// 人脸注册
public void SignUpFace(string image, string imageType, string groupId, string userId)
{
var options = new Dictionary<string, object>{
{"user_info", "user's info"},
{"quality_control", "NORMAL"},
{"liveness_control", "LOW"}
};
// 带参数调用人脸注册
var result = client.UserAdd(image, imageType, groupId, userId, options);
Debug.Log(result);
}
上面说的人脸注册 有两种方法可以实现,一是可以选择在云端手动创建组然后添加图片,二是可以通过URL方式在Unity中上传,下面都有介绍操作方式。
1.在控制台手动创建组并添加用户
手动创建方法很简单,直接点击新建组然后创建用户,将图片添加上即可!
2.使用脚本代码在Unity中上传人脸库
下面再来简单说一下在Unity中通过url上传的方法。
在Unity项目的Assets下 创建文件夹 StreamingAssets / FaceUpLoad
然后在网上找几张同一个人的图片,用作人脸识别测试时候使用,将下载的图片放到我们创建的文件夹中。
代码如下:
//获取图片base64字符串,由于QPS限制,此处采用协程降低注册频率
IEnumerator IEGetStringBase64()
{
//获取到每一张图片的路径
string[] picsPathArr = Directory.GetFiles(Application.streamingAssetsPath + "/**FaceDetect**/");
//循环获取每张图片的base64字符串
for (int i = 0; i < picsPathArr.Length; i++)
{
//unity会自动生成.meta文件,过滤掉
if (picsPathArr[i].Contains("meta")) continue;
//读取
FileInfo file = new FileInfo(picsPathArr[i]);
var stream = file.OpenRead();
byte[] buffer = new byte[file.Length];
//读取图片字节流
stream.Read(buffer, 0, Convert.ToInt32(file.Length));
//base64字符串
string imageBase64 = Convert.ToBase64String(buffer);
//采用base64字符串方式上传
string imageType = "BASE64";
//用户组
string groupId = "GodY";
//用户id,一般同一个人的图片放在同一个id下
string userId = "xiaoY";
//开始注册
SignUpFace(imageBase64, imageType, groupId, userId);
yield return new WaitForSeconds(0.6f);
}
}
注册完后我们可以在百度大脑的控制台找到之前创建的应用的人脸识别库里看到上传的图片
上面我们已经将人脸注册到人脸库了,下面就来在Unity中使用一张新的图片来与人脸库中的进行人脸对比。
虽然说上去是进行人脸对比,实际上是使用的人脸搜索的API。
值得注意的是 使用该人脸搜索方法是从我们注册的人脸库中 检测与我们当前想要进行比较的人脸最相似的人脸,所以说我们最好是新建组之后,为想进行识别的人脸单独创建在一个userID会比较好管理!
可以在官网查看更多关于人脸搜索与人脸库管理详细参数:https://ai.baidu.com/ai-doc/FACE/Gk37c1uzc
我们这里使用的应该是 1:N 人脸搜索,人脸对比其实和人脸搜索实现的功能效果差不多。
关于这俩的对比差异,官方也给出了解答,我们就不纠结这个了,这个直接拿来做测试使用就行!
后面感兴趣可以把官方给的样例全部使用测试一遍,看看到底哪个更好用一些 ~
接下来我们另外弄一张之前人脸库里没有的图片保存到FaceDetect文件夹下
编写脚本代码如下:代码中的groupid需要改成自己的人脸识别库中创建的的groupid
userid需要改成自己的人脸识别库中的userid
public void FaceSearch()
{
//1.拿到我们放在文件夹中的照片文件
FileInfo file = new FileInfo(Application.streamingAssetsPath + "/FaceDetect/图片文件名.jpg");
var stream = file.OpenRead();
byte[] buffer = new byte[file.Length];
stream.Read(buffer, 0, Convert.ToInt32(file.Length));//读取图片字节流
var image = Convert.ToBase64String(buffer);
//2.使用人脸搜索API进行对比查询
var imageType = "BASE64";
var groupIdList = "@@@";//需修改为自己人脸库的组,人脸库注册的组
var options = new Dictionary<string, object>{
{"match_threshold", 70},//限制 匹配阈值(设置阈值后,score低于此阈值的用户信息将不会返回)
{"user_id", "@@@"},//需修改为自己人脸库的id,在指定某个固定user_id时调用,可不加此部分
};
var result = client.Search(image, imageType, groupIdList,options);
Debug.Log(result);
}
对比后会返回一组数据,其中我们只要看score的值,就可以看到人脸的相似度如何
如果score值超过80,就基本认定是同一个人。
这样我们基本就实现了一个简单的人脸识别功能,在注册完人脸库之后,就可以自己随便找一些人脸照片进行测试使用啦!
当然这样对使用者来说体验很差,需要一直去看返回的score值来判断人脸识别的结果。
下面就对该功能进行一个整合优化体验给大家做一个参考,更多想法可以自己改进体验哦!
简单说一下整合优化的思路。
实际识别检测效果如下:
我们可以通过在人脸识别库中添加某个人的人脸照片,就可以实现在Unity中自定义某个人的人脸识别检测!
第一次使用的时候代码一直报错,问了客服才知道原来是免费资源没有领取导致的!!所以直接去领取资源吧!
"error_code": 18,
"error_msg": "Open api qps request limit reached"
免费资源领取地址:https://console.bce.baidu.com/ai/#/ai/face/overview/index
选择需要领取的资源点击领取。
领取后的资源可以在资源列表中查看!
问客服的地址如下:https://ticket.bce.baidu.com/#/ticket/list
虽然文章内容不少,但是整体流程很简单。
先建一个交互类,然后注册人脸,就进行人脸识别就可以了!
工程下载地址:https://download.csdn.net/download/zhangay1998/86803920
Windows简易版源码如下
using Baidu.Aip.Face;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.UI;
public class FaceRecognitionDemo: MonoBehaviour
{
[Header("设置APPID/AK/SK")]
public string APP_ID = "你的 App ID";
public string API_KEY = "你的 Api Key";
public string SECRET_KEY = "你的 Secret Key";
Face client;
[Header("UI部分")]
public Button _selectDocumentBtn;//选择文件按钮
public Button _faceSearchBtn;//开始检测按钮
public Image _faceSearchImage;//当前选择的图片
public Text _faceSearchTxt;//检测结果文本
private float _faceSearchScore;//最终返回的分数
private Texture2D m_Tex;
private OpenFileName ofn;
void Start()
{
ofn = new OpenFileName();
client = new Face(API_KEY, SECRET_KEY);
client.Timeout = 60000; // 修改超时时间
_selectDocumentBtn.onClick.AddListener(OpenFileWin);
_faceSearchBtn.onClick.AddListener(FaceSearch);
//StartCoroutine(IEGetStringBase64());
}
//可优化部分:开始检测换为协程,加一个等待两秒的动画,两秒结束后显示检测结果
#region 注册人脸部分
//获取图片base64字符串,由于QPS限制,此处采用协程降低注册频率
IEnumerator IEGetStringBase64()
{
//获取到每一张图片的路径
string[] picsPathArr = Directory.GetFiles(Application.streamingAssetsPath + "/FaceUpLoad/");
//循环获取每张图片的base64字符串
for (int i = 0; i < picsPathArr.Length; i++)
{
//unity会自动生成.meta文件,过滤掉
if (picsPathArr[i].Contains("meta")) continue;
//读取
FileInfo file = new FileInfo(picsPathArr[i]);
var stream = file.OpenRead();
byte[] buffer = new byte[file.Length];
//读取图片字节流
stream.Read(buffer, 0, Convert.ToInt32(file.Length));
//base64字符串
string imageBase64 = Convert.ToBase64String(buffer);
//采用base64字符串方式上传
string imageType = "BASE64";
//用户组
string groupId = "group1";
//用户id,一般同一个人的图片放在同一个id下
string userId = "YangMi";
//开始注册
SignUpFace(imageBase64, imageType, groupId, userId);
yield return new WaitForSeconds(0.6f);
}
}
///
/// 人脸注册
///
///
///
///
///
public void SignUpFace(string image, string imageType, string groupId, string userId)
{
var options = new Dictionary<string, object>{
{"user_info", "WuYanZu"},
{"quality_control", "NORMAL"},
{"liveness_control", "LOW"},
{"action_type", "REPLACE"}
};
// 带参数调用人脸注册
var result = client.UserAdd(image, imageType, groupId, userId, options);
Debug.Log("注册完成:" + result);
}
#endregion
///
/// 打开文件夹选择想检测的文件
///
public void OpenFileWin()
{
ofn.structSize = Marshal.SizeOf(ofn);
ofn.filter = "All Files\0*.*\0\0";
ofn.file = new string(new char[256]);
ofn.maxFile = ofn.file.Length;
ofn.fileTitle = new string(new char[64]);
ofn.maxFileTitle = ofn.fileTitle.Length;
string path = Application.dataPath;
path = path.Replace('/', '\\');
//默认路径
ofn.initialDir = path;
//ofn.initialDir = "D:\\MyProject\\UnityOpenCV\\Assets";
ofn.title = "Open Project";
ofn.defExt = "xlsx";//显示文件的类型
//注意 一下项目不一定要全选 但是0x00000008项不要缺少
ofn.flags = 0x00080000 | 0x00001000 | 0x00000800 | 0x00000200 | 0x00000008;//OFN_EXPLORER|OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST| OFN_ALLOWMULTISELECT|OFN_NOCHANGEDIR
if (WindowDll.GetOpenFileName(ofn))
{
Debug.Log("当前选择的文件路径:" + ofn.file);
SelectImage();
}
}
///
/// 将选择的图片显示在Image上
///
public void SelectImage()
{
//加载
LoadFromFile(ofn.file);
//变换格式
Sprite tempSprite = Sprite.Create(m_Tex, new Rect(0, 0, m_Tex.width, m_Tex.height), new Vector2(10, 10));
_faceSearchImage.sprite = tempSprite;//赋值
}
//开始对比
public void FaceSearch()
{
//1.拿到我们放在文件夹中的照片文件
//FileInfo file = new FileInfo(Application.dataPath + "/StreamingAssets/FaceDetect/Ym14s.jpeg");
FileInfo file = new FileInfo(ofn.file);
var stream = file.OpenRead();
byte[] buffer = new byte[file.Length];
stream.Read(buffer, 0, Convert.ToInt32(file.Length));//读取图片字节流
var image = Convert.ToBase64String(buffer);
//2.使用人脸搜索API进行对比查询
var imageType = "BASE64";
var groupIdList = "Test1";//人脸库注册的组,需替换为自己的人脸库组名
var options = new Dictionary<string, object>{
{"match_threshold", 70},
{"user_id", "ym"},//在指定某个固定user_id时调用,可不加此部分
};
var result = client.Search(image, imageType, groupIdList);
Debug.Log(result);
string str = result.ToString();
int scoreIndex = str.IndexOf("score");//返回人脸检测到的分数
if(scoreIndex != 0)//避免拿不到返回的检测分数
{
string scoreResult = str.Substring(scoreIndex + 8, 5);
//拿到返回数值后的操作
//float f1 = Convert.ToSingle(scoreResult);
float f;
if (float.TryParse(scoreResult, out f))
{
_faceSearchScore = f;
}
else
{
_faceSearchScore = 50f;
}
}
else { _faceSearchScore = 50f; }
DisplayingResults();
}
///
/// 结果处理
///
private void DisplayingResults()
{
if (_faceSearchScore >= 80)
{
_faceSearchTxt.text="是本人没错了!";
}
else
{
_faceSearchTxt.text = "不是本人!这是哪个小饼干??";
}
}
///
/// 根据路径读取本地文件并转换为Texture2D文件
///
///
private void LoadFromFile(string path)
{
m_Tex = new Texture2D(1, 1);
m_Tex.LoadImage(ReadPNG(path));//读取图片字节流
}
private byte[] ReadPNG(string path)
{
FileStream fileStream = new FileStream(path, FileMode.Open, System.IO.FileAccess.Read);
fileStream.Seek(0, SeekOrigin.Begin);
byte[] binary = new byte[fileStream.Length]; //创建文件长度的buffer
fileStream.Read(binary, 0, (int)fileStream.Length);
fileStream.Close();
fileStream.Dispose();
fileStream = null;
return binary;
}
}