百度人脸识别算法包含有在线算法和本地离线算法,同时也涵盖了多种语言:有C#、C++、Android。现在就带大家来认识一下,百度人脸识别算法(C#版本)是个什么样子的。
准备工作
先注册百度帐号,并登录
SDK完整详细文档:https://ai.baidu.com/sdk
SDK在线版本文档:https://ai.baidu.com/ai-doc/FACE/Ck37c1ri0
SDK离线版本文档:https://ai.baidu.com/ai-doc/FACE/4k37c1n7e
在线版本
- 进入控制台,创建新的应用,获取授权
API Key
,Secret Key
,地址:https://console.bce.baidu.com/ai/#/ai/face/app/list 。 -
安装在线依赖库。可通过页面下载或nuget安装【在NuGet中搜索
Baidu.AI
,安装最新版即可】,推荐使用nuget安装。 - 安装好后,我们可以在C#代码中,编写以下调用代码:
//在线调用
private JObject MatchFace(byte[] image2)
{
//初始化百度人脸在线sdk
var face = new Baidu.Aip.Face.Face("API Key", "Secret Key");
//读取一张需要对比的本地图片
var image1 = File.ReadAllBytes("bp.jpg");
var faces = new JArray
{
new JObject
{
{"image",Convert.ToBase64String(image1)},
{"image_type", "BASE64"},
{"face_type", "LIVE"},
{"quality_control", "LOW"},
{"liveness_control", "NONE"},
},
new JObject
{
{"image", Convert.ToBase64String(image2)},
{"image_type", "BASE64"},
{"face_type", "LIVE"},
{"quality_control", "LOW"},
{"liveness_control", "NONE"},
}
};
var result = face.Match(faces);
return result;
}
通过以上代码就可以完成最简单的人脸比对功能了。
注意:百度的算法依赖Newtonsoft.Json.dll,如果本地也存在此dll,需要注意版本冲突!
在线版本其它功能都有详细的文档介绍,此处不做过多介绍了,主要了解使用线上版本的先决步骤和注意事项。
离线版本
离线版本相比在线版本会更复杂,离线版本需要先在官网上下载相关的SDK,约580MB。
下载后解压压缩包,使用visual studio 2015以上版本打开。
函数的入口在Face.cs
文件中,可根据示例代码进行自行学习。
自己对代码稍微整理了下,请大家享用(其它代码,有需要请邮箱留言):
public class BaiduFaceApi
{
const string STR_BAIDU_API = "BaiduFaceApi.dll";
/// <summary>
/// 初始化人脸sdk
/// </summary>
/// <param name="id_card"></param>
/// <returns></returns>
[DllImport(STR_BAIDU_API, EntryPoint = "sdk_init", CharSet = CharSet.Ansi
, CallingConvention = CallingConvention.Cdecl)]
public static extern int sdk_init(bool id_card);
/// <summary>
/// sdk销毁
/// </summary>
[DllImport(STR_BAIDU_API, EntryPoint = "sdk_destroy", CharSet = CharSet.Ansi
, CallingConvention = CallingConvention.Cdecl)]
public static extern void sdk_destroy();
/// <summary>
/// 是否授权
/// </summary>
/// <returns></returns>
[DllImport(STR_BAIDU_API, EntryPoint = "is_auth", CharSet = CharSet.Ansi
, CallingConvention = CallingConvention.Cdecl)]
public static extern bool is_auth();
/// <summary>
/// 获取设备指纹
/// </summary>
/// <returns></returns>
[DllImport(STR_BAIDU_API, EntryPoint = "get_device_id", CharSet = CharSet.Ansi
, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr get_device_id();
/// <summary>
/// 人脸跟踪
/// </summary>
/// <param name="oface"></param>
/// <param name="mat"></param>
/// <param name="max_track_num"></param>
/// <returns></returns>
[DllImport(STR_BAIDU_API, EntryPoint = "track_mat", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern int track_mat(IntPtr oface, IntPtr mat, ref int max_track_num);
/// <summary>
/// 跟踪最大人脸
/// </summary>
/// <param name="file_name"></param>
/// <returns></returns>
[DllImport(STR_BAIDU_API, EntryPoint = "track_max_face", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr track_max_face(string file_name);
/// <summary>
/// 通过二进制图片跟踪人脸
/// </summary>
/// <param name="image"></param>
/// <param name="size"></param>
/// <param name="max_track_num"></param>
/// <returns></returns>
[DllImport(STR_BAIDU_API, EntryPoint = "track_by_buf", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr track_by_buf(byte[] image, int size, int max_track_num);
/// <summary>
/// 清除人脸跟踪
/// </summary>
[DllImport(STR_BAIDU_API, EntryPoint = "clear_tracked_faces", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern void clear_tracked_faces();
/// <summary>
/// 跟踪人脸
/// </summary>
/// <param name="file_name"></param>
/// <param name="max_track_num"></param>
/// <returns></returns>
[DllImport(STR_BAIDU_API, EntryPoint = "track", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr track(string file_name, int max_track_num);
/// <summary>
/// 跟踪最大人脸
/// </summary>
/// <param name="image"></param>
/// <param name="size"></param>
/// <returns></returns>
[DllImport(STR_BAIDU_API, EntryPoint = "track_max_face_by_buf", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr track_max_face_by_buf(byte[] image, int size);
/// <summary>
/// 人脸1:1比对(传二进制图片buffer)
/// </summary>
/// <param name="buf1"></param>
/// <param name="size1"></param>
/// <param name="buf2"></param>
/// <param name="size2"></param>
/// <returns></returns>
[DllImport(STR_BAIDU_API, EntryPoint = "match_by_buf", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr match_by_buf(byte[] buf1, int size1, byte[] buf2, int size2);
/// <summary>
/// 设置人脸最小尺寸
/// </summary>
/// <param name="size"></param>
[DllImport("BaiduFaceApi.dll", EntryPoint = "set_min_face_size", CharSet = CharSet.Ansi
, CallingConvention = CallingConvention.Cdecl)]
public static extern void set_min_face_size(int size = 30);
[DllImport("BaiduFaceApi.dll", EntryPoint = "rgb_ir_liveness_check_by_buf", CharSet = CharSet.Ansi
, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr rgb_ir_liveness_check_by_buf(byte[] rgb_buf, int rgb_size,
byte[] ir_buf, int ir_size);
// 单目RGB静默活体检测(传入图片文件二进制buffer)
[DllImport("BaiduFaceApi.dll", EntryPoint = "rgb_liveness_check_by_buf", CharSet = CharSet.Ansi
, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr rgb_liveness_check_by_buf(byte[] buf, int size);
}
打开摄像头并用WPF显示的方法:
OpenCvSharp.Extensions.BitmapConverter
类需要反编译出来,百度算法自带的没有包含。
private void StartCamera()
{
Task.Run(() =>
{
while (PlayState.Starting == State)
{
try
{
var curCamera = Cv2.CreateFrameSource_Camera(0);
Mat curFrame=new Mat();
System.Drawing.Bitmap bitmap;
State = PlayState.Playing;
while (PlayState.Stoped != State)
{
curCamera.NextFrame(curFrame);
//设置摄像头的画面镜像显示,以保证人物移动方向和视频流中相同
//除此方法还有更强大的方法,是使用Remap.该方法功能更强大,同时也更耗性能
curFrame=curFrame.Flip(FlipMode.Y);
bitmap = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(curFrame);
Dispatcher.Invoke(() => img.Source = ImageUtil.BitmapToBitmapImage(bitmap));
}
curCamera.Dispose();
}
catch (Exception)
{
Thread.Sleep(1000);
}
}
});
}
public enum PlayState
{
Starting,
Playing,
Pause,
Stoped
}
remap的参考实现
public static void TestRemap()
{
int index = 0;
Mat src = new Mat(path, ImreadModes.AnyColor | ImreadModes.AnyDepth);
Mat dst = new Mat(src.Size(), src.Type());
Mat x = new Mat(src.Rows, src.Cols, MatType.CV_32FC1);
Mat y = new Mat(src.Rows, src.Cols, MatType.CV_32FC1);
//new OpenCvSharp.Window("Input", WindowMode.AutoSize, src);
int key = 0;
while (true)
{
key = Cv2.WaitKey(500);
index = key % 4;
if ((char)key == 27)
{
break;
}
Console.WriteLine("{0}%{1}={2}", key, 4, index);
UpdateRemap(src, x, y, 0);
Cv2.Remap(src, dst, x, y, InterpolationFlags.Linear, BorderTypes.Constant, new Scalar(0, 0, 255));
//new OpenCvSharp.Window("Output", WindowMode.AutoSize, dst);
}
}
public static void UpdateRemap(Mat src, Mat x, Mat y, int flipMode)
{
for (int row = 0; row < src.Rows; row++)
{
for (int col = 0; col < src.Cols; col++)
{
switch (flipMode)
{
case 0:
if (col > (src.Cols * 0.25) && col < (src.Cols * 0.75) && row > (src.Rows * 0.25) && row < (src.Rows * 0.75))
{
/*
* 图像的 Cols 列 组成矩阵的宽度 对应 二维平面的x轴
* 图像的 Rows 行 组成矩阵的高度 对应 二维平面的y轴
* if 条件成立必定满足
* src.Cols*0.75 <= col <= src.Cols*0.25
* src.Rows*0.75 <= row <= src.Rows*0.25
* 则:
* (col - (src.Cols * 0.25)范围在 0 -- 0.5 之间
* 若 (2 * (col - (src.Cols * 0.25)) 则:范围在 0 --1 之间 (原图像的大小)X轴
* (row - (src.Rows * 0.25))范围在 0 -- 0.5 之间
* 若 (2 * (row - (src.Rows * 0.25)) 则:范围在 0 --1 之间 (原图像的大小)Y轴
*
* 前面创建 x ,y 对象是的类型是 CV_32FC1 所以 赋值要用 float 类型
*/
float xVal = (float)(2 * (col - (src.Cols * 0.25)));
float yVal = (float)(2 * (row - (src.Rows * 0.25)));
x.Set<float>(row, col, xVal);
y.Set<float>(row, col, yVal);
}
else
{
x.Set<float>(row, col, 0);
y.Set<float>(row, col, 0);
}
break;
case 1:
x.Set<float>(row, col, (src.Cols - col - 1)); //左右映射 X 轴
y.Set<float>(row, col, row); //Y 轴不变
break;
case 2:
x.Set<float>(row, col, col); //X 轴
y.Set<float>(row, col, (src.Rows - row - 1)); //上下映射 Y轴
break;
case 3:
x.Set<float>(row, col, (src.Cols - col - 1)); //左右映射 X 轴
y.Set<float>(row, col, (src.Rows - row - 1));//上下映射 Y轴
break;
}
}
}
}
划重点:百度SDK在使用的过程中会遇到很多问题,影响我们项目推进。为了防止大家入坑
- 使用百度算法之前,必须要先注册,否则SDK无法正常使用。注册方法只需要打开启动目录下的
LicenseTool.exe
文件,输入授权码。授权成功后,会生成两个授权文件:license.ini
和license.key
。授权是绑定物理机器,如果换另外一台机器,授权码将失效。授权过程中,如果遇到授权失败,则多点几次重试即可。 face-resource
资源包,一定要放在程序运行的上一级目录,否则会导致SDK初始化失败,也就是返回值为-1
。- 人脸识别算法在调用的过程中,我们可能会发现错误返回,抽取人脸特征数据失败,这种情况多数因为 人脸识别的人脸大小过大,把人脸
size
设置更小就可以了(调用set_min_face_size
方法进行设置),默认值是100
。具体参数需要根据实际设定。 - 百度算法依赖
opencv
相关的库,我们在打开摄像头的过程中,也需要使用opencv的相关功能。为了避免冲突,我们需要从baidu sdk
目录下的common
文件夹中,把里面的文件拷贝到启动目录下,否则会出现版本冲突导致的编译错误。 - 程序启动时,会出现
无法定位程序输入点 SfmDxGetSwapChainStats 于动态链接库 xxx d3d9.dll
的错误,只需要将d3d9.dll
改成d3nv.dll
即可 opencv
相关的扩展方法,百度SDK所使用的版本过低,不包含该扩展方法,需要自行反编译出来加入使用,需要开启启用不安全代码。- 人脸识别率会受装饰,头发等影响,使用过程中尽可能避免脸部遮挡,画浓妆等,推荐阈值
80
。 -
调用人脸接口,可能会出现返回的指针为空指针,那么这种情况很有可能是因为百度
SDK
被Dispose
了
持续更新……
本文会经常更新,请阅读原文: https://huchengv5.gitee.io//post/%E7%99%BE%E5%BA%A6%E4%BA%BA%E8%84%B8%E5%85%A5%E9%97%A8.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名胡承(包含链接: https://huchengv5.gitee.io/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 。