原文地址:http://blog.csdn.net/watkinsong/article/details/9849973html
有一种方式不须要本身配置全部的Sun JDK, Android SDK以及NDK,Eclipse等设置,使用已经配置好的开发套件就能够进行直接的开发,由NVIDIA开发的开发套件Tegra Android Development Pack可以直接设置好全部的开发环境,并且最新的版本还包含了OPENCV,不想本身配置的朋友能够直接下载这个套件。可是我本人没有尝试过使用这个套件,怎么使用也不明白,因此若是不想本身配置环境的话仍是须要本身去看看这个套件的使用。java
1. Sun JDK
首先须要安装java开发环境,这里必须使用Sun JDK,不能使用Opencv JDK. 安卓开发不支持Opencv JDK.linux
JDK下载地址:http://www.oracle.com/technetwork/java/javase/downloads/index.htmlandroid
建议下载稳定版本的J2SE.c++
安装好 Sun JDK后须要按照java JDK的安装方式配置环境变量。编程
设置JDK系统环境变量:ubuntu
在环境变量中添加以下内容windows
JAVA_HOME= C:\Program Files\Java\jdk1.X.XXX
Path=…..; %JAVA_HOME%\binoracle

2. Android SDK
安装安卓开发用的SDK,能够从 http://developer.android.com/sdk/index.html 这里下载最新的SDK。下载完毕后解压缩到一个不包含空格的目录便可。建议使清晰明了的目录,之后还要用。app
建议将SDK安装到独立的文件夹中,文件夹名不要有空格,也不要起中文名字。
Android SDK 不用配置系统环境变量,在Eclipse中建立Android的工程时候或者安装完ADT(Android Development Tools)以后会提示配置Android SDK 目录。这里只要保证目录名字不包含空格就能够了。
3. Eclipse
下载Eclipse做为开发用的IDE
下载地址:http://www.eclipse.org/downloads/
提示:最新发现下载的ADT中包含了最新版本Eclipse,能够不用下载。
4. Android Development Tools(ADT)
下载安卓开发工具包,包含一些经常使用的开发工具。
也能够直接使用Eclipse在线安装,但下载后再装比较方便,速度快。
下载地址:http://developer.android.com/sdk/eclipse-adt.html
下载完ADT后,给Eclipse安装ADT组件。
在Eclipse中:菜单Help-->Install new Software

安装ADT时的截图以下:

这个时候会看到两类组件,一类是Develop Tools,还有就是NDT Plugins,NDT Plugins是本地编程编译工具,也就是用来编译本地C++代码的,建议将两组工具都所有安装。
特别说明:若是你须要作本地C++开发的话,必定要把NDT Plugins勾选上。(注释:安装的时候务必选择NDK Plugins)

5. 配置Eclipse
ADT安装完毕,应该能够在Eclipse工具栏和Window菜单上找到Android SDK管理器的图标

点击Preferences开始设置Eclipse的Android开发环境……

设置安卓开发的SDK目录,这里须要将SDK目录指定到刚才咱们下载的Android SDK目录的根目录。

在Eclipse中选择Windows->Android SDK Manager,能够管理下载的SDK,也能够下载最新的SDK,用于不一样的SDK平台开发。
选择你所须要开发的平台的SDK(我最先下载的那个SDK包含了不少版本的SDK,可是最新下载的最新的SDK,结果只包含了很好的Android 4.3的API,不少都须要本身下载)

6. 建立虚拟机
使用Android Virtual Device Manager管理和建立虚拟机,用于调试。
(配置虚拟机的时候,有的虚拟机配置能够选择是否模拟GPU,建议根据本身的配置须要进行测试,我有一次使用了模拟GPU,结果模拟器的图像显示彻底不正常)



————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
上面部分主要介绍了Android开发环境的基本配置步骤,下面将要经过示例,讲解如何配置NDK进行本地JNI调用。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7. 安装CDT(CDT plugin for Eclipse)
Eclipse的CDT插件是用来在Eclipse进行C++开发的工具,若是你在配置安卓开发环境的时候安装ADT的过程当中,已经选择了NDK Plugins,那么就不须要再进行安装了,由于NDK Plugins已经包含了CDT,以下图:
若是在安装的时候没有选择NDK插件,那么须要再次安装CDT。

