接上一篇博客,“Android环境搭建之二 Eclipse下搭建Android+OpenCV开发坏境”,其中提到Android平台下使用OpenCV有两种方式,一种是直接利用OpenCV的java API进行开发;另一种是在Android平台下调用C代码,在C代码中调用OpenCV。上一篇博客介绍了第一种方法,这篇博客来介绍第二种方法。
相比于第一种方法,在Android中调用C代码的坏境配置以及调用过程要复杂的多,但是带来的好处是效率大大提高,在手机这种资源有限的平台上,效率显得尤为重要。需要强调的一点是NDK r8版本之后,并不需要在Windows上安装Cygwin来模拟Linux坏境了,如此可以简化很多操作、减少出错的几率,而很多人在环境配置上都倒在了Cygwin上,导致后续过程难以进行。
本文默认你的计算机上装有Eclipse的基本Android开发坏境,即只要可以开发最基本的“HelloWorld”工程即可。首先例举下Android + C所需要软件,已上传至百度网盘,大家可去分享下载。
http://pan.baidu.com/s/1mgr7nF6 密码:bv7y
AndroidNDK:android-ndk-r10 (r8版本之后都可)
NDK路径设置插件:com.android.ide.eclipse.ndk_23.0.2.1259578.jar
本文在配置过程中参考了很多优秀的技术博客,感谢他们的分享,推荐其中几篇和大家共同学习。
http://jingyan.baidu.com/article/3ea51489e7a9bd52e61bbac7.html
http://blog.csdn.net/shulianghan/article/details/18964835
一、Android + C环境搭建
(1)在Eclipse根目录同一文件夹下。如下图
(2)下载NDK路径设置插件(com.android.ide.eclipse.ndk_23.0.2.1259578.jar),将其放在Eclipse下的plugins文件夹下,如图
(3)打开Eclipse,依次打开 windows à preference àAndroid à NDK,在NDK location下设置NDK的路径,如图
以上基本步骤完成表明基本坏境已经搭建完成,相比于网络上普遍博客介绍的需要安装Cygwin坏境,所需步骤大大减少。接下来用个实例来验证下所配置坏境是否正确。
(4)Eclipse中导入“hello-jni”程序,以此打开Fileàimport,如下图
(5)右键“HelloJni”工程àAndroid ToolsàAdd Native Support
(6) “Ctrl + B”编译过程,然后运行,运行结果如下,则Android + C基本坏境成功。
二、编写自己的Android+C程序
(1)新建工程“DemoJni”,并建立一个基本布局,一个Button加上一个TextView
(2)右键 “DemoJni”工程àAndroidToolsàAdd Native Support,添加完之后可以发现包资源管理器多了一个 jni 文件夹,文件夹下会有Android.mk 和.cpp文件
(3)在Activity中声明Native函数,如下:
public native int myAdd(int a, int b);
pulibc native int muMinus(int a, int b);
static {
System.loadLibrary("hello-jni");
}
4)右键“DemoJni”工程à属性àBuilder(构建器)à新建。新建的构建器具体设置如下:location(位置)为javah.exe的路径,通过浏览文件系统来选择;工作目录为“DemoJni”工程目录,通过浏览工作空间来选择;自变量的路径设置为 “-classpath src -djni/platform com.example.demojni.MainActivity”,最后的一段路径为声明Native函数的Activity路径。如图
(5)”Ctrl + B”编译工程,完成之后可以看到platform文件夹下多了 .h 头文件,然后从其他工程中拷贝一份 Application.mk 文件至 jni 文件夹下。如图
(6)在 .cpp 文件夹中编写本地C/C++代码;在Android.mk和Application.mk文件中编写相应内容,然后进行编译。这部分内容的详细说明在一些技术博客中可以找到,以下为推荐的博客地址:http://blog.csdn.net/shulianghan/article/details/18964835
(7)在手机或者模拟器上运行调试。
三、编写自己的Android+ C + OpenCV程序
基于以上Android + C坏境,下面来说明如何更进一步在C代码中调用OpenCV从而在涉及多媒体信息处理的程序时,可以大大提高程序性能。在基于以上“DemoJni”工程的基础上,进行调用OpenCV的说明。(1)首先右键“DemoJni”工程à属性àAndroidà添加OpenCV4Android的依赖库。
(3)右键“DemoJni”工程à属性àC/C++GeneralàPath and Symbols,添加以下路径
${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
其中最后一个路径指定到计算机中OpenCV4Android的路径,例如在我的电脑中具体路径为” D:\Java\work\OpenCV_SDK\native\jni\include”。
(4)在Activity中声明Native函数,新建布局,完成一个基本的Blur功能。然后编译工程,要想在C代码中使用OpenCV,需要在Android.mk和Application.mk中添加一些代码,具体如下。
在Android.mk文件中,额外添加一行”include ../OpenCV_SDK/native/jni/OpenCV.mk”,其中路径是OpenCV4Android中OpenCV.mk文件的路径。
在Application.mk中,额外添加代码
APP_STL:= gnustl_static
APP_CPPFLAGS :=-frtti -fexceptions
这部分的详细说明可以在 OpenCV的官方手册中找到,具体地址如下;http://docs.opencv.org/doc/tutorials/introduction/android_binary_package/android_dev_intro.html#android-dev-intro
(5)在.cpp文件中编写本地C++代码,调用OpenCV API,然后编译运行。
MainActivity中代码为:
package com.example.demojni;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
public class MainActivity extends ActionBarActivity {
private Button button;
private ImageView imageView;
private Bitmap bmp;
public native int[] JniBlur(int[] pixels, int w, int h);
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS:{
System.loadLibrary("DemoJni");
} break;
default:{
super.onManagerConnected(status);
} break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.blur_layout);
button = (Button)findViewById(R.id.blur_button);
imageView = (ImageView)findViewById(R.id.blur_imageview);
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.lena);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO 自动生成的方法存根
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 = JniBlur(pixels, w, h);
Bitmap resultImg = Bitmap.createBitmap(w, h, Config.ARGB_8888);
resultImg.setPixels(resultInt, 0, w, 0, 0, w, h);
imageView.setImageBitmap(resultImg);
}
});
}
public void onResume(){
super.onResume();
/**
* 通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是
* OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存在于OpenCV安装包的apk目录中
*/
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_9, this, mLoaderCallback);
}
}
.cpp文件中代码为:
#include <jni.h>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/core.hpp>
using namespace cv;
using namespace std;
extern "C" JNIEXPORT jintArray JNICALL Java_com_example_demojni_MainActivity_JniBlur
(JNIEnv * env, jobject obj, jintArray pixels, jint w, jint h)
{
jint *cbuf;
cbuf = env->GetIntArrayElements(pixels, NULL);
if(cbuf == NULL){
return 0;
}
Mat imgData(h, w, CV_8UC4, (unsigned char*)cbuf);
blur(imgData, imgData,Size(25,25),Point(-1, -1));
int size=w * h;
jintArray result = env->NewIntArray(size);
env->SetIntArrayRegion(result, 0, size, cbuf);
env->ReleaseIntArrayElements(pixels, cbuf, 0);
return result;
}
(6)最后运行结果如下,表明在C代码中调用OpenCV API进行图像的模糊处理,即Android + C + OpenCV 搭建成功。
中国科学技术大学多媒体计算与通信教育部-微软重点实验室 MultiMedia Computing Group