Android中使用OpenCV

  1. http://underthehood.blog.51cto.com/2531780/670169  (利用JNI编写C++ OpenCV代码,通过Android NDK创建动态库(.so))
  2. 2.利用JNI编写C++ OpenCV代码,通过Android NDK创建动态库(.so)

    新建一个工作空间,例如"TestOpenCV",在Window->Preferences中设置好Android SDK的路径,如下图所示。

    wps_clip_image-1955

    然后新建一个Android项目,Build Target选择Android2.2,命名为"HaveImgFun",活动名改为HaveImgFun,Package name中填写com.testopencv.haveimgfun,最后点击finish。

    如同使用OpenCV Java API那样,将OpenCV-2.3.1文件夹拷贝到与工作空间同一级目录中;另外,将"F:\OpenCV-2.3.1-android-bin\samples"下的includeOpenCV.mk文件拷贝到和项目HaveImgFun同一级目录中,如下图所示:

    wps_clip_image-30013

    (上面这个各个文件夹和文件的放置很重要,因为OpenCV-2.3.1下的OpenCV.mk中有很多相对路径的指定,如果不是这样放置,在NDK生成动态库时可能会报文件或文件夹无法找到的错误)

    选择Package Explorer中你的项目,右键选择new->folder,新建一个名为jni的文件夹,用来存放你的c/c++代码。

    然后把res->layout下的main.xml的内容改为下面所示:

       
       
    1. <?xml version="1.0" encoding="utf-8"?> 
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    3.     android:orientation="vertical" 
    4.     android:layout_width="fill_parent" 
    5.     android:layout_height="fill_parent" 
    6.     > 
    7.     <Button android:layout_height="wrap_content"  
    8.         android:layout_width="fill_parent"  
    9.         android:id="@+id/btnNDK"  
    10.         android:text="使用C++ OpenCV进行处理" /> 
    11.     <Button android:layout_height="wrap_content"  
    12.         android:layout_width="fill_parent"  
    13.         android:id="@+id/btnRestore"  
    14.         android:text="还原" />  
    15.     <ImageView android:id="@+id/ImageView01"  
    16.     android:layout_width="fill_parent"  
    17.     android:layout_height="fill_parent" />  
    18.      
    19. </LinearLayout> 

    上面的代码就是一个线性布局里面包含2个按钮加上一个显示图像的ImageView

    在文件夹src下的com.testopencv.haveimgfun包中新建一个类用于包装使用了opencv c++代码的动态库的导出函数,类名为LibImgFun。

    Eclipse会为你创建一个新的文件LibImgFun.java,将里面的内容改为:

       
       
    1. package com.testopencv.haveimgfun; 
    2.  
    3. public class LibImgFun {  
    4.  
    5. static {  
    6.  
    7.         System.loadLibrary("ImgFun");  
    8.  
    9.     }  
    10.  
    11. /** 
    12.  
    13.     * @param width the current view width 
    14.  
    15.     * @param height the current view height 
    16.  
    17.     */ 
    18.  
    19. public static native int[] ImgFun(int[] buf, int w, int h);  
    20.  
    21. }  

    从上面的代码可以得知,我们的动态库名字应该为“libImgFun.so”,注意"public static native int[] ImgFun(int[] buf, int w, int h)"中的native关键字,表明这个函数来自native code。static表示这是一个静态函数,这样就可以直接用类名去调用。

    在jni文件夹下建立一个"ImgFun.cpp"的文件,内容改为下面所示:

       
       
    1. #include <jni.h> 
    2.  
    3. #include <stdio.h> 
    4.  
    5. #include <stdlib.h> 
    6.  
    7. #include <opencv2/opencv.hpp> 
    8.  
    9. using namespace cv; 
    10.  
    11. extern "C" { 
    12.  
    13. JNIEXPORT jintArray JNICALL Java_com_testopencv_haveimgfun_LibImgFun_ImgFun( 
    14.  
    15.         JNIEnv* env, jobject obj, jintArray buf, int w, int h); 
    16.  
    17. JNIEXPORT jintArray JNICALL Java_com_testopencv_haveimgfun_LibImgFun_ImgFun( 
    18.  
    19.         JNIEnv* env, jobject obj, jintArray buf, int w, int h){ 
    20.  
    21. jint *cbuf; 
    22.  
    23. cbuf = env->GetIntArrayElements(buf, false); 
    24.  
    25. if(cbuf == NULL) 
    26.  
    27.  
    28. return 0; 
    29.  
    30.  
    31. Mat myimg(h, w, CV_8UC4, (unsigned char*)cbuf); 
    32.  
    33. for(int j=0;j<myimg.rows/2;j++) 
    34.  
    35.  
    36. myimg.row(j).setTo(Scalar(0,0,0,0)); 
    37.  
    38.  
    39. int size=w * h; 
    40.  
    41. jintArray result = env->NewIntArray(size); 
    42.  
    43. env->SetIntArrayRegion(result, 0, size, cbuf); 
    44.  
    45. env->ReleaseIntArrayElements(buf, cbuf, 0); 
    46.  
    47. return result; 
    48.  
    49.  

    上面的代码中#include <jni.h>是必须要包含的头文件,#include <opencv2/opencv.hpp>是opencv要包含的头文件。

    动态库要导出的函数如下声明:

    JNIEXPORT jintArray JNICALL Java_com_testopencv_haveimgfun_LibImgFun_ImgFun(

            JNIEnv* env, jobject obj, jintArray buf, int w, int h);

    JNIEXPORT 和JNICALL是必须要加的关键字

    jintArray就是int[],这里返回类型要么为空,要么为jni中定义的类型,事实上就是C\C++类型前面加上j,如果是数组,则在后面加上Array。

    函数名的命名规则如下:

    Java_(包路径)_(类名)_(函数名) (JNIEnv *env, jobject obj, 自己定义的参数...)

    包路径中的"."用"_"(下划线)代替,类名就是上面包装该动态库函数的类的名字,最后一个才是真正的函数名;JNIEnv *env和jobject obj这两个参数时必须的,用来调用JNI环境下的一些函数;后面就是你自己定义的参数。在这里,jintArray buf代表了传进来的图像的数据,int w是图像的宽,int h是图像的高。

    这个函数的功能是将传进来的图像的上半部分涂成黑色。

    然后再在jni下新建两个文件"Android.mk"文件和"Application.mk"文件,这两个文件事实上就是简单的Makefile文件。

    其中将Android.mk的内容改为如下所示:

       
       
    1. LOCAL_PATH := $(call my-dir) 
    2.  
    3. include $(CLEAR_VARS) 
    4.  
    5. include ../includeOpenCV.mk 
    6.  
    7. ifeq ("$(wildcard $(OPENCV_MK_PATH))",""
    8.  
    9. #try to load OpenCV.mk from default install location 
    10.  
    11. include $(TOOLCHAIN_PREBUILT_ROOT)/user/share/OpenCV/OpenCV.mk 
    12.  
    13. else 
    14.  
    15. include $(OPENCV_MK_PATH) 
    16.  
    17. endif 
    18.  
    19. LOCAL_MODULE    := ImgFun 
    20.  
    21. LOCAL_SRC_FILES := ImgFun.cpp 
    22.  
    23. include $(BUILD_SHARED_LIBRARY) 

    Application.mk的内容改为如下所示:

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

    其中APP_ABI指定的是目标平台的CPU架构。(经过很多测试,android2.2必须指定为armeabi,android2.2以上的使用armeabi-v7a,如果没有设置对,很有可能安装到android虚拟机失败,当然你同时如上面写上也是可以的)

    上面的步骤完成后,就可以使用NDK生成动态库了,打开cygwin,cd到项目目录下,如下图所示:

    wps_clip_image-26301

    输入$NDK/ndk-build命令,开始创建动态库。成功的话如下图所示。

    wps_clip_image-7682

    这时候刷新Eclipse的Package Explorer会出现两个新的文件夹obj和libs。

    现在,只剩最后一步完成这个测试程序。

    将一张图片,例如"lena.jpg"放到项目res->drawable-hdpi目录中并刷新该目录。

    然后将HaveImgFun.java的内容改为下面所示:

     

       
       
    1. package com.testopencv.haveimgfun; 
    2.  
    3. import android.app.Activity;  
    4.  
    5. import android.graphics.Bitmap;  
    6.  
    7. import android.graphics.Bitmap.Config;  
    8.  
    9. import android.graphics.drawable.BitmapDrawable;  
    10.  
    11. import android.os.Bundle;  
    12.  
    13. import android.widget.Button; 
    14.  
    15. import android.view.View;  
    16.  
    17. import android.widget.ImageView;  
    18.  
    19. public class HaveImgFun extends Activity { 
    20.  
    21. /** Called when the activity is first created. */ 
    22.  
    23. ImageView imgView; 
    24.  
    25. Button btnNDK, btnRestore; 
    26.  
    27. @Override 
    28.  
    29. public void onCreate(Bundle savedInstanceState) { 
    30.  
    31. super.onCreate(savedInstanceState);  
    32.  
    33.         setContentView(R.layout.main);  
    34.  
    35. this.setTitle("使用NDK转换灰度图");  
    36.  
    37. btnRestore=(Button)this.findViewById(R.id.btnRestore);  
    38.  
    39. btnRestore.setOnClickListener(new ClickEvent());  
    40.  
    41. btnNDK=(Button)this.findViewById(R.id.btnNDK);  
    42.  
    43. btnNDK.setOnClickListener(new ClickEvent());  
    44.  
    45. imgView=(ImageView)this.findViewById(R.id.ImageView01); 
    46.  
    47.         Bitmap img=((BitmapDrawable) getResources().getDrawable(R.drawable.lena)).getBitmap(); 
    48.  
    49. imgView.setImageBitmap(img); 
    50.  
    51.     } 
    52.  
    53. class ClickEvent implements View.OnClickListener{ 
    54.  
    55. public void onClick(View v){ 
    56.  
    57. if(v == btnNDK)  
    58.  
    59.             {  
    60.  
    61.  
    62. long current=System.currentTimeMillis();  
    63.  
    64. Bitmap img1=((BitmapDrawable) getResources().getDrawable(R.drawable.lena)).getBitmap();  
    65.  
    66. int w=img1.getWidth(),h=img1.getHeight();  
    67.  
    68. int[] pix = new int[w * h];  
    69.  
    70. img1.getPixels(pix, 0, w, 00, w, h);  
    71.  
    72. int[] resultInt=LibImgFun.ImgFun(pix, w, h);  
    73.  
    74. Bitmap resultImg=Bitmap.createBitmap(w, h, Config.RGB_565);  
    75.  
    76. resultImg.setPixels(resultInt, 0, w, 00,w, h);  
    77.  
    78. long performance=System.currentTimeMillis()-current;  
    79.  
    80. imgView.setImageBitmap(resultImg);  
    81.  
    82. HaveImgFun.this.setTitle("w:"+String.valueOf(img1.getWidth())+",h:"+String.valueOf(img1.getHeight())  
    83.  
    84.                          +" NDK耗时 "+String.valueOf(performance)+" 毫秒");  
    85.  
    86.             }  
    87.  
    88. else if(v == btnRestore) 
    89.  
    90.  
    91.         Bitmap img2=((BitmapDrawable) getResources().getDrawable(R.drawable.lena)).getBitmap();  
    92.  
    93. imgView.setImageBitmap(img2); 
    94.  
    95.         HaveImgFun.this.setTitle("使用OpenCV进行图像处理"); 
    96.  
    97.  
    98.  
    99.     } 
    100.  

    点击全部保存,OK,现在可以选择一个Android虚拟机运行看一下效果,配置好Run Configuration然后点击Run,得到下面的结果:

    wps_clip_image-25816

    点击使用C++ OpenCV进行处理,得到下面的结果:

    wps_clip_image-23188

    本文出自 “UnderTheHood” 博客,请务必保留此出处http://underthehood.blog.51cto.com/2531780/670169

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值