8. Android NDK
访问 http://developer.android.com/sdk/ndk/index.html
下载最新的Android NDK,是一个ZIP解压包,只需解压到某个路径便可,例如"F:\android-ndk-r8b-windows\android-ndk-r8b",再把这个路径添加到系统的环境变量PATH中。
update: 目前我使用的版本是:android-ndk-r10d
9. 安装Cygwin(能够选择性安装,经过命令行进行编译C++代码, 不建议使用)
若是你是在windows上做开发,能够选择安装,若是是在ubuntu上,则根本不须要安装。
在http://blog.csdn.net/watkinsong/article/details/8829235 这篇博客中,第三部分,介绍了使用Cygwin的方法,可是这里不推荐使用,因此若是你想使用的话,请参考上面连接中的博客配置方式。
10. OpenCV For Android
下载最新的opencv for android,
下载地址:http://sourceforge.net/projects/opencvlibrary/files/opencv-android/
安装完之后最好配置环境变量。
(不配置环境变量也能够,能够直接在eclipse中指定opencv头文件的包含目录)
注释: 最近仔细看了下opencv for android与opencv的区别,opencv4android也包含了opencv中的c++的头文件,因此若是你之前的c/c++代码使用了opencv的头文件,那么不用原来的opencv 也能够,由于opencv4android也有c/c++的头文件,只要你的工程配置可以找到这些头文件便可。另外,opencv4android中主要包含的是java版本的API, 都是.so连接库,.so 连接库是linux用的连接库文件。
***************
opencv4android中还包含了opencv.mk这样的一个make文件,这个文件对于编译本地opencv代码是很是重要的,若是你不想用opencv4android的SDK,可是也要把这个SDK中的opencv.mk这个文件复制到你的opencv目录或者其余目录,未来在 Android程序中配置NDK本地编译的时候须要使用这个文件。很是重要。
**********
11. 在Android中使用Opencv
使用opencv有两种方式,一种是使用opencv的java版本的API,可是这种方式不是经过本地调用实现的,所有都是java代码,因此这里先不讲,另一种方式就是使用opencv的c++版本的API,将本地c++代码编译成.so连接库,而后在安卓开发中进行调用,本地cpp代码使用NDK进行编译。
11.1 安卓java代码
下面给出一个使用Canny算子检测边缘的本地代码调用的使用方式。
新建安卓项目,配置使用安卓API等信息,这里个人项目名称为HaveImgFun
而后修改界面控制文件res->layout->activity_have_img_fun.xml
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <Button android:layout_height="wrap_content"
- android:layout_width="fill_parent"
- android:id="@+id/btnNDK"
- android:text="使用C++ OpenCV进行处理" />
- <Button android:layout_height="wrap_content"
- android:layout_width="fill_parent"
- android:id="@+id/btnRestore"
- android:text="还原" />
- <ImageView android:id="@+id/ImageView01"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent" />
- </LinearLayout>
在文件夹src下的com.testopencv.haveimgfun包中新建一个类用于包装使用了opencv c++代码的动态库的导出函数,类名为LibImgFun。
Eclipse会为你建立一个新的文件LibImgFun.java,将里面的内容改成:
- package com.testopencv.haveimgfun;
- public class LibImgFun {
- static {
- System.loadLibrary("ImgFun");
- }
-
- public static native int[] ImgFun(int[] buf, int w, int h);
- }
从上面的代码能够得知,咱们的动态库名字应该为“libImgFun.so”,注意"public static native int[] ImgFun(int[] buf, int w, int h)"中的native关键字,代表这个函数来自native code。static表示这是一个静态函数,这样就能够直接用类名去调用。
修改功能代码,修改HaveImgFun.java的代码,代码内容以下:
- package com.testopencv.haveimgfun;
-
- import android.app.Activity;
- import android.graphics.Bitmap;
- import android.graphics.Bitmap.Config;
- import android.graphics.drawable.BitmapDrawable;
- import android.os.Bundle;
- import android.widget.Button;
- import android.view.View;
- import android.widget.ImageView;
-
- public class HaveImgFun extends Activity {
-
- ImageView imgView;
- Button btnNDK, btnRestore;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_have_img_fun);
-
- this.setTitle("使用NDK转换灰度图");
- btnRestore = (Button) this.findViewById(R.id.btnRestore);
- btnRestore.setOnClickListener(new ClickEvent());
- btnNDK = (Button) this.findViewById(R.id.btnNDK);
- btnNDK.setOnClickListener(new ClickEvent());
- imgView = (ImageView) this.findViewById(R.id.ImageView01);
- Bitmap img = ((BitmapDrawable) getResources().getDrawable(
- R.drawable.lena)).getBitmap();
- imgView.setImageBitmap(img);
- }
-
- class ClickEvent implements View.OnClickListener {
- public void onClick(View v) {
- if (v == btnNDK) {
- long current = System.currentTimeMillis();
- Bitmap img1 = ((BitmapDrawable) getResources().getDrawable(
- R.drawable.lena)).getBitmap();
- int w = img1.getWidth(), h = img1.getHeight();
- int[] pix = new int[w * h];
- img1.getPixels(pix, 0, w, 0, 0, w, h);
- int[] resultInt = LibImgFun.ImgFun(pix, w, h);
- Bitmap resultImg = Bitmap.createBitmap(w, h, Config.RGB_565);
- resultImg.setPixels(resultInt, 0, w, 0, 0, w, h);
- long performance = System.currentTimeMillis() - current;
- imgView.setImageBitmap(resultImg);
- HaveImgFun.this.setTitle("w:" + String.valueOf(img1.getWidth())
- + ",h:" + String.valueOf(img1.getHeight()) + "NDK耗时"
- + String.valueOf(performance) + " 毫秒");
- } else if (v == btnRestore) {
- Bitmap img2 = ((BitmapDrawable) getResources().getDrawable(
- R.drawable.lena)).getBitmap();
- imgView.setImageBitmap(img2);
- HaveImgFun.this.setTitle("使用OpenCV进行图像处理");
- }
- }
- }
- }
注意:这里因为不一样的项目名以及类名,可能在运行程序的时候提示某个类找不到,这就须要查看AndroidManifest.xml这个文件了, AndroidMainfest.xml代码示例:
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.haveimgfun"
- android:versionCode="1"
- android:versionName="1.0" >
-
- <uses-sdk
- android:minSdkVersion="8"
- android:targetSdkVersion="18" />
-
- <application
- android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@style/AppTheme" >
- <activity
- android:name="com.example.haveimgfun.HaveImgFun"
- android:label="@string/app_name" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
-
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-
- </manifest>
上面的代码中指定了程序运行时须要实例化的类,
- android:name="com.example.haveimgfun.HaveImgFun"
上面这句代码须要根据不一样的项目名称以及类名进行修改,有时候会出现类找不到的错误提示。
11.2 C++代码
在项目中新建一个jni文件,用于放置该项目的全部cpp代码。
在jni文件夹下创建一个"ImgFun.cpp"的文件,内容改成下面所示:
- #include <jni.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <opencv2/opencv.hpp>
- using namespace cv;
- IplImage * change4channelTo3InIplImage(IplImage * src);
-
- extern "C" {
- JNIEXPORT jintArray JNICALL Java_com_testopencv_haveimgfun_LibImgFun_ImgFun(
- JNIEnv* env, jobject obj, jintArray buf, int w, int h);
- JNIEXPORT jintArray JNICALL Java_com_testopencv_haveimgfun_LibImgFun_ImgFun(
- JNIEnv* env, jobject obj, jintArray buf, int w, int h) {
-
- jint *cbuf;
- cbuf = env->GetIntArrayElements(buf, false);
- if (cbuf == NULL) {
- return 0;
- }
-
- Mat myimg(h, w, CV_8UC4, (unsigned char*) cbuf);
- IplImage image=IplImage(myimg);
- IplImage* image3channel = change4channelTo3InIplImage(&image);
-
- IplImage* pCannyImage=cvCreateImage(cvGetSize(image3channel),IPL_DEPTH_8U,1);
-
- cvCanny(image3channel,pCannyImage,50,150,3);
-
- int* outImage=new int[w*h];
- for(int i=0;i<w*h;i++)
- {
- outImage[i]=(int)pCannyImage->imageData[i];
- }
-
- int size = w * h;
- jintArray result = env->NewIntArray(size);
- env->SetIntArrayRegion(result, 0, size, outImage);
- env->ReleaseIntArrayElements(buf, cbuf, 0);
- return result;
- }
- }
-
- IplImage * change4channelTo3InIplImage(IplImage * src) {
- if (src->nChannels != 4) {
- return NULL;
- }
-
- IplImage * destImg = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 3);
- for (int row = 0; row < src->height; row++) {
- for (int col = 0; col < src->width; col++) {
- CvScalar s = cvGet2D(src, row, col);
- cvSet2D(destImg, row, col, s);
- }
- }
-
- return destImg;
- }
在上面的代码中,给出了简单的Canny算子检测边缘的代码,而且返回检测后的图像显示。
上面的代码中#include <jni.h>是必需要包含的头文件,#include <opencv2/opencv.hpp>是opencv要包含的头文件。
注释: 1.
- JNIEXPORT jintArray JNICALL Java_com_testopencv_haveimgfun_LibImgFun_ImgFun(
- JNIEnv* env, jobject obj, jintArray buf, int w, int h)</span></span>
这个函数名,必须与java代码中的包名以及类名,函数名彻底一致,
- Java_com_testopencv_haveimgfun_LibImgFun_ImgFun
分别表示了包,类,函数名,中间用_分开,这个是很是重要的,不然会提示找不到函数的异常 错误。
2. eclipse很奇怪,多是个人配置问题,我原本已经配置好了opencv的目录,可是若是不配置eclipse工程的包含目录,是找不到opencv头文件的。
- #include <opencv2/opencv.hpp>
这行代码,若是不配置eclipse工程中的包含目录,找不到系统环境变量中的opencv目录,这个若是各位有解决办法,还请多多指教。
若是 给工程添加包含目录,只有添加了包含目录,才能找到对应的头文件:
这里包含的头文件的目录既能够是opencv4android的c++头文件目录,也能够是之前你已经配置好的opencv目录
下面的截图中,是个人项目的配置,这里须要包括NDK中的若干最新版本的头文件,以及一些标准的c/c++的头文件,其中,标准的c/c++的头文件,会在将android项目转换为c/cpp项目的过程当中自动添加。请你们根据本身的系统配置以及文件存放目录对应的修改。原来的配置说明中使用的版本都太古老了。。。(下面第一张图是最新的配置截图,第二张图是原先的配置截图,放在这里仅供参考对比。)
特别注意:因此的配置中,都要让你的项目找到opencv的jni的目录,这样才能使用opencv的c/c++ 对应的头文件。


