看了网上的很多教程和官方http://opencv.willowgarage.com/wiki/Android提供的如何在Android上使用OpenCV的教程,照着一步一步的做最后总有些问题,不是APK安装失败就是运行时突然报错退出。和同学一起摸索了一段时间后,终于弄成功,在这里做一个总结。最关键的问题是项目中各个文件夹和文件的位置要放置正确,而且目标机器的CPU架构要设置正确,下面是配置的详细过程。

一、Android开发环境

1.Sun JDK 6

访问http://www.oracle.com/technetwork/java/javase/downloads/index.html这里并且安装好JDK

注意:不要使用OpenJDK,Android SDK支持Sun JDK

2.Android SDK

访问http://developer.android.com/sdk/index.html获取android sdk,如果选择的是Windows安装文件,则你还需要安装32bit JRE。

3.Android SDK组件

l Android SDK Tools, revision 12或者更新

l SDK平台Android 2.2, API 8, revision 2(also known as Java API)

这是OpenCV Java API支持的最低平台,OpenCV发布默认为Android 2.2

4. Eclipse IDE和ADT plugin for Eclipse

访问http://www.eclipse.org/downloads/下载Eclipse并解压即可。

打开Eclipse,选择Help->Install New Software菜单,但后点击Add按钮,在Add Repository对话框中的Name一栏输入"ADT Plugin",Location一栏输入https://dl-ssl.google.com/android/eclipse/,但后点击OK。在Available Software对话框中选中所有单选框,然后一路next直到finish为止,当安装ADT完毕后重启Eclipse即可。

5. Android NDK

访问http://developer.android.com/sdk/ndk/index.html 下载最新的Android NDK,是一个ZIP解压包,只需解压到某个路径即可,例如"F:\android-ndk-r6b-windows\android-ndk-r6b",再把这个路径添加到系统的环境变量PATH中。

6. Cygwin

访问http://cygwin.com/index.html下载最新的Cygwin,最好安装全部的Cygwin组件。假设安装在"C:\cygwin"下,将"C:\cygwin\bin"添加到系统环境变量PATH中,为了方便的在命令行下调用Android NDK,找到"C:\cygwin\home\(你的用户名)"这个目录,打开文件".bash_profile",在文件的最下面加上下面两行内容:

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

export NDK

这样便可以在命令行中以 "$NDK/ndk-build" 这种形式调用NDK了。

二、OpenCV

1.首先下载在http://sourceforge.net/projects/opencvlibrary/files/opencv-android/2.3.1/ 已经预编译好的opencv包。

2.把下载好的包解压到某个路径上(最好不要带空格),例如"F:\OpenCV-2.3.1-android-bin"

三、如何在Android程序中使用OpenCV

有两种方式(重点讲后面一种):

1.使用OpenCV Java API。

OpenCV安装路径"F:\OpenCV-2.3.1-android-bin"下有两个文件夹,如下图

将文件夹"OpenCV-2.3.1"拷贝到你的Eclipse工作空间所在的目录,也就是在你的项目的上一级目录中,然后导入到工作空间中,在Package Explorer中选择你的项目,单机右键在弹出菜单中选择Properties,然后在弹出的Properties窗口中左侧选择Android,然后点击右下方的Add按钮,选择OpenCV-2.3.1并点击OK,如下图:

此时,展开你的项目树,你可以看到新加了一个OpenCV-2.3.1_src目录,如下图,那么就是正确添加了OpenCV Java API,否则就是你放置OpenCV-2.3.1的目录路径不正确。

然后就可以在你的Java源文件中导入OpenCV的API包,并且使用OpenCV API了,OpenCV API的包的形式如下:

Org.opencv.(OpenCV模块名).(OpenCV类名)

例如:

Org.opencv.core.Mat

2.利用JNI编写C++ OpenCV代码,通过Android NDK创建动态库(.so)

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

然后新建一个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同一级目录中,如下图所示:

(上面这个各个文件夹和文件的放置很重要,因为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 </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   

从上面的代码可以得知,我们的动态库名字应该为“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  
29 return 0; 
30  
31 } 
32  
33 Mat myimg(h, w, CV_8UC4, (unsigned char*)cbuf); 
34  
35 for(int j=0;j<myimg.rows/2;j++) 
36  
37 { 
38  
39 myimg.row(j).setTo(Scalar(0,0,0,0)); 
40  
41 } 
42  
43 int size=w * h; 
44  
45 jintArray result = env->NewIntArray(size); 
46  
47 env->SetIntArrayRegion(result, 0, size, cbuf); 
48  
49 env->ReleaseIntArrayElements(buf, cbuf, 0); 
50  
51 return result; 
52  }
53 }  

上面的代码中#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 include $(BUILD_SHARED_LIBRARY)

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

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

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

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

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

这时候刷新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, 0, 0, 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, 0, 0,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  
 92         Bitmap img2=((BitmapDrawable) getResources().getDrawable(R.drawable.lena)).getBitmap();  
 93  
 94 imgView.setImageBitmap(img2); 
 95  
 96         HaveImgFun.this.setTitle("使用OpenCV进行图像处理"); 
 97  
 98 } 
 99  
100 } 
101  
102     } 
103  

104 }

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

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

trackback:http://underthehood.blog.51cto.com/2531780/670169

注:

