几年前我接触的计算机视觉学习库emgucv、aforge.net因为识别率低误差大,加上我没有时间去训练模型因此关于人脸识别领域被我搁置了很久,
直到今年我接触了dlib,从效果演示来看让我非常满意特别是它可以匹配出人脸的68个特征点(包括眼睛、眉毛、鼻子、嘴巴等)于是我就想将它用到C#
上(早前我封装过C++),大约花费了两周时间从编译官方demo到移植成功,中间遇到各种坑(C++各种指针、对应C#类型转换、编译环境配置等等),
中间无数次想过放弃但是出于对于这项技术的需要我还是坚持了下来,今天终于成功将dlib运用到了C#上提取了68个特征点并绘制出来效果还是让人满意的,
本文将简述下实现移植的大概步骤希望能帮助需要的人。
视觉学习库:opencv、emgucv、dlib
编译工具:visual studio 2017 、CMake
操作系统:Win10 64位
请参考我这篇dlib、opencv的文章 http://www.cnblogs.com/dongzhaosheng/p/8568163.html
dlib提供了两种语言一个是C++库 ,另一个是python库(为此我还特意去学了几天python),首先我想到了用python封装,但是结果我失败了听说python是胶水语言实质上它是把其它语言沾到自己身上,要是想把python
融合到其它语言上恐怕你还得下功夫想想怎么才能粘的没有缝。这里提供一个目前C#引用python的方案InronPython,我尝试了用它去调用python里的方法很遗憾现在python都已经3.0了 该容器还是2.x的解决方案,因此
第三方库我根本掉不到也没法封装,因此该方案被我pass了。当然你可以用python来写你自己的项目如果不要是要求即时输出的话可以将数据保存到目录再读回来。
第二种方案就是用C++来封装dll给C#调用毕竟是自己平台的东西所以兼容方面肯定没问题,封装C++方案一共3种:DllImport、利用CLI 编写的C++要符合CLS规范(虽然是C++项目里编写但是用到的几乎没有C++的类型)、
利用Component Object Model (COM组件对象模型)的方式包装这也是windows底层大部分插件的构成方式。
①DllImport 最简单跟调用系统API一样就可以完成封装好的方法,但是缺点是只能是静态方法,难以对内部对象直接进行控制(不好做前期的初始化或者释放)
[DllImport(“user32.dll”,CharSet = CharSet.Unicode)]
public static extern int MessageBox(IntPtr hWnd,String text,String caption,uint type);
②CLI 编写dll 但是要用CLS规范的类型来写,优点是可以做到无缝衔接只要成功编写到C#中就可以像平时操作其它类的方式来操作,但是对于不熟悉C++的C#工程师或者不熟悉面向对象的C++工程师都是一种煎熬,并且对于调用第三方库并不是很友好我经过
多番尝试无法编译后(需要兼容太多写法)只好放弃。
③最后我选择的方案也是CLI,可是微软帮我们提供了一种非常便捷的包装方式,它不需要你搭建非常复杂的接口转换只需要利用ATL向导即可轻松完成一个方法或者对象的建立,然后你只要在继承接口的方法中写实现过程即可,但是你要满足输出输入格式因为com为兼容语言
类型是中间类型(关于C#跟C++ATL的类型转换可自行查阅)。
因为人脸识别与特征识别都不是我实现的,我只是利用dlib机器学习库的现成函数拼凑的功能,但是从C++移植到C#的大致方式我还是要说下因为现在网上还没有看到比较完整的解决方案。首先就是类型转换字符型没什么好说的BSTR在C#里对应的就是string,到了
c++中有现成的转换函数_com_util::ConvertBSTRToString,然后是人脸识别传入的图像数据首先我想到的就是将图像指针直接传入然后到C++中完成图像初始化,经过多方资料查阅后我查到原来C#的emgucv里的图像指针可以直接到C++的opencv里转成需要的Mat类型
因为他们都符合IplImage规范,指针的传递可以用LRESULT来完成C#里看到的是一个long型数据。
关于输出结果很多人倾向于在C++里完成图像操作然后直接将图像返回到C#,但是如果我只想取得人类数据结构呢?我这里使用的可以序列号与反序列化的json,在c++里将得到的人脸模型转成json格式再到C#里反序列化拿到实例即可,C++没有C#那么方便的Json转换工具只有一个rapidjson库(一个台湾同胞写的第三方库)。
rapidjson::Document jsonDoc;
rapidjson::Document::AllocatorType &allocator = jsonDoc.GetAllocator(); //获取分配器
jsonDoc.SetArray();
/
插入数据过程
rapidjson::StringBuffer buffer;
rapidjson::Writer writer(buffer);
jsonDoc.Accept(writer);
std::string strJson = buffer.GetString();
封装好后就可以直接在C#里进行调用了
NaughtyKidATLLib.NaughtyKidDlibClass dlib = new NaughtyKidDlibClass();
//初始化数据文件
var a = dlib.InitialDat($"{System.IO.Directory.GetCurrentDirectory()}\\shape_predictor_68_face_landmarks.dat");
//返回特征点
System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
Image img3 = new Image(System.IO.Directory.GetCurrentDirectory() + "\\temp.jpg");
watch.Start();//开始计时
var b = dlib.GetFaceModel((long)img3.Ptr);
var models = NaughtyKid.MyTools.SerializationHelper.ScriptDeserialize>(b);
watch.Stop();//停止计时
链接: https://pan.baidu.com/s/1hWHOStcZtSHJdTsFdQ_Qag 提取码: jwkx
注册环境:Microsoft Visual C++ Redistributable for Visual Studio 2017 最新版本14.13 或以上
注册方法:该dll为64为dll 进入cmd(管理员方式运行)进入c:\Windows\SysWOW64\下 通过regsvr32 dll所在路径(NaughtyATL.dll不是opencv那个但是两者必须位于同一目录下)
注册成功后方可在C#引用中COM里找到NaughtyATL