11.3 配置文件
而后再在jni下新建两个文件"Android.mk"文件和"Application.mk"文件,这两个文件事实上就是简单的Makefile文件。
使用NDK进行编译的时候,须要使用Android.mk和Application.mk两个文件。
Android.mk:
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- OPENCV_LIB_TYPE:=STATIC
- ifeq ("$(wildcard $(OPENCV_MK_PATH))","")
- #try to load OpenCV.mk from default install location
- include E:\java\OpenCV-2.4.5-android-sdk\sdk\native\jni\OpenCV.mk
- else
- include $(OPENCV_MK_PATH)
- endif
- LOCAL_MODULE := ImgFun
- LOCAL_SRC_FILES := ImgFun.cpp
- include $(BUILD_SHARED_LIBRARY)
Application.mk:
- APP_STL:=gnustl_static
- APP_CPPFLAGS:=-frtti -fexceptions
- APP_ABI:=armeabi armeabi-v7a </span>
在Android.mk文件中,须要主要修改的代码是以下一行:
- include E:\java\OpenCV-2.4.5-android-sdk\sdk\native\jni\OpenCV.mk
这里要指定到opencv.mk这个文件,不然在NDK进行编译本地c/c++ 文件得时候会提示你找不到opencv.mk这个文件。不用你把opencv.mk放到哪里,只要用绝对或者相对目录加载进来就能够。
特别注意:这里必定要在Android.mk文件中包含正确的opencv中的OpenCV.mk文件,由于这个OpenCV.mk文件配置了如何使用opencv中的动态连接库。而且经过这个文件把opencv中的c/cpp的连接库文件复制到了android项目中,在anroid项目运行的时候才能找到本地的native code对应的函数。也就是说,经过OpenCV.mk文件,在编译本地c/cpp文件的时候才能找到本地c/cpp代码所使用的opencv函数的连接库。
可是,由于我以前是在Windows上做开发,因此混合使用native code和 opencv 的 java SDK是没有问题的,不用手动copy opencv4android 的libopencv_java.so文件也能自动的把这个文件复制过去,可是目前我在ubuntu上配置的时候,发现最大的一个问题就是当混合使用native code和java sdk的时候, 没有自动的给android项目添加 libopencv_java.so这个库文件,并且当手动把文件添加到android项目的libs目录后NDK在 build本地代码的时候会自动把手动添加的库文件删了。。。WTF, 这个问题困扰了我很久,也找了不少资料,可是都不是太理想,最后仍是看NDK的文档把问题解决了,这里真心不知道为何在Ubuntu下配置使用opencv java SDK会有问题,不会自动的复制.so文件。。。
目前,仅仅使用native code不须要关心上面提出的问题,上面的问题会单独写一个blog给出解决方法。
而后须要使用LOCAL_SRC_FILES包含须要编译的文件。全部的c/c++ 文件都要分别列出来。
上面一行代码用来指定生成的连接库的名称。
11.4 编译本地C++代码
编译本地C++代码可使用Cygwin进行编译,cd 到项目目录,而后运行ndk-build
也可使用windows控制台进行编译,一样cd到项目目录,运行ndk-build
还可使用Eclipse进行编译,建议配置使用Eclipse进行编译,这样当项目的本地cpp代码发生变化的时候就能够实现自动的cpp代码编译,不用每次都在命令行中手动的进行编译,虽然使用黑乎乎的命令行手动编译,输出一堆信息显着很牛逼的样子。
(如下内容,若是使用cygwin进行编译,则不须要进行操做,直接使用cygwin或者命令行进行编译,保证编译经过之后便可运行程序,若是选择使用Eclipse自动进行编译,则参考如下内容进行配置)
首先须要将该项目转换到C++项目,使得该项目具备C++代码属性,以下所述。
点击项目,右击,New -> Other -> C/C++ -> Convert to a C/C++ Project.



