前言
最近需要在Android移动设备上进行OpenCV开发,搭建环境是第一步,我在网上看了n个相关的中文和英文的教程,终于成功搭建环境,现在跟大家分享一下成果,希望为大家减少不必要的困惑和麻烦。
系统环境
win7/win8(注:我的系统是win8专业版)
相关工具
1.Android ADT Bundle for Windows(注: 我下的是adt-bundle-windows-x86_64-20131030)
下载地址:https://developer.android.com/sdk/index.html?hl=sk
2.Android NDK(注:我下的是android-ndk-r8e-windows-x86_64)
下载地址:https://developer.android.com/tools/sdk/ndk/index.html
3.OpenCV for Android(注:我下的是OpenCV-2.4.8-android-sdk)
下载地址:http://sourceforge.net/projects/opencvlibrary/files/opencv-android/
Android ADT环境搭建
解压下载的Android ADT Bundle for Windows到某一目录下,我解压在D:\MySoftware目录下。
Android SDK环境变量的配置
运行 android –h 如果有下面的输出,表明安装成功:
Android NDK的安装与配置
OpenCV for Android 环境搭建
基于SDK的OpenCV开发
基于NDK的OpenCV开发
下面我将以一个实例向大家展示基于NDK的OpenCV开发。Android上层程序的编写
- activity_main.xml
- MainActivity.java
package my.example.ndkopencv; import org.opencv.android.BaseLoaderCallback; import org.opencv.android.LoaderCallbackInterface; import org.opencv.android.OpenCVLoader; import android.os.Bundle; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Bitmap.Config; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ImageView; public class MainActivity extends Activity implements OnClickListener{ private Button btnProc; private ImageView imageView; private Bitmap bmp; //OpenCV类库加载并初始化成功后的回调函数,在此我们不进行任何操作 private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) { @Override public void onManagerConnected(int status) { switch (status) { case LoaderCallbackInterface.SUCCESS:{ System.loadLibrary("image_proc"); } break; default:{ super.onManagerConnected(status); } break; } } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnProc = (Button) findViewById(R.id.btn); imageView = (ImageView) findViewById(R.id.image_view); //将测试图片加载程序中并进行显示 bmp = BitmapFactory.decodeResource(getResources(), R.drawable.test); imageView.setImageBitmap(bmp); btnProc.setOnClickListener(this); } @Override public void onClick(View v) { 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 = ImageProc.grayProc(pixels, w, h); Bitmap resultImg = Bitmap.createBitmap(w, h, Config.ARGB_8888); resultImg.setPixels(resultInt, 0, w, 0, 0, w, h); imageView.setImageBitmap(resultImg); } @Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库 OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this, mLoaderCallback); } }
- ImageProc.java
package my.example.ndkopencv; public class ImageProc { public static native int[] grayProc(int[] pixels, int w, int h); }
生成C++头文件
底层C++程序的编写
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
include ../OpenCV-SDK/native/jni/OpenCV.mk
LOCAL_SRC_FILES := ImageProc.cpp
LOCAL_MODULE := image_proc
include $(BUILD_SHARED_LIBRARY)
代码说明:
第一行:指明当前编译路径;
第二行:清空变量;
第三行:将OpenCV类库中的编译文件包含进来,如此一来在我们的工程中即可使用OpenCV类库;
第四行:指定需要编译的C++源文件;
第五行:指定编译生成的类库名称;
第六行:调用命令将源文件编译为静态库。
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := armeabi-v7a
APP_PLATFORM := android-8
#include
#include
#include
#include
using namespace cv;
using namespace std;
JNIEXPORT jintArray JNICALL Java_my_example_ndkopencv_ImageProc_grayProc(JNIEnv* env, jclass obj, jintArray buf, jint w, jint h){
jint *cbuf;
cbuf = env->GetIntArrayElements(buf, false);
if(cbuf == NULL){
return 0;
}
Mat imgData(h, w, CV_8UC4, (unsigned char*)cbuf);
uchar* ptr = imgData.ptr(0);
for(int i = 0; i < w*h; i ++){
//计算公式:Y(亮度) = 0.299*R + 0.587*G + 0.114*B
//对于一个int四字节,其彩色值存储方式为:BGRA
int grayScale = (int)(ptr[4*i+2]*0.299 + ptr[4*i+1]*0.587 + ptr[4*i+0]*0.114);
ptr[4*i+1] = grayScale;
ptr[4*i+2] = grayScale;
ptr[4*i+0] = grayScale;
}
int size=w * h;
jintArray result = env->NewIntArray(size);
env->SetIntArrayRegion(result, 0, size, cbuf);
env->ReleaseIntArrayElements(buf, cbuf, 0);
return result;
}
编译和运行程序
Android NDK: This is likely to result in incorrect builds. Try using LOCAL_S