最近因为需要实现了一下人脸识别,实现了这个功能之后,就想着把踩过的坑总结出来。参考过许多博客,发现主要有两种形式,一种是基于 android SDK 实现人脸检测,而另一种是利用openCvManager来实现。这两种方式都实现了动态的人脸检测,当然网上也有许多关于检测静态图片中的人脸的文章。这里我就不详细介绍了。我这里实现的功能主要是启动Camera之后,当照相机中有人脸出现时,Camera界面动态的绘制出人脸矩形框,获取到人脸框之后,Camera拍照,获取到图片,将图片传送到后台来验证人脸。这里需要区分一下人脸检测和人脸识别。人脸检测是检测camera中是否有人脸存在,如果存在的话会绘制出矩形框将人脸标志出来,而人脸识别则是根据两张人脸验证是否是一个人的概念。
先大概讲一下第一种实现动态人脸检测的方式.这是Camera基于Google自带的FaceDetectionListener进行人脸检测,当Camera拍摄到人脸后,会将人脸矩形绘制出来。这个具体大家可以参考这位大神的博客,http://blog.csdn.net/yanzi1225627/article/details/38098729/
他已经讲的很细致了。但感觉google自带检测算法在测试的时候检测人脸比较慢,而且有些机型不支持这种人脸检测接口,不过他的文章还是很具有参考价值的。那么我就在这里就具体介绍一下使用openCv实现动态人脸检测的具体方法。
先提供下opencv下载地址http://opencv.org/ 我下载的是最新的3.2版本,本来用的是2.4,但会发现比较卡顿。公司一直使用的eclipse,而且demo中的工程也是eclipse项目,所以直接使用eclipse来进行调试。当然网上也有许多在As中使用的教程,大家可以去多找找。
在我们下载sdk后解压后,进入到samples目录下会见到如下结构:
我们直接运行example-face-detection.apk后,会发现提示要安装opencv-manager,在sdk/apk目录下,我们会发现不同种类的opencvmanager,我们根据不同的机型选择对应的manager安装,我选择的是OpenCV_3.2.0_Manager_3.20_armeabi-v7a.apk这个包,安装之后,我们打开应用后就可以发现摄像头进行动态的人脸检测了。
运行完官方的apk后,我们开始自己进行调试。我们导入OpenCV-android-sdk/samples的face-detection工程,然后新建个工程作为Library,在我们的library中导入OpenCV-android-sdk/java中相关类和资源文件,
然后对face-detection进行关联,这时候我的工程仍然报错,发现下面的错误:
没有找到ndk-build.cmd.原来是没有配置ndk-build路径,配置完NDKROOT即可。这里就是右键properties-C/C++build,如下图:
这里的ndk我下载的是android-ndk-r9d,在下载完毕之后我们直接解压就行,这里的NDKROOT就是我们解压后的ndk的路径。附带上Ndk的下载路径:
http://dl.google.com/android/ndk/android-ndk-r9d-windows-x86.zip
之后又碰见如下错误:
这是因为在jni/Android.mk中没配置opencv的Opencv.mk的根路径,同理和ndk配置方式一样,我们配置完我们自己的${OPENCV_ANDROID_SDK}就行了
至此,我们运行官方的demo,可直接就运行出来,但我们发现总需要安装opencvmanager,很明显,有时候这是不合适的。所以接下来我介绍下不需要安装opencvmanager的方法,很简单,分下面4步:
1.修改Android.mk前几行 为下列形式,
include $(CLEAR_VARS)
OPENCV_CAMERA_MODULES:=on
OPENCV_INSTALL_MODULES:=on
OPENCV_LIB_TYPE:=SHARED
- 1
- 2
- 3
- 4
2.打开FdActivity.java文件,在其中添加一个静态初始化块代码,
static {
Log.i(TAG, "OpenCV library load!");
if (!OpenCVLoader.initDebug()) {
Log.i(TAG, "OpenCV load not successfully");
} else {
System.loadLibrary("detection_based_tracker");// load other libraries
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
它是用来加载OpenCV_java库的,如果没有加回报类似于native method not found的错误
3.注释掉onResume中的initAsync那句,让程序不去访问OpenCV Manager。
@Override
public void onResume()
{
super.onResume();
if (!OpenCVLoader.initDebug()) {
Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
// OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mLoaderCallback);
} else {
Log.d(TAG, "OpenCV library found inside package. Using it!");
mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
4.修改FdActivity.java的OnCreate()方法,从上面的private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this)代码块中拷贝try-catch块放到OnCreate的setContentView()之后。
try {
// load cascade file from application resources
InputStream is = getResources().openRawResource(R.raw.lbpcascade_frontalface);
File cascadeDir = getDir("cascade", Context.MODE_PRIVATE);
mCascadeFile = new File(cascadeDir, "lbpcascade_frontalface.xml");
FileOutputStream os = new FileOutputStream(mCascadeFile);
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
is.close();
os.close();
mJavaDetector = new CascadeClassifier(mCascadeFile.getAbsolutePath());
if (mJavaDetector.empty()) {
Log.e(TAG, "Failed to load cascade classifier");
mJavaDetector = null;
} else
Log.i(TAG, "Loaded cascade classifier from " + mCascadeFile.getAbsolutePath());
mNativeDetector = new DetectionBasedTracker(mCascadeFile.getAbsolutePath(), 0);
cascadeDir.delete();
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "Failed to load cascade. Exception thrown: " + e);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
之后运行,即可发现不用在依赖OpenCvManager,程序也可以直接运行了。
这里上述进行的主要是camera动态监测出人脸,其实这基本上已经算完成了。现在我们集成到我们自己的工程中,通过利用百度的人脸识别,来判断人脸是否一致,从而实现人脸识别。
首先,我们可以在百度AI开放平台上 http://ai.baidu.com/ 创建我们自己的人脸识别应用
![这里写图片描述](http://img.blog.csdn.net/20170712160653919? /2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjg5MzE2MjM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
创建完后 我们的应用会有一个APPiD和ApI key我们利用这两个属性值可生成access_token具体的调用方式我们可参考
https://cloud.baidu.com/doc/FACE/Face-API.html生成access_token后,当发送post请求时,附带上这个token即可,我们可以进行人脸识别了。我这里调用的是人脸注册和人脸认证接口,通过认证返回的分数值,我们可判断是否是同一个人。具体请求参数和返回参数,大家可以参考百度人脸识别的API。
好了,现在开始创建我们的应用。在创建完工程后,我们把face-detection中的FdActivity和DetectionBasedTracker导入到我们自己的工程中,这之中的FdActivity相当于相机界面,这里注意不要改动包名,否则可能会出现找不到本地方法的错误。同时,我们要将jni和Lib armead目录复制到我们的目录里,之后我们要利用Eclipse自动编译NDK/JNI。下面简单介绍一下自动编译的方法:
1.将Android项目转换为C/C++项目,如下图,New -> Other -> C/C++ -> Convert to a C/C++ Project.
2. 配置NDK编译路径,Project->Properties,如下图,其中Build-Command中ANDROID_NDK为环境变量中配置的Android-NDK路径;Build-Directory为当前工程目录
3.Project->Properties,CNU C和GNU C++中配置OpenCV的链接库
配置完指后,我们需要稍微修改一下我们自己创建的opencvLibrary的内容。因为camera中出现人脸框后需要拍照功能,我们需要简单的修改OpenCvLibrary中的JavaCameraView,在这个类里面我们可以调用camera的takepicture方法。同时通过PictureCallback将我们拍的照片保存到我们指定的路径。
mCamera.takePicture(mShutterCallback, null, mJpegPictureCallback);
ShutterCallback mShutterCallback = new ShutterCallback()
{
public void onShutter() {
}
};
PictureCallback mJpegPictureCallback = new PictureCallback()
{
public void onPictureTaken(byte[] data, Camera camera) {
Log.d("hr", "拍照回调");
Bitmap b = null;
if (null != data) {
b = BitmapFactory.decodeByteArray(data, 0, data.length);
mCamera.stopPreview();
}
if (null != b) {
Bitmap rotaBitmap = ImageUtil.getRotateBitmap(b, 00.0f);
boolean bitmap = FileUtil.saveBitmap(rotaBitmap,idTag);
if (bitmap) {
if (handler!=null) {
handler.sendEmptyMessage(2000);
}
}
}
mCamera.startPreview();
}};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
在FdActivity的 onCameraFramed中 facesArray 得长度 即是人脸显示的数目
Rect[] facesArray = faces.toArray();
for (int i = 0; i < facesArray.length; i++)
Imgproc.rectangle(mRgba, facesArray[i].tl(), facesArray[i].br(), FACE_RECT_COLOR, 3);
- 1
- 2
- 3
当相机捕捉到人脸后,我们可以通过handle自动拍照也可以手动拍照,这就看具体的需求了。保存图片之后,我们调用百度的接口,根据返回值来判断人体的相似度。
url = new URL(urlPath);
Bitmap bmp = FileUtil.getValidateBitmap();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.JPEG, 100, baos);
try {
baos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
byte[] buffer = baos.toByteArray();
// 将图片的字节流数据加密成base64字符输出
String photo = Base64.encodeToString(buffer, 0, buffer.length,Base64.DEFAULT);
HashMap<String, String> map=new HashMap<String, String>();
map.put("uid", uid);
map.put("group_id", groupid);
map.put("image", photo);
map.put("ext_fields", "faceliveness");
String str=HttpClientUtil.doPost(urlPath, map);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
获取验证和注册的方式代码基本上一样,就是发送普通的网络请求,我们根据返回值可以判断出人脸是否一致。至此整个人脸验证流程就算结束了。我这里就光展示一下动态检测人脸的效果
当然还有认证的流程,因为感觉难点主要在动态识别人脸这块,所以调用百度的接口,实现人脸识别的过程就不再详细介绍了。当然 ,还有一些小问题,当竖屏时,检测不准确或者Camera界面没有全屏, 或者是前置摄像头的开启。大家有兴趣的话可以去研究一下。