配置Eclipse对cpp代码进行编译:
首先须要给当前项目添加一个编译环境变量
以下目录
open Eclipse menu Window -> Preferences -> C/C++ -> Build -> Environment,
点击Add...
添加一个NDKROOT,而且设置值为NDK的根目录。
而后设置编译的一些参数
Project Properties -> C/C++ Build, uncheck Use default build command, replace “Build command” text from "make" to
"${NDKROOT}/ndk-build.cmd" on Windows,
"${NDKROOT}/ndk-build" on Linux and MacOS.

而后修改Behaviour选项卡,设置编译配置(配置在保存代码的时候进行自动编译):

点击肯定,而后确认NDK(ndk-build)编译可以正常进行编译,
能够看到下图:

这个时候,会在C++代码中,看到很是多的错误提示,遍地都是错误提示,这里不要慌,这里只是假的错误提示,编译cpp代码可以编译经过,可是运行程序是不行的,会提示你代码有错误,须要解决这些问题。

打开工程属性,Project Properties -> C/C++ General -> Paths and Symbols
为GNC C++编译器添加以下路径:(这里添加的路径就是NDK 中的c/c++ 头文件的路径)
- # for NDK r8 and prior:
- ${NDKROOT}/platforms/android-9/arch-arm/usr/include
- ${NDKROOT}/sources/cxx-stl/gnu-libstdc++/include
- ${NDKROOT}/sources/cxx-stl/gnu-libstdc++/libs/armeabi-v7a/include
- ${ProjDirPath}/../../sdk/native/jni/include
- # for NDK r8b and later:
- ${NDKROOT}/platforms/android-9/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}/../../sdk/native/jni/include
而后就会看到全部的错误都消失了,这样从新编译本地cpp代码,而后就能够运行工程了。
注释:上面的头文件配置路径都是比较古老的了,最新的配置请看下图:

