OpenCV for Android入门

        在上一篇转载的文章(http://blog.csdn.net/liudekuan/article/details/8569687)中,已经对OpenCV在Android环境的搭建进行了比较详细的说明,但文中所用版本为OpenCV2.3.1,与目前最新版OpenCV-2.4.3.2-Android-sdk稍有差异。本文将在新版基础上进行OpenCV4android入门级说明。

 

1.环境搭建

进行android开发所需要的环境一般为:eclipse + android sdk + ADT,而OpenCV的开发由于需要编写本地代码(C/C++),因此还需要安装以下工具:NDK,Cygwin,CDT。网上都有大量详细的安装讲 解,本文只描述下其中关键步骤。

1.1 NDK的安装

(1) NDK下载后解压到固定目录即可,无需安装。本文解压到D盘根目录下,其路径为:D:\android-ndk-r8d;

(2) 添加环境变量,将其安装路径添加到系统path变量中,并添加系统变量NDKROOT:D:\android-ndk-r8d。

1.2 Cygwin的安装

(1) 安装包当然可以选择全部,只是如此以来则比较耗时。你也可以只安装开发NDK用得着的包:autoconf2.1、automake1.10、 binutils、gcc-core、gcc-g++、gcc4-core、gcc4-g++、gdb、pcre、pcre-devel、gawk、 make;

(2) 将安装路径添加系统变量path中;

(3) 为了方便的在命令行下调用Android NDK,找到”C:\cygwin\home\(你的用户名)”这个目录,打开文件”.bash_profile”,在文件的最下面加上下面两行内容:

NDK=/cygdrive/f/android-ndk-r6b-windows/android-ndk-r6b

export NDK

1.3 CDT的安装

打开http://www.eclipse.org/cdt/downloads.php,找到对应的repository地址,注意这个地址对应的Eclipse版本要与第二步中你下载的版本一致。接着,打开Eclipse软件Help->Install New Software菜单安装即可。

 

2.OpenCV4Android

2.1 下载

进入官网(http://opencv.org/)下载OpenCV4Android并解压,其目录结构如下:

图1 OpenCV-2.4.3.2-android-sdk目录结构

        其中,sdk目录即是我们开发opencv所需要的类库;samples目录中存放着若干opencv应用示例(包括人脸检测等),可为我们进行 android下的opencv开发提供参考;doc目录为opencv类库的使用说明及api文档等;而apk目录则存放着对应于各内核版本的 OpenCV_2.4.3.2_Manager_2.4应用安装包。此应用用来管理手机设备中的opencv类库,在运行opencv应用之前,必须确保 手机中已经安装了OpenCV_2.4.3.2_Manager_2.4_*.apk,否则opencv应用将会因为无法加载opencv类库而无法运 行。

2.2 将SDK引入工作空间

(1) 选择一个路径,新建文件夹做为工作空间(我在E盘根目录下新建workspace目录来做为工作空间);

(2) 将OpenCV-2.4.3.2-android-sdk中的sdk目录copy至工作空间,并将其更名为OpenCV-SDK(是否更改名称无所谓,这是我个人习惯而已);

(3) 以新建的目录为工作空间,打开eclipse;

(4) 将OpenCV-SDK引入到工作空间中,如下图所示:

图2

图 3

图4

 

图5

3 开发实例

在经过上述的环境配置之后,就可以进行opencv开发了。如http://blog.csdn.net/liudekuan/article/details/8569687所述,android中opencv的开发有两种方式:直接调用opencv中的java api;利用JNI编写C++ OpenCV代码,通过Android NDK创建动态库。本文分别利用这两种方式实现图像的灰度处理操作。

3.1 工程一:通过调用OpenCV提供的java api实现灰度处理

3.1.1 创建工程

(1) 打开eclipse,创建android应用工程GrayProcess;

(2) 将测试图像lena.jpg添加到资源目录res/drawable-hdpi中;

(3) 在Package Explorer中选择项目GrayProcess,单击右键在弹出菜单中选择Properties,然后在弹出的Properties窗口中左侧选择 Android,然后点击右下方的Add按钮,选择OpenCV Library 2.4.3并点击OK,操作完成后,会将OpenCV类库添加到GrayProcess的Android Dependencies中,如下图所示:

图 6

图7

图8

3.1.2 工程代码

(1) 字符串资源文件:strings.xml

  1. <resources>
  2.     <string name=“app_name”>GrayProcess</string>
  3.     <string name=“hello_world”>Hello world!</string>
  4.     <string name=“menu_settings”>Settings</string>
  5.     <string name=“title_activity_main”>MainActivity</string>
  6.     <string name=“str_proc”>gray process</string>
  7.     <string name=“str_desc”>image description</string>
  8. </resources>

(2) 布局文件:main.xml

  1. <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”
  2.     xmlns:tools=“http://schemas.android.com/tools”
  3.     android:orientation=“vertical”
  4.     android:layout_width=“match_parent”
  5.     android:layout_height=“match_parent” >
  6.     <Button
  7.         android:id=“@+id/btn_gray_process”
  8.         android:layout_width=“fill_parent”
  9.         android:layout_height=“wrap_content”
  10.         android:text=“@string/str_proc”/>
  11.     <ImageView
  12.         android:id=“@+id/image_view”
  13.         android:layout_width=“wrap_content”
  14.         android:layout_height=“wrap_content”
  15.         android:contentDescription=“@string/str_proc”/>
  16. </LinearLayout>

(3) MainActivity.java

  1. package com.iron.grayprocess;
  2. import org.opencv.android.BaseLoaderCallback;
  3. import org.opencv.android.LoaderCallbackInterface;
  4. import org.opencv.android.OpenCVLoader;
  5. import org.opencv.android.Utils;
  6. import org.opencv.core.Mat;
  7. import org.opencv.imgproc.Imgproc;
  8. import android.os.Bundle;
  9. import android.app.Activity;
  10. import android.graphics.Bitmap;
  11. import android.graphics.BitmapFactory;
  12. import android.graphics.Bitmap.Config;
  13. import android.view.View;
  14. import android.view.View.OnClickListener;
  15. import android.widget.Button;
  16. import android.widget.ImageView;
  17. public class MainActivity extends Activity implements OnClickListener{
  18.     private Button btnProc;
  19.     private ImageView imageView;
  20.     private Bitmap bmp;
  21.     //OpenCV类库加载并初始化成功后的回调函数,在此我们不进行任何操作
  22.     private BaseLoaderCallback  mLoaderCallback = new BaseLoaderCallback(this) {
  23.         @Override
  24.         public void onManagerConnected(int status) {
  25.             switch (status) {
  26.                 case LoaderCallbackInterface.SUCCESS:{
  27.                 } break;
  28.                 default:{
  29.                     super.onManagerConnected(status);
  30.                 } break;
  31.             }
  32.         }
  33.     };
  34.     @Override
  35.     public void onCreate(Bundle savedInstanceState) {
  36.         super.onCreate(savedInstanceState);
  37.         setContentView(R.layout.main);
  38.         btnProc = (Button) findViewById(R.id.btn_gray_process);
  39.         imageView = (ImageView) findViewById(R.id.image_view);
  40.         //将lena图像加载程序中并进行显示
  41.         bmp = BitmapFactory.decodeResource(getResources(), R.drawable.lena);
  42.         imageView.setImageBitmap(bmp);
  43.         btnProc.setOnClickListener(this);
  44.     }
  45.     @Override
  46.     public void onClick(View v) {
  47.         Mat rgbMat = new Mat();
  48.         Mat grayMat = new Mat();
  49.         //获取lena彩色图像所对应的像素数据
  50.         Utils.bitmapToMat(bmp, rgbMat);
  51.         //将彩色图像数据转换为灰度图像数据并存储到grayMat中
  52.         Imgproc.cvtColor(rgbMat, grayMat, Imgproc.COLOR_RGB2GRAY);
  53.         //创建一个灰度图像
  54.         Bitmap grayBmp = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), Config.RGB_565);
  55.         //将矩阵grayMat转换为灰度图像
  56.         Utils.matToBitmap(grayMat, grayBmp);
  57.         imageView.setImageBitmap(grayBmp);
  58.     }
  59.     @Override
  60.     public void onResume(){
  61.         super.onResume();
  62.         //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是
  63.         //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存在于OpenCV安装包的apk目录中
  64.         OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this, mLoaderCallback);
  65.     }
  66. }

 

