OpenCV Android 中的应用

使用 Android NDK 编译 so

简介

linuxt 系统下使用 OpenCV2.3 + NDK R6 编译 OpenCV 人脸检测应用

准备

Android NDK ( r5 或更高版本 ) http://developer.android.com/sdk/ndk/index.html

OpenCV Android http://sourceforge.net/projects/opencvlibrary/files/opencv-android/2.3/

cmake( 可选,替代 NDK) http://www.cmake.org/

注: http://code.google.com/p/android-opencv/ 网站上说要使用 crystax ndk r4 代替 NDK 。估计可能是对于较旧的 Android 版本需要这样。如果 NDK 无法编译,请尝试使用 crystax ndk r4 编译。

OpenCV 设置

从网站上下载 OpenCV 2.3.0 for Android 后,解压到某个目录,如 ~/ 目录下

设置 OPENCV_PACKAGE_DIR 环境变量

$ export OPENCV_PACKAGE_DIR=~/enCV-2.3.0/

新建一个 Android 工程

eclipse 中新建一个 android 工程如 study.opencv ,并且在工程根目录下新建一个名为 jni 的目录。将下载的 android-ndk-r6 解压到某个目录下,如 ~/

~/android-ndk-r6/sample 下某个 sample 中拷贝 Android.mk, Application.mk study.opencv/jni 目录

设置编译脚本

Android.mk 中, include $(CLEAR_VARS) 后面,加入下行

include $(OPENCV_PACKAGE_DIR)/$(TARGET_ARCH_ABI)/share/opencv/OpenCV.mk

如果应用支持 ARM NEON 那么还需要加入以下行

include $(OPENCV_PACKAGE_DIR)/armeabi-v7a-neon/share/opencv/OpenCV.mk

LOCAL_ARM_NEON := true

Application.mk 中加入以下行

APP_STL := gnustl_static

APP_CPPFLAGS := -frtti -fexceptions

注:关于 Android.mk Application.mk 的详细说明,请参考 ndk/docs Android-mk.html Application-mk.html

Java 层定义 native 接口

新建 study.opencv.FaceRec 类,定义一个人脸检测的本地接口

/**

* detect front face from image.

*

* @param xml

* opencv haarcascade xml file path

* @param infile

* input image file path

* @param outfile

* output image file path

*/

public native void detect(String xml, String infile, String outfile);

生成 jni 头文件

使用 javah 命令生成 jni 头文件

$ cd ~/workspace/study.opencv/bin

$ javah study.opencv.FaceRec

会在 bin 目录生成一个 study_opencv_FaceRec.h 文件。将此文件拷贝到 ../jni 目录中

注:如果接口有变更,请先手动删除生成的 .h 文件。以防止一些意外的错误。

c 层实现图像人脸检测

jni 目录中使用文本编辑器新建一个 facedetect.cpp ,实现图像人脸检测

#include "cv.h"#include "highgui.h"#include <stdio.h>#include <stdlib.h>#include <string.h>#include <assert.h>#include <math.h>#include <float.h>#include <limits.h>#include <time.h>#include <ctype.h>#include <android/log.h>#include <study_opencv_FaceRec.h>#include <jni.h>#define  LOG_TAG    "opencv_face_detect"#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)static CvMemStorage* storage = 0;static CvHaarClassifierCascade* cascade = 0;void detect_and_draw( IplImage* image );const char* cascade_name =    "haarcascade_frontalface_alt.xml";/*    "haarcascade_profileface.xml";*//*int captureFromImage(char* xml, char* filename);*/char* jstring2String(JNIEnv*, jstring);int captureFromImage(char* xml, char* filename, char* outfile){LOGI("begin: ");// we just detect image// CvCapture* capture = 0;    IplImage *frame, *frame_copy = 0;    const char* input_name = "lina.png";if(xml != NULL){cascade_name = xml;}if(filename != NULL){input_name = filename;}LOGI("xml=%s,filename=%s", cascade_name, input_name);// load xml     cascade = (CvHaarClassifierCascade*)cvLoad( cascade_name, 0, 0, 0 );LOGI("load cascade ok ? %d", cascade != NULL ? 1 : 0);if( !cascade )    {        LOGI("ERROR: Could not load classifier cascade\n" );// I just won't write long full file path, to instead of relative path, but I failed.FILE * fp = fopen(input_name,"w");if(fp == NULL){LOGE("create failed");}        return -1;    }storage = cvCreateMemStorage(0);// cvNamedWindow( "result", 1 );    IplImage* image = cvLoadImage( input_name, 1 );    if( image )    {LOGI("load image successfully");        detect_and_draw( image );        // cvWaitKey(0);if(outfile != NULL){LOGI("after detected save image file");cvSaveImage(outfile, image);//把图像写入文件}        cvReleaseImage( &image );    }else{LOGE("can't load image from : %s ", input_name);}}void detect_and_draw( IplImage* img ){    static CvScalar colors[] =     {        {{0,0,255}},        {{0,128,255}},        {{0,255,255}},        {{0,255,0}},        {{255,128,0}},        {{255,255,0}},        {{255,0,0}},        {{255,0,255}}    };    double scale = 1.3;    IplImage* gray = cvCreateImage( cvSize(img->width,img->height), 8, 1 );    IplImage* small_img = cvCreateImage( cvSize( cvRound (img->width/scale),                         cvRound (img->height/scale)),                     8, 1 );    int i;    cvCvtColor( img, gray, CV_BGR2GRAY );    cvResize( gray, small_img, CV_INTER_LINEAR );    cvEqualizeHist( small_img, small_img );    cvClearMemStorage( storage );    if( cascade )    {        double t = (double)cvGetTickCount();        CvSeq* faces = cvHaarDetectObjects( small_img, cascade, storage,                                            1.1, 2, 0/*CV_HAAR_DO_CANNY_PRUNING*/,                                            cvSize(30, 30) );        t = (double)cvGetTickCount() - t;        LOGI( "detection time = %gms\n", t/((double)cvGetTickFrequency()*1000.) );        for( i = 0; i < (faces ? faces->total : 0); i++ )        {            CvRect* r = (CvRect*)cvGetSeqElem( faces, i );            CvPoint center;            int radius;            center.x = cvRound((r->x + r->width*0.5)*scale);            center.y = cvRound((r->y + r->height*0.5)*scale);            radius = cvRound((r->width + r->height)*0.25*scale);            cvCircle( img, center, radius, colors[i%8], 3, 8, 0 );        }    }    // cvShowImage( "result", img );    cvReleaseImage( &gray );    cvReleaseImage( &small_img );}JNIEXPORT void JNICALL Java_study_opencv_FaceRec_detect  (JNIEnv * env, jobject obj, jstring xml, jstring filename, jstring outfile){LOGI("top method invoked! ");/*LOGI("1");char * c_xml = (char *)env->GetStringUTFChars(xml, JNI_FALSE);LOGI("char * = %s", c_xml);if(c_xml == NULL){LOGI("error in get char*");return;}char * c_file = env->GetStringCritical(env, filename, 0);if(c_xml == NULL){LOGI("error in get char*");return;}captureFromImage(c_xml, c_file);env->ReleaseStringCritical(env, xml, c_xml);env->ReleaseStringCritical(env, file_name, c_file);*/captureFromImage(jstring2String(env,xml), jstring2String(env,filename), jstring2String(env,outfile));}//jstring to char*char* jstring2String(JNIEnv* env, jstring jstr){if(jstr == NULL){LOGI("NullPointerException!");return NULL;}char* rtn = NULL;jclass clsstring = env->FindClass("java/lang/String");jstring strencode = env->NewStringUTF("utf-8");jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);jsize alen = env->GetArrayLength(barr);jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);if (alen > 0){rtn = (char*)malloc(alen + 1);memcpy(rtn, ba, alen);rtn[alen] = 0;}env->ReleaseByteArrayElements(barr, ba, 0);LOGI("char*=%s",rtn);return rtn;}

