最近因为需要实现了一下人脸识别,实现了这个功能之后,就想着把踩过的坑总结出来。参考过许多博客,发现主要有两种形式,一种是基于 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/ 创建我们自己的人脸识别应用

创建完后 我们的应用会有一个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界面没有全屏, 或者是前置摄像头的开启。大家有兴趣的话可以去研究一下。