本文会介绍在Android项目中使用OpenCV的一种方法,并会给出两个demo。
文中涉及开发环境在《Windows下Android开发环境的配置》的基础之上进行搭建。
在http://sourceforge.net/projects/opencvlibrary/files/opencv-android/2.4.8/下载OpenCV-2.4.8-Android-sdk.zip。假定解压到目录D:\workspace\opencv-android-sdk。
打开Eclipse,点击 File->Import->General->Existing Projects into Workspace,找到D:\workspace\opencv-android-sdk,选择导入OpenCV Library,也可以再导入一两个sample,但最好不要全部导入,因为如果设置了自动编译,会降低每次打开Eclipse的速度。
导入完成后,右击OpenCV Library,点击Properties->Android,选择合适的Build Project Target,即Android的API版本。
可以尝试运行一下OpenCV sample项目,检查一下设置是否正确。
仿照文章《Windows下Android开发环境的配置》的步骤创建一个新的Android项目。假定包名为net.johnhany.grayprocessjni。所需添加的文件及代码如下所示:
GrayProcess.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
package net.johnhany.grayprocessjni; import org.opencv.android.BaseLoaderCallback; import org.opencv.android.LoaderCallbackInterface; import org.opencv.android.OpenCVLoader; import android.os.Bundle; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Bitmap.Config; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ImageView; public class GrayProcess extends Activity implements OnClickListener{ private Button btnProc; private ImageView imageView; private Bitmap bmp; private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback( this ) { @Override public void onManagerConnected( int status) { switch (status) { case LoaderCallbackInterface.SUCCESS:{ System.loadLibrary( "image_proc" ); } break ; default :{ super .onManagerConnected(status); } break ; } } }; @Override public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_gray_process); btnProc = (Button) findViewById(R.id.btn_gray_process); imageView = (ImageView) findViewById(R.id.image_view); bmp = BitmapFactory.decodeResource(getResources(), R.drawable.testpic1); imageView.setImageBitmap(bmp); btnProc.setOnClickListener( this ); } @Override public void onClick(View v) { int w = bmp.getWidth(); int h = bmp.getHeight(); int [] pixels = new int [w*h]; bmp.getPixels(pixels, 0 , w, 0 , 0 , w, h); int [] resultInt = ImageProc.grayProc(pixels, w, h); Bitmap resultImg = Bitmap.createBitmap(w, h, Config.ARGB_8888); resultImg.setPixels(resultInt, 0 , w, 0 , 0 , w, h); imageView.setImageBitmap(resultImg); } @Override public void onResume(){ super .onResume(); OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_8, this , mLoaderCallback); } } |
ImageProcess.java
1
2
3
4
5
|
package net.johnhany.grayprocessjni; public class ImageProc { public static native int [] grayProc( int [] pixels, int w, int h); } |
activity_gray_process.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
< LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android" xmlns:tools = "http://schemas.android.com/tools" xmlns:opencv = "http://schemas.android.com/apk/res-auto" android:orientation = "vertical" android:layout_width = "match_parent" android:layout_height = "match_parent" > < Button android:id = "@+id/btn_gray_process" android:layout_width = "fill_parent" android:layout_height = "wrap_content" android:text = "@string/str_proc" /> < ImageView android:id = "@+id/image_view" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:contentDescription = "@string/str_proc" /> </ LinearLayout > |
res\values\strings.xml
1
2
3
4
5
6
7
8
9
|
<? xml version = "1.0" encoding = "utf-8" ?> < resources > < string name = "app_name" >GrayProcessJNI</ string > < string name = "action_settings" >Settings</ string > < string name = "str_proc" >gray process</ string > < string name = "str_desc" >image description</ string > </ resources > |
把一张图片拷贝到tes\drawable-hdpi,假设图片名称为testpic1。
在项目目录里新建一个名为“jni”的文件夹,里面添加如下3个文件:
Android.mk
1
2
3
4
5
6
7
|
LOCAL_PATH := $(call my- dir ) include $(CLEAR_VARS) include D:\workspace\opencv-android-sdk\sdk\native\jni\OpenCV.mk LOCAL_SRC_FILES := ImageProc.cpp LOCAL_MODULE := image_proc include $(BUILD_SHARED_LIBRARY) |
这个文件负责把cpp或c文件编译成可以被Android程序调用的.so库.
Application.mk
1
2
3
4
|
APP_STL := gnustl_static APP_CPPFLAGS := -frtti -fexceptions APP_ABI := armeabi-v7a APP_PLATFORM := android-15 |
ImageProc.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
#include <ImageProc.h> #include <opencv2/core/core.hpp> #include <string> #include <vector> using namespace cv; using namespace std; JNIEXPORT jintArray JNICALL Java_net_johnhany_grayprocessjni_ImageProc_grayProc(JNIEnv* env, jclass obj, jintArray buf, jint w, jint h){ jint *cbuf; cbuf = env->GetIntArrayElements(buf, false ); if (cbuf == NULL){ return 0; } Mat imgData(h, w, CV_8UC4, (unsigned char *)cbuf); uchar* ptr = imgData.ptr(0); for ( int i = 0; i < w*h; i ++){ int grayScale = ( int )(ptr[4*i+2]*0.299 + ptr[4*i+1]*0.587 + ptr[4*i+0]*0.114); ptr[4*i+1] = grayScale; ptr[4*i+2] = grayScale; ptr[4*i+0] = grayScale; } int size=w * h; jintArray result = env->NewIntArray(size); env->SetIntArrayRegion(result, 0, size, cbuf); env->ReleaseIntArrayElements(buf, cbuf, 0); return result; } |
这个cpp文件负责调用OpenCV函数进行图像处理。
项目结构大致如图:
打开cmd.exe,输入:
1
2
3
|
cd D:\workspace\GrayProcessJni\bin\classes D: javah net.johnhany.grayprocessjni.ImageProc |
此时classes文件夹内会多出一个net_johnhany_grayprocessjni_ImageProc.h文件,把它拷贝到jni文件夹内,并把名字改为ImageProc.h。
在Eclipse内点击Window->Preferences->C/C++->Build->Environment,增加一个环境变量:
变量名 | 值 |
NDKROOT | D:\android-ndk |
右击项目名称,点击New->Other->C/C++->Convert to a C/C++ Project (Adds C/C++ Nature)。
选择C++ Project,工具链选择Makefile的Other Toolchain(由于我的项目已经转换过,所以没有显示在项目名称列表里)。
转换完成后会提示是否切换到C/C++视图,选择不切换。
此时在代码文件内会有大量的错误,这是由于还没有包含进所需的C++库文件。
右击项目名称,点击Refresh。
再右击项目名称,点击Properties->Android,选择合适的Android API版本,点击Library中的Add,选择OpenCV Library。
点击C/C++ Build,把Builder Settings中的Build Command改为:
${NDKROOT}/ndk-build.cmd
Behaviour的设置如图:
点击C/C++ General->Paths and Symbols->Includes,在GNU C++中添加如下路径:
${NDKROOT}/platforms/android-15/arch-arm/usr/include
${NDKROOT}/sources/cxx-stl/gnu-libstdc++/4.6/include
${NDKROOT}/sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi-v7a/include
${ProjDirPath}/../OpenCV-2.4.8-android-sdk/sdk/native/jni/include
这里的路径最好用NDKROOT或ProjDirPath的相对路径表示,如果用带有盘符的绝对路径可能会由于编译工具和Windows系统的表示方式不同而造成无法编译(如Cygwin)。
还要在安卓设备上安装OpenCV Manager。如果使用的是模拟器,打开cmd.exe,输入:
1
2
|
cd D:\android-sdk\platform-tools adb install D:\workspace\opencv-android-sdk\apk\OpenCV_2.4.8_Manager_2.16_armv7a-neon.apk |
如果使用的是手机,把D:\workspace\opencv-android-sdk\apk目录下的OpenCV_2.4.8_Manager_2.16_armv7a-neon.apk拷贝到手机中,在手机中手动安装。
点击Run,会自动完成编译、打开模拟器、安装、启动运行等步骤;或者在编译之后把bin文件夹内的apk文件拷贝到手机里,手动安装,运行。
运行效果如下,点击按钮,图片变成灰色:
这里有另一个使用OpenCV的例子,支持的图像处理方法更多,可以切换图像,还可以用手指选择要作处理的区域:
https://github.com/johnhany/AndroidProj/tree/master/ImageProcess