终于能够运行程序了,能够看到本程序的截图以下:(因为使用的虚拟机,因此运行速度比较慢)


**************************************************************************************************************************************************************************************
注释:上面的说明都是用的opencv 的c/c++版本的头文件以及代码,若是你用opencv4android中提供的例子,例子里面都用到opencv4android的java版本的API,这样你须要给工程配置Library,才能编译经过,我在最初的尝试中,都指定了API,可是一会API那个路径就变成叉叉了,后来发现,eclipse中必需要把libray那个工程加入进去,才能正确的加载library, 这样,你的eclipse必须把opencv4android中的Android版本的library那个工程加进去才能够。我用的opencv4android 2.4.6的版本, 这个版本的library工程名称为OpenCV Library - 2.4.6,必须在eclipse中把这个工程导入才能够成功的引用opencv4android 的java版本SDK
全部的引用 import org.opencv.core.Rect; 这种类型的包的文件,都说明这个工程包含了opencv4android的java版本的API,须要配置library.
并且还须要配置Android SDK 版本(不然编译提示出错)

Q & A:
在博客评论中,困扰不少人的问题有:
1. 不少人提到了cbuf = env->GetIntArrayElements(buf, false); 编译不过,这个应该是NDK版本的问题,其实我也没有仔细去查资料,究竟是什么问题,总之把问题解决了就能够了,我在从新配置的时候也遇到了这个问题,无非就是传入的参数和函数能够接收的参数不匹配,那么给它传一个匹配的就行了。
如今改为了: cbuf = env->GetIntArrayElements(buf, NULL); 这样就能够编译经过了