3.1.3 运行结果

       

图9

3.2 工程二:利用JNI编写C++ OpenCV代码实现灰度处理

3.2.1 创建工程

        步骤如工程一,创建新工程GrayProcess2,将lena.jpg添加到资源文件,并按3.1.1所示将opencv类库添加到工程中。

3.2.2 编写上层代码(java)

        (1) res/values/strings.xml

  1. <resources>
  2.     <string name=“app_name”>GrayProcess2</string>
  3.     <string name=“hello_world”>Hello world!</string>
  4.     <string name=“menu_settings”>Settings</string>
  5.     <string name=“title_activity_main”>GrayProcess2</string>
  6.     <string name=“str_proc”>gray process</string>
  7.     <string name=“str_desc”>image description</string>
  8. </resources>

        (2) res/layout/main.xml

  1. <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”
  2.     xmlns:tools=“http://schemas.android.com/tools”
  3.     android:orientation=“vertical”
  4.     android:layout_width=“match_parent”
  5.     android:layout_height=“match_parent” >
  6.     <Button
  7.         android:id=“@+id/btn_gray_process”
  8.         android:layout_width=“fill_parent”
  9.         android:layout_height=“wrap_content”
  10.         android:text=“@string/str_proc”/>
  11.     <ImageView
  12.         android:id=“@+id/image_view”
  13.         android:layout_width=“wrap_content”
  14.         android:layout_height=“wrap_content”
  15.         android:contentDescription=“@string/str_proc”/>
  16. </LinearLayout>

        (3)MainActivity.java

  1. package com.iron.grayprocess2;
  2. import org.opencv.android.BaseLoaderCallback;
  3. import org.opencv.android.LoaderCallbackInterface;
  4. import org.opencv.android.OpenCVLoader;
  5. import android.os.Bundle;
  6. import android.app.Activity;
  7. import android.graphics.Bitmap;
  8. import android.graphics.BitmapFactory;
  9. import android.graphics.Bitmap.Config;
  10. import android.view.View;
  11. import android.view.View.OnClickListener;
  12. import android.widget.Button;
  13. import android.widget.ImageView;
  14. public class MainActivity extends Activity implements OnClickListener{
  15.     private Button btnProc;
  16.     private ImageView imageView;
  17.     private Bitmap bmp;
  18.     //OpenCV类库加载并初始化成功后的回调函数,在此我们不进行任何操作
  19.      private BaseLoaderCallback  mLoaderCallback = new BaseLoaderCallback(this) {
  20.         @Override
  21.         public void onManagerConnected(int status) {
  22.             switch (status) {
  23.                 case LoaderCallbackInterface.SUCCESS:{
  24.                     System.loadLibrary(“image_proc”);
  25.                 } break;
  26.                 default:{
  27.                     super.onManagerConnected(status);
  28.                 } break;
  29.             }
  30.         }
  31.     };
  32.     @Override
  33.     public void onCreate(Bundle savedInstanceState) {
  34.         super.onCreate(savedInstanceState);
  35.         setContentView(R.layout.main);
  36.         btnProc = (Button) findViewById(R.id.btn_gray_process);
  37.         imageView = (ImageView) findViewById(R.id.image_view);
  38.         //将lena图像加载程序中并进行显示
  39.          bmp = BitmapFactory.decodeResource(getResources(), R.drawable.lena);
  40.         imageView.setImageBitmap(bmp);
  41.         btnProc.setOnClickListener(this);
  42.     }
  43.     @Override
  44.     public void onClick(View v) {
  45.         int w = bmp.getWidth();
  46.         int h = bmp.getHeight();
  47.         int[] pixels = new int[w*h];
  48.         bmp.getPixels(pixels, 0, w, 0, 0, w, h);
  49.         int[] resultInt = ImageProc.grayProc(pixels, w, h);
  50.         Bitmap resultImg = Bitmap.createBitmap(w, h, Config.ARGB_8888);
  51.         resultImg.setPixels(resultInt, 0, w, 0, 0, w, h);
  52.         imageView.setImageBitmap(resultImg);
  53.     }
  54.     @Override
  55.     public void onResume(){
  56.         super.onResume();
  57.         //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是
  58.         //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存在于OpenCV安装包的apk目录中
  59.         OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this, mLoaderCallback);
  60.     }
  61. }

        代码第28行:System.loadLibrary(“image_proc”)用来当OpenCV类库初始化完成后加载类库image_proc。此类库由我们来生成,用于完成图像灰度处理的操作,此部分将在3.2.3中说明。

         (4) ImageProc.java

  1. package com.iron.grayprocess2;
  2. public class ImageProc {
  3.     public static native int[] grayProc(int[] pixels, int w, int h);
  4. }