Android.mk:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

include $(OPENCV_PACKAGE_DIR)/$(TARGET_ARCH_ABI)/share/opencv/OpenCV.mk

LOCAL_MODULE := facedetect

LOCAL_CFLAGS := -Werror

LOCAL_SRC_FILES := \

facedetect.cpp \

LOCAL_LDLIBS := -llog

include $(BUILD_SHARED_LIBRARY)

Application.mk:

APP_ABI := armeabi armeabi-v7a

APP_PLATFORM := android-10

APP_STL := gnustl_static

APP_CPPFLAGS := -frtti -fexceptions

使用 NDK 进行编译

在工程 jni ndk-build

$ cd ~/workspace/study.opencv/jni

$ ~/android-ndk-r6/ndk-build.

如果 编译 成功, 会在工程下面生成 libs/armeabi/facedetect.so .

如有 编译 根据提示修改 错误

调用 JNI 接口

opencv 脸检测 要用到的 xml 文件 ( 位于 OpenCV-2.3.0/armeabi/share/opencv/haarcascades/ 目录下 ) 像文件使用 DDMS push data/data/study.opencv/files 中。

activity 中新建一个 线 程, FaceRec#detect 方法。

@Override

public void onCreate(Bundle savedInstanceState) {

super .onCreate(savedInstanceState);

setContentView(R.layout. main );

final FaceRec face = new FaceRec();

new Thread() {

@Override

public void run() {

face.detect(

"/data/data/study.opencv/files/haarcascade_frontalface_alt2.xml" ,

"/data/data/study.opencv/files/wqw1.jpg" ,

"/data/data/study.opencv/files/wqw1_detected.jpg" );

}

}.start();

}

运行结果

经测试,对png,jpg,bmp图片正确识别人脸,不过速度太慢了。

参考

脸检测 http://www.opencv.org.cn/index.php/%E4%BA%BA%E8%84%B8%E6%A3%80%E6%B5%8B


更多相关文章

  1. android socket 配置文件
  2. Android学习笔记之mainfest文件中android属性
  3. android 下载文件注意事项
  4. mainfest文件中android属性
  5. ~/.dirlst 文件
  6. Android 读取和保存文件(手机内置存储器)
  7. 麦子学院Android应用开发工程师视频教程 共18章
  8. 使用Eclipse开发Android时整个工程或第三方jar包的正确使用
  9. Android Studio 工程视图&项目构成

随机推荐

  1. JQuery函数不能用于初始触发器
  2. 百度地图Api进阶教程-实例高级操作8.html
  3. HTML5新增标签与属性
  4. 如何确定在web页面上呈现的字符串的长度(
  5. telnet建立http连接获取网页HTML内容
  6. jQuery:流体同位素仅在调整大小后才工作
  7. html中显示div的时候,超出浏览器的宽,怎么
  8. 用Jsoup实现html中标签替换
  9. 一步一步学Silverlight 2系列(3):界面布局
  10. vim / vi / linux:正确缩进html文件