初学者刚开始接触Android,又要开发opencv,真是举步维艰啊,现把阶段学习结果记录下:
1.android studio+opencv的安装及配置
android studio2.2官网下载
opencv-android2.4.10官网下载
这个网上资料介绍的很多,不过由于环境不一样,还是踩了很多坑。由于opencv是C++为基础语言,所以很多资料也是C的代码,而android studio采用的是java,不过还好android studio可以利用jni调用C++代码,android studio默认使用CMake构建编译,以前做Andorid下的opencv开发都是基于Android.mk方式是编译和配置opencv依赖的加载,本文通过配置CMakeList.txt来完成NDK opencv开发。
这个配置搞了很久,配置不好jni里面没法引用OpenCV,因为有很多不懂的地方,后续还是需要学习下,把配置以上传供后来人参考。
# Sets the minimum version of CMake required to build the native # library. You should either keep the default value or only pass a # value of 3.4.0 or lower. cmake_minimum_required(VERSION 3.4.1) #工程路径 set(pathToProject D:\\androidwork\\myfirstapp6) #OpenCV-android-sdk路径 set(pathToOpenCv D:\\OpenCV-2.4.10-android-sdk) SET(OpenCV_lib_DIR $ENV{HOME}/OpenCV-2.4.10-android-sdk/sdk/native/jni) #支持-std=gnu++11 set(CMAKE_VERBOSE_MAKEFILE on) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") #配置加载native依赖 include_directories(${pathToOpenCv}/sdk/native/jni/include) #CPP文件夹下带编译的cpp文件 add_library(native-lib SHARED src/main/cpp/native-lib.cpp) #动态方式加载 add_library(lib_opencv SHARED IMPORTED ) #引入libopencv_java3.so文件 set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION ${pathToProject}/app/src/main/jniLibs/${ANDROID_ABI}/libopencv_java.so) #C++日志 find_library( log-lib log ) #target_link_libraries( native-lib $\{log-lib} ) target_link_libraries( native-lib $\{log-lib} lib_opencv jnigraphics)2.在java里添加java层声明函数,然后在函数上alt+enter就会出现创建native层对应函数的菜单。
public static native int[] getGrayImage(int[] pixels, int w, int h);//静态函数
native函数默认创建在native-lib.cpp内,对应函数如下:
JNIEXPORT jintArray JNICALL Java_myandroidword_myfirstapp6_MainActivity_getGrayImage(JNIEnv *env, jclass type, jintArray pixels_, jint w, jint h) { jint *pixels = env->GetIntArrayElements(pixels_, NULL); // TODO if(pixels==NULL){ return NULL; } cv::Mat imgData(h, w, CV_8UC4, pixels); uchar *ptr = imgData.ptr(0); for (int i = 0; i < w * h; i++) { 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] = (uchar) grayScale; ptr[4 * i + 2] = (uchar) grayScale; ptr[4 * i + 0] = (uchar) grayScale; } int size = w * h; jintArray result = env->NewIntArray(size); env->SetIntArrayRegion(result, 0, size, pixels); env->ReleaseIntArrayElements(pixels_, pixels, 0); return result;此函数转换后并返回灰度图数据。
3.如果要灵活使用jni开发,就需要在java层和native层进行参数传递,用法如下:
JNIEXPORT jlong JNICALL Java_myandroidword_myfirstapp6_MainActivity_GrayImage(JNIEnv *env, jobject instance, jlong bitmat) { // TODO Mat *bitmpaMat = (Mat*) bitmat; Mat *gray = new Mat(bitmpaMat->rows ,bitmpaMat->cols,CV_32FC1); cv::cvtColor(*bitmpaMat,*gray,CV_BGR2GRAY); jlong j=(jlong)gray; if(j>0) { return j; } else{ return 0;} }
4.native层调用java方法
// 1 . 找到java代码的 class文件 // jclass(*FindClass)(JNIEnv*, const char*); jclass dpclazz = env->FindClass("myandroidword/myfirstapp6/MainActivity"); // instance代表类的实例getobjclass取得实例所属的类 // jclass dpclazz = env->GetObjectClass(instance); if(dpclazz==0){ return; } // 2 寻找class里面的方法 // jmethodID(*GetMethodID)(JNIEnv*, jclass, const char*, const char*); jmethodID method1 = env->GetMethodID(dpclazz,"testcalljava","(J)V"); if(method1==0){ return; } //3 .调用这个方法 // void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); std::string hello = "Hello Call Java OK"; jstring js=env->NewStringUTF(hello.c_str()); env->CallVoidMethod(instance,method1,js);
其中需要知道java层的函数签名描述,采用的是javap - s+classpath,需要进入classes文件夹,才能使用。