Application of OpenCV in Android
Use Android NDK to compile the so Library
Introduction
Use OpenCV2.3 + NDK R6 to compile the OpenCV Face Detection application in the linuxt System
Preparation
Android NDK (r5 or later) http://developer.android.com/sdk/ndk/index.html
OpenCV Android pack http://sourceforge.net/projects/opencvlibrary/files/opencv-android/2.3/
Cmake (optional, alternative NDK) http://www.cmake.org/
Note: The http://code.google.com/p/android-opencv/ website says you want to use crystax ndk r4 instead of NDK. It may be necessary for older Android versions. If the NDK cannot be compiled, use crystax ndk r4 to compile it.
OpenCV settings
Download OpenCV 2.3.0 for Android from the website and decompress it to a directory, such ~ /Directory
Set OPENCV_PACKAGE_DIR environment variable
$ Export OPENCV_PACKAGE_DIR = ~ /EnCV-2.3.0/
Create an Android Project
Create an android project such as study. opencv in eclipse and create a directory named jni under the root directory of the project. Extract the downloaded android-ndk-r6 to a directory, such ~ /
SLAVE ~ /Android-ndk-r6/sample copy Android. mk, Application. mk to study. opencv/jni directory in a sample
Set the compilation script
In Android. mk, include $ (CLEAR_VARS) and add downstream
Include $ (OPENCV_PACKAGE_DIR)/$ (TARGET_ARCH_ABI)/share/opencv/OpenCV. mk
If the application supports arm neon, add the following lines:
Include $ (OPENCV_PACKAGE_DIR)/armeabi-v7a-neon/share/opencv/OpenCV. mk
LOCAL_ARM_NEON: = true
Add the following lines to Application. mk:
APP_STL: = gnustl_static
APP_CPPFLAGS: =-frtti-fexceptions
Note: For details about Android. mk and Application. mk, see android-mk.html and application-mk.html under ndk/docs.
Java layer defines native Interfaces
Create the study. opencv. FaceRec class and define a local interface for face detection.
/**
* 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 );
Generate jni header file
Use the javah command to generate the jni header file
$ Cd ~ /Workspace/study. opencv/bin
$ Javah study. opencv. FaceRec
A study_opencv_FaceRec.h file is generated in the bin directory. Copy the file to the./jni directory.
Note: If the API changes, manually delete the generated. h file. To prevent unexpected errors.
Image Face Detection on layer c
Use the text editor to create a facedetect. cpp in the jni directory to implement image face detection.
Cpp Code
# 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: cocould 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); // write the image to a file
}
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 }},
{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
Use NDK for compilation
Run ndk-build in the project jni directory
$ Cd ~ /Workspace/study. opencv/jni
$ ~ /Android-ndk-r6/ndk-build.
If the compilation is successful, the libs/armeabi/facedetect. so library will be generated under the project.
If any compilation failure occurs, modify the error as prompted.
Call JNI Interface
Use DDMS to push the xml file (located in the OpenCV-2.3.0/armeabi/share/opencv/haarcascades/directory) and image file to data/study. opencv/files.
Create a new thread in the activity and call the FaceRec # detect method.
@ 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/study. opencv/files/haarcascade_frontalface_alt2.xml ",
"/Data/study. opencv/files/wqw1.jpg ",
"/Data/study. opencv/files/wqw1_detected.jpg ");
}
}. Start ();
}
Running result
It was tested that correct face recognition for png, jpg, and bmp images was slow.