使用Android NDK编译OpenCV应用r人脸检测

本文介绍如何在Android设备上使用OpenCV库进行人脸检测应用开发,包括使用NDK编译so库,设置环境变量,创建Android工程,实现图像人脸检测等功能。通过示例代码展示了如何捕获图像,加载预训练的人脸检测模型,检测并绘制人脸矩形框,最终将处理后的图像保存为文件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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 ,实现图像人脸检测

Cpp代码 复制代码  收藏代码
  1. #include "cv.h"   
  2. #include "highgui.h"   
  3.   
  4. #include <stdio.h>   
  5. #include <stdlib.h>   
  6. #include <string.h>   
  7. #include <assert.h>   
  8. #include <math.h>   
  9. #include <float.h>   
  10. #include <limits.h>   
  11. #include <time.h>   
  12. #include <ctype.h>   
  13.   
  14. #include <android/log.h>   
  15. #include <study_opencv_FaceRec.h>   
  16. #include <jni.h>   
  17.   
  18. #define  LOG_TAG    "opencv_face_detect"   
  19. #define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)   
  20. #define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)   
  21.   
  22. static CvMemStorage* storage = 0;   
  23. static CvHaarClassifierCascade* cascade = 0;   
  24. void detect_and_draw( IplImage* image );   
  25. const char* cascade_name =   
  26.     "haarcascade_frontalface_alt.xml";   
  27. /*    "haarcascade_profileface.xml";*/  
  28. /*int captureFromImage(char* xml, char* filename);*/  
  29. char* jstring2String(JNIEnv*, jstring);   
  30. int captureFromImage(char* xml, char* filename, char* outfile)   
  31. {   
  32.     LOGI("begin: ");   
  33.     // we just detect image   
  34.     // CvCapture* capture = 0;   
  35.     IplImage *frame, *frame_copy = 0;   
  36.     const char* input_name = "lina.png";   
  37.     if(xml != NULL)   
  38.     {   
  39.         cascade_name = xml;    
  40.     }   
  41.     if(filename != NULL)   
  42.     {   
  43.         input_name = filename;   
  44.     }   
  45.     LOGI("xml=%s,filename=%s", cascade_name, input_name);   
  46.     // load xml    
  47.     cascade = (CvHaarClassifierCascade*)cvLoad( cascade_name, 0, 0, 0 );   
  48.     LOGI("load cascade ok ? %d", cascade != NULL ? 1 : 0);   
  49.     if( !cascade )   
  50.     {   
  51.         LOGI("ERROR: Could not load classifier cascade\n" );   
  52.         // I just won't write long full file path, to instead of relative path, but I failed.   
  53.         FILE * fp = fopen(input_name,"w");   
  54.         if(fp == NULL){   
  55.             LOGE("create failed");   
  56.         }   
  57.         return -1;   
  58.     }   
  59.     storage = cvCreateMemStorage(0);   
  60.     // cvNamedWindow( "result", 1 );   
  61.     IplImage* image = cvLoadImage( input_name, 1 );   
  62.     if( image )   
  63.     {   
  64.         LOGI("load image successfully");   
  65.         detect_and_draw( image );   
  66.         // cvWaitKey(0);   
  67.         if(outfile != NULL)   
  68.         {   
  69.             LOGI("after detected save image file");   
  70.             cvSaveImage(outfile, image);//把图像写入文件   
  71.         }   
  72.         cvReleaseImage( &image );   
  73.     }   
  74.     else  
  75.     {   
  76.         LOGE("can't load image from : %s ", input_name);   
  77.     }   
  78. }   
  79. void detect_and_draw( IplImage* img )   
  80. {   
  81.     static CvScalar colors[] =    
  82.     {   
  83.         {{0,0,255}},   
  84.         {{0,128,255}},   
  85.         {{0,255,255}},   
  86.         {{0,255,0}},   
  87.         {{255,128,0}},   
  88.         {{255,255,0}},   
  89.         {{255,0,0}},   
  90.         {{255,0,255}}   
  91.     };   
  92.     double scale = 1.3;   
  93.     IplImage* gray = cvCreateImage( cvSize(img->width,img->height), 8, 1 );   
  94.     IplImage* small_img = cvCreateImage( cvSize( cvRound (img->width/scale),   
  95.                          cvRound (img->height/scale)),   
  96.                      8, 1 );   
  97.     int i;   
  98.     cvCvtColor( img, gray, CV_BGR2GRAY );   
  99.     cvResize( gray, small_img, CV_INTER_LINEAR );   
  100.     cvEqualizeHist( small_img, small_img );   
  101.     cvClearMemStorage( storage );   
  102.     if( cascade )   
  103.     {   
  104.         double t = (double)cvGetTickCount();   
  105.         CvSeq* faces = cvHaarDetectObjects( small_img, cascade, storage,   
  106.                                             1.1, 2, 0/*CV_HAAR_DO_CANNY_PRUNING*/,   
  107.                                             cvSize(30, 30) );   
  108.         t = (double)cvGetTickCount() - t;   
  109.         LOGI( "detection time = %gms\n", t/((double)cvGetTickFrequency()*1000.) );   
  110.         for( i = 0; i < (faces ? faces->total : 0); i++ )   
  111.         {   
  112.             CvRect* r = (CvRect*)cvGetSeqElem( faces, i );   
  113.             CvPoint center;   
  114.             int radius;   
  115.             center.x = cvRound((r->x + r->width*0.5)*scale);   
  116.             center.y = cvRound((r->y + r->height*0.5)*scale);   
  117.             radius = cvRound((r->width + r->height)*0.25*scale);   
  118.             cvCircle( img, center, radius, colors[i%8], 3, 8, 0 );   
  119.         }   
  120.     }   
  121.     // cvShowImage( "result", img );   
  122.     cvReleaseImage( &gray );   
  123.     cvReleaseImage( &small_img );   
  124. }   
  125.   
  126. JNIEXPORT void JNICALL Java_study_opencv_FaceRec_detect   
  127.   (JNIEnv * env, jobject obj, jstring xml, jstring filename, jstring outfile)   
  128. {   
  129.     LOGI("top method invoked! ");/*LOGI("1");  
  130.     char * c_xml = (char *)env->GetStringUTFChars(xml, JNI_FALSE);  
  131.     LOGI("char * = %s", c_xml);  
  132.     if(c_xml == NULL)  
  133.     {  
  134.         LOGI("error in get char*");  
  135.         return;  
  136.     }  
  137.     char * c_file = env->GetStringCritical(env, filename, 0);  
  138.     if(c_xml == NULL)  
  139.     {  
  140.         LOGI("error in get char*");  
  141.         return;  
  142.     }  
  143.     captureFromImage(c_xml, c_file);  
  144.     env->ReleaseStringCritical(env, xml, c_xml);  
  145.     env->ReleaseStringCritical(env, file_name, c_file);  
  146.     */  
  147.     captureFromImage(jstring2String(env,xml), jstring2String(env,filename), jstring2String(env,outfile));   
  148.   
  149. }   
  150.   
  151. //jstring to char*   
  152.   
  153. char* jstring2String(JNIEnv* env, jstring jstr)   
  154. {   
  155.     if(jstr == NULL)   
  156.     {   
  157.         LOGI("NullPointerException!");   
  158.         return NULL;   
  159.     }   
  160.     char* rtn = NULL;   
  161.     jclass clsstring = env->FindClass("java/lang/String");   
  162.     jstring strencode = env->NewStringUTF("utf-8");   
  163.     jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");   
  164.     jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);   
  165.     jsize alen = env->GetArrayLength(barr);   
  166.     jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);   
  167.     if (alen > 0)   
  168.     {   
  169.         rtn = (char*)malloc(alen + 1);   
  170.         memcpy(rtn, ba, alen);   
  171.         rtn[alen] = 0;   
  172.     }   
  173.     env->ReleaseByteArrayElements(barr, ba, 0);   
  174.     LOGI("char*=%s",rtn);   
  175.     return rtn;   
  176. }  
#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

     publicvoid onCreate(Bundle savedInstanceState) {

         super.onCreate(savedInstanceState);

         setContentView(R.layout.main );

 

         finalFaceRec face = newFaceRec();

         newThread() {

              @Override

              publicvoid 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图片正确识别人脸,不过速度太慢了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值