(0) 感谢RaySaint的详细教程,上述过程均在ubuntu 11.04上测试并通过, target为Android 4.0.3。

(1)OpenCV-2.3.1文件夹和includeOpenCV.mk也可以放在别的路径下,只要在Android.mk文件中试用对应路径即可;

(2)Android.mk中

1 ifeq ("$(wildcard $(OPENCV_MK_PATH))","") 
2 #try to load OpenCV.mk from default install location  
3 include $(TOOLCHAIN_PREBUILT_ROOT)/user/share/OpenCV/OpenCV.mk 
4 else 
5 include $(OPENCV_MK_PATH) 

6 endif

可直接修改为 include=/usr/point/to/OpenCV.mk

(3) 执行ndk-build命令时,可能会出现“No rule to make target".

solution:Android.mk文件中存在一些不必要的空格,删除即可。

(4)没有Application.mk也可以正常运行。

(5)OpenCV for android (OpenCV-2.3.1-android-bin.tar.bz2)

(6)更多参考:http://opencv.itseez.com/doc/tutorials/introduction/android_binary_package/android_binary_package.html

转载于:https://www.cnblogs.com/JohnShao/archive/2012/03/26/2417326.html

如何在Android中使用OpenCV相关推荐

  1. 如何在android中创建自定义对话框?

    本文翻译自:How to create a Custom Dialog box in android? I want to create a custom dialog box like below ...

  2. android 定时器5秒执行一次,如何在android中每30秒执行一次查询?

    我有一个查询,我想每30秒执行一次并将其记录到Logcat.我是通过处理程序完成的,我没有得到回应.如何在android中每30秒执行一次查询? 这里是我的代码: runnable = new Run ...

  3. 让一个图片填满一个控件_如何在Android中实现一个全景图控件(二)

    一.背景 在 如何在Android中实现一个全景图控件(一)中,介绍了项目的一些基本情况(有 demo 演示),如果项目对你有帮助,希望文章赏个赞,项目 star 一下. 项目地址:https://g ...

  4. android view gesturedetector,如何在Android中利用 GestureDetector进行手势检测

    如何在Android中利用 GestureDetector进行手势检测 发布时间:2020-11-26 16:15:21 来源:亿速云 阅读:92 作者:Leah 今天就跟大家聊聊有关如何在Andro ...

  5. android开发 转跳功能,如何在Android中利用Intent实现一个页面跳转功能

    如何在Android中利用Intent实现一个页面跳转功能 发布时间:2021-02-20 17:06:31 来源:亿速云 阅读:113 作者:Leah 本篇文章为大家展示了如何在Android中利用 ...

  6. android中fragment如何保存edittext文本,如何在Android中使用DialogFragment进行文本输入?...

    我想获得一个值,用户输入到一个对话框,使用建议的DialogFragment类为它,对话框构造和运行良好,但我不能返回EditText参数的值父类,没有得到一个空值指针异常.如何在Android中使用 ...

  7. 如何在Android中使用Intent拨打电话?

    本文翻译自:How to make a phone call using intent in Android? I'm using the following code to make a call ...

  8. android打电话的intent,如何在Android中使用intent打电话?

    如何在Android中使用intent打电话? 我正在使用以下代码在Android中进行调用,但它给了我安全例外,请帮忙. posted_by = "111-333-222-4"; ...

  9. android 显示进度,progressdialog-如何在Android中显示进度对话框?

    progressdialog-如何在Android中显示进度对话框? 当我单击"登录"按钮时,我想显示ProgressDialog,这需要时间才能移动到另一个页面. 我怎样才能做到 ...

最新文章

  1. Linux中errno使用
  2. 【OpenCV 4开发详解】漫水填充法
  3. 多通道图像的通道分享和合成函数-split、merge
  4. 敏捷的项目启动-尽早启动!
  5. 白话中台战略:中台是个什么鬼?
  6. 别人运行的好好的R代码,到我这怎么就冲突了?
  7. futuretask java 并发请求_Java并发编程:Callable、Future和FutureTask
  8. angular--Observable总结
  9. REACT是否真的就比VUE强?(文末附两个框架的学习福利)
  10. ACM图论在线画图工具分享 【csacadmy app Graph Editor】
  11. 重做raid后,重启无法进入系统
  12. 使用经验-深度学习云服务器mist GPU
  13. python学习-06(模块化编程)
  14. 如何一次打开多个Word文档
  15. 山东python编程培训,Python进入山东省小学教材了
  16. 部署apollo-client到maven私服上时遇到的问题及排查过程
  17. java前端页面调用webservice_一个简单的Webservice的demo(中)_前端页面调用
  18. 华为服务器1u系列,1u服务器
  19. 每日一道leetcode(python)695. 岛屿的最大面积
  20. 婚后10年 她早已出轨 妻子的出轨全文5200

热门文章

  1. spring的定时任务schedule
  2. strip(),replace()和re.sub()用法
  3. 原生DOM选择器querySelector和querySelectorAll
  4. linux中的特殊文件权限
  5. java 蓝桥杯算法训练 P0501
  6. java反射数组_java反射Array的使用
  7. python coroutine,go routine对比--理解多线程、协程
  8. 像目标主机一样的tcp流重组
  9. (19)ISE14.7软件生成bit失败永久解决方法(FPGA不积跬步101)
  10. (68)SPI工作模式有哪些?分为几种极性模式。