ImageProc.java中只定义了方法grayProc,关键字native表明,此方法的实现由本地代码(C/C++)来完成。

3.2.3 编写JNI及C相关代码

        在项目中新建目录jni,在jni目录中分别添加文件Android.mk,Application.mk,ImageProc.h,ImageProc.cpp,这四个文件的内容分别如下所示。

        (1) Android.mk

  1. LOCAL_PATH := $(call my-dir)
  2. include $(CLEAR_VARS)
  3. include ../OpenCV-SDK/native/jni/OpenCV.mk
  4. LOCAL_SRC_FILES  := ImageProc.cpp
  5. LOCAL_MODULE     := image_proc
  6. include $(BUILD_SHARED_LIBRARY)

代码说明:

第一行:指明当前编译路径;

第二行:清空变量;

第三行:将OpenCV类库中的编译文件包含进来,如此一来在我们的工程中即可使用OpenCV类库;

第四行:指定需要编译的C++源文件;

第五行:指定编译生成的类库名称;

第六行:调用命令将源文件编译为静态库。

注:第三行指定的路径很关键,当opencv类库与工程路径相关位置发生改变时,此路径也要随之改变。

        (2) Application.mk(配置文件)

  1. APP_STL := gnustl_static
  2. APP_CPPFLAGS := -frtti -fexceptions
  3. APP_ABI := armeabi-v7a
  4. APP_PLATFORM := android-8

        (3) ImageProc.h

  1. #include <jni.h>
  2. extern “C” {
  3. JNIEXPORT jintArray JNICALL Java_com_iron_grayprocess2_ImageProc_grayProc
  4.   (JNIEnv *, jclass, jintArray, jint, jint);
  5. }

