一、开发环境准备
1、Android Studio到 2.2版本,在sdk Manager中下载 ndk-bundle 和 cmake
2、下载OpenCV for Android 下载地址:http://opencv.org/downloads.html
二、搭建环境
1、使用Android Studio 新建一个项目(在c++ suport 上打上对勾)
2、带项目创建好之后,将opencv中的头文件,和动态链接库copy到项目中,并在CMakeLists.txt中引入他们。
三、编码
1、新建jni类
package com.blueberry.facerec;
/**
* Created by blueberry on 11/16/2016.
*
* 人脸检测
*/
public class FaceDetector {
static {
System.loadLibrary("face_detect");
}
private long cPtr;
public FaceDetector(String path) {
cPtr = init(path);
}
/**
* 初始化CascadeClassifier,需要传入xml文件的路径,CascadeClassifier会根据这个xml文件进行识别。
*
* @param path xml文件的路径
* @return CascadeClassifier的路径
*/
public final native long init(String path);
/**
* 人脸检测
*
* @param path 人脸图片文件的路径
* @return 检测到人脸数据的集合
*/
public final native Face[] detect(String path);
/**
* 删除CascadeClassifier
*
* @param cPtr CascadeClassifier的指针
*/
private final native void delete(long cPtr);
@Override
protected void finalize() throws Throwable {
delete(cPtr);
}
}
package com.blueberry.facerec;
/**
* Created by blueberry on 11/17/2016.
*
* 检测到人脸的坐标
*/
public class Face {
private int x;
private int y;
private int width;
private int height;
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
@Override
public String toString() {
return "Face{" +
"x=" + x +
", y=" + y +
", width=" + width +
", height=" + height +
'}';
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
}
对应的cpp文件
//
// Created by Administrator on 11/16/2016.
//
#ifndef FACERECOGNITION_FACE_DETECT_CPP
#define FACERECOGNITION_FACE_DETECT_CPP
#include "face_detect.h"
using namespace cv;
using namespace std;
CascadeClassifier *g_CascadeClassifier;
JNIEXPORT jlong JNICALL
Java_com_blueberry_facerec_FaceDetector_init(JNIEnv *env, jobject instance, jstring path_) {
const char *path = env->GetStringUTFChars(path_, NULL);
g_CascadeClassifier = new CascadeClassifier(path);
long ptr = reinterpret_cast<long>(g_CascadeClassifier);
env->ReleaseStringUTFChars(path_, path);
LOGD("cascadeClassifier initialized");
return ptr;
}
JNIEXPORT jobjectArray JNICALL
Java_com_blueberry_facerec_FaceDetector_detect(JNIEnv *env, jobject instance, jstring path_) {
if (!g_CascadeClassifier) {
jclass je = env->FindClass("java/lang/Exception");
env->ThrowNew(je, "CascadeClassifier not initialized!");
}
// 图片的路径
const char *path = env->GetStringUTFChars(path_, 0);
//单通道模式,读取图片
Mat srcImage = imread(path, IMREAD_GRAYSCALE);
//直方图均值化
equalizeHist(srcImage, srcImage);
vector vectors;
g_CascadeClassifier->detectMultiScale(srcImage, vectors);
LOGD("detected.");
jclass objectClass = env->FindClass("com/blueberry/facerec/Face");
// 返回的人脸数组
jobjectArray result = env->NewObjectArray(vectors.size(), objectClass, NULL);
jmethodID cId = env->GetMethodID(objectClass, "" , "()V");
jfieldID xId = env->GetFieldID(objectClass, "x", "I");
jfieldID yId = env->GetFieldID(objectClass, "y", "I");
jfieldID widthId = env->GetFieldID(objectClass, "width", "I");
jfieldID heightId = env->GetFieldID(objectClass, "height", "I");
for (int i = 0; i < vectors.size(); i++) {
jobject obj = env->NewObject(objectClass, cId);
env->SetIntField(obj, xId, vectors[i].x);
env->SetIntField(obj, yId, vectors[i].y);
env->SetIntField(obj, widthId, vectors[i].width);
env->SetIntField(obj, heightId, vectors[i].height);
env->SetObjectArrayElement(result, i, obj);
}
env->ReleaseStringUTFChars(path_, path);
return result;
}
JNIEXPORT void JNICALL
Java_com_blueberry_facerec_FaceDetector_delete(JNIEnv *env, jobject instance, jlong cptr) {
cv::CascadeClassifier *cascadeClassifier = reinterpret_cast(cptr);
delete cascadeClassifier;
return;
}
#endif //FACERECOGNITION_FACE_DETECT_CPP
四、遇到的坑
1、在编写项目时,我发现ndk-bundle更新了,于是我点了一下更新,满怀激动的心情等到下载完(更新到了ndk 13),但在我编译的时候蛋疼的事情发生了,编译不通过了,搞得我很焦灼。最后静下心来看log 其中报错的信息中有这么一段话:
..... Invalid Android NDK revision (should be 12):
What!!! 真是蛋疼~ ,说明:升级有风险,更新需谨慎啊!,但最后终于找到了一个方法来fix它,打开android_sdk/cmke/3.6.xxxxx/android.toolchain.cake文件,找到报错的那一行:
if(NOT ANDROID_NDK_PACKAGE_REVISION MATCHES "^${ANDROID_NDK_REVISION}\\.")
message(FATAL_ERROR "Invalid Android NDK revision (should be ${ANDROID_NDK_REVISION}): ${ANDROID_NDK_PACKAGE_REVISION}.")
endif()
错误正式从这里出来的。
最后们只需要吧ANDROID_NDK_REVISION 改成13就行了,也就是位于android.toolchain.cmake的 第43行左右 改为 set(ANDROID_NDK_REVISION 13) 好了。
2、在 build.gradle 要引入opencv的动态库文件目录
sourceSets {
main {
java.srcDirs 'src/main/java'
jniLibs.srcDirs 'opencv/prebuilt', 'lib'
}}
其中 opencv/prebuilt为opencv动态库的目录
3、设置 abi类型
ndk {
//打包进APK的ABI类型
abiFilters 'x86'
}
这里只能设置一种ABI类型,设置2个以上就会报错,目前我也不知道什么情况~,有知道的朋友留言哈~
旨在体验Android 2.2 使用NDK开发,代码中若是有不严谨的地方,还望大神勿喷
最后完整项目的地址: https://github.com/blueberryCoder/FaceRecognition