Android上使用OpenCV处理图像

        本文会介绍在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。

testpic1
image-526

在项目目录里新建一个名为“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函数进行图像处理。

项目结构大致如图:

jni-00
image-527

打开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。

h-file
image-528

在Eclipse内点击Window->Preferences->C/C++->Build->Environment,增加一个环境变量:

变量名
NDKROOT D:\android-ndk

ndkroot
image-529

右击项目名称,点击New->Other->C/C++->Convert to a C/C++ Project (Adds C/C++ Nature)。

jni-01
image-530

选择C++ Project,工具链选择Makefile的Other Toolchain(由于我的项目已经转换过,所以没有显示在项目名称列表里)。

jni-02
image-531

转换完成后会提示是否切换到C/C++视图,选择不切换。

此时在代码文件内会有大量的错误,这是由于还没有包含进所需的C++库文件。

右击项目名称,点击Refresh。

再右击项目名称,点击Properties->Android,选择合适的Android API版本,点击Library中的Add,选择OpenCV Library。

jni-03
image-532

点击C/C++ Build,把Builder Settings中的Build Command改为:

${NDKROOT}/ndk-build.cmd

jni-04
image-533

Behaviour的设置如图:

jni-05
image-534

点击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

jni-06
image-535

这里的路径最好用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文件拷贝到手机里,手动安装,运行。

运行效果如下,点击按钮,图片变成灰色:

gray-process-1
image-536

gray-process-2
image-537

这里有另一个使用OpenCV的例子,支持的图像处理方法更多,可以切换图像,还可以用手指选择要作处理的区域:

https://github.com/johnhany/AndroidProj/tree/master/ImageProcess

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