(4) ImageProc.cpp

  1. #include <ImageProc.h>
  2. #include <opencv2/core/core.hpp>
  3. #include <string>
  4. #include <vector>
  5. using namespace cv;
  6. using namespace std;
  7. JNIEXPORT jintArray JNICALL Java_com_iron_grayprocess2_ImageProc_grayProc(JNIEnv* env, jclass obj, jintArray buf, jint w, jint h){
  8.     jint *cbuf;
  9.     cbuf = env->GetIntArrayElements(buf, false);
  10.     if(cbuf == NULL){
  11.         return 0;
  12.     }
  13.     Mat imgData(h, w, CV_8UC4, (unsigned char*)cbuf);
  14.     uchar* ptr = imgData.ptr(0);
  15.     for(int i = 0; i < w*h; i ++){
  16.         //计算公式:Y(亮度) = 0.299*R + 0.587*G + 0.114*B
  17.         //对于一个int四字节,其彩色值存储方式为:BGRA
  18.         int grayScale = (int)(ptr[4*i+2]*0.299 + ptr[4*i+1]*0.587 + ptr[4*i+0]*0.114);
  19.         ptr[4*i+1] = grayScale;
  20.         ptr[4*i+2] = grayScale;
  21.         ptr[4*i+0] = grayScale;
  22.     }
  23.     int size=w * h;
  24.     jintArray result = env->NewIntArray(size);
  25.     env->SetIntArrayRegion(result, 0, size, cbuf);
  26.     env->ReleaseIntArrayElements(buf, cbuf, 0);
  27.     return result;
  28. }

说明:

  •         ImageProc.h头文件可以通过jdk提供的工具javah来生成,具体使用说明请参考相关文档,本文为演示自己编写了此文件;
  •         JNI中的定义的函数遵循一定的命名规则:Java_包名_类名_方法名,具体参考JNI编程相关知识;
  •         ImageProc.cpp中利用彩色值转换为灰度值的计算公式,将lena.jpg图像的每个像素转换为灰度值,并返回给应用层。需要注意的是对于ARGB_8888类型的图像而言,其每一个像素值在int型数据中的存储序列为BGRA。

3.2.4 运行

        由于程序中涉及到了JNI编程,因此需要用cygwin对其中的C/C++代码进行编译。打开cygwin,进入到工程的根目录中执行命令:$NDK /ndk-build完成相关编译;之后在eclipse中刷新工程GrayProcess2,运行即可。编译及运行结果分别如下所示。

图10 使用cygwin对C代码进行编译

 

图11 程序运行结果图对比

版权声明:本文为博主原创文章,未经博主允许不得转载。

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

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

支付宝扫一扫打赏

微信扫一扫打赏