Android Studio 2.2 NDK开发 opencv 人脸识别

一、开发环境准备
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中引入他们。

最后我们的目录大致如下:
Android Studio 2.2 NDK开发 opencv 人脸识别_第1张图片

三、编码
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

你可能感兴趣的:(Android,Andorid应用开发)