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中引入他们。

最后我们的目录大致如下:
这里写图片描述

三、编码
1、新建jni类

package com.blueberry.facerec;

/**
 * Created by blueberry on 11/16/2016.
 * <p>
 * 人脸检测
 */
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.
 * <p>
 * 检测到人脸的坐标
 */
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<Rect> 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, "<init>", "()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<cv::CascadeClassifier *>(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

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值