本文介绍如何通过android studio通过jni调用openCV,不使用Opencv Manager,使用静态编译openCV的方式,生成单独的一个so文件。
可先看上篇文章http://blog.csdn.net/cheng20150809/article/details/51348420,AndroidStudio基本的JNI编程。
1、新建一个空的Activity工程,添加Add->Folder->JNI Folder,并添加cpp文件(Windows下再多添加一个空的,只有一个cpp文件,ndk会报错),同样我们使用JNI动态注册的方式,避免较长的函数名。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | #include <jni.h> #include <android/log.h> #include <string.h> #include <stdio.h> #include <android/bitmap.h> #include <assert.h> #include <opencv2/core/core.hpp> #include <opencv2/core/mat.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #ifndef LOG #define LOG_TAG "imgprocess" #define ALOGD(...) \ __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__); #define ALOGE(...) \ __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__); #define ALOGV(...) \ __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__); #endif LOG #ifndef NELEM # define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) #endif // 若这里不加namespace cv,则下面所有openCV的数据类型都要加cv::限定 using namespace cv; // 传入一个bitmap对象,传出一个包含边缘信息的bitmap对象 // 注意前两个参数是JNIEnv* env, jobject thiz // JNIEnv标识当前NDK环境的对象指针,可以通过该参数访问NDK中的内置成员, // jobject表示调用当前NDK方法的Java对象,可以用该参数值访问调用该方法的Java对象成员。 jobject getEdge(JNIEnv* env, jobject thiz, jobject bitmap){ AndroidBitmapInfo bitmapInfo; uint32_t* storedBitmapPixels = NULL; int pixelsCount; int ret = -1; // 读取bitmap基本信息 if ((ret = AndroidBitmap_getInfo(env, bitmap, &bitmapInfo)) < 0) { return NULL; } ALOGD( "width:%d height:%d stride:%d" , bitmapInfo.width, bitmapInfo.height, bitmapInfo.stride); // 这里只处理RGBA_888类型的bitmap if (bitmapInfo.format != ANDROID_BITMAP_FORMAT_RGBA_8888) { return NULL; } // 提取像素值 void * bitmapPixels = NULL; if ((ret = AndroidBitmap_lockPixels(env, bitmap, &bitmapPixels)) < 0) { return NULL; } // 生成openCV Mat矩阵 Mat srcMat(Size(bitmapInfo.width, bitmapInfo.height), CV_8UC4); pixelsCount = bitmapInfo.height * bitmapInfo.width; memcpy (srcMat.data, bitmapPixels, sizeof (uint32_t) * pixelsCount); AndroidBitmap_unlockPixels(env, bitmap); // 处理求边缘得到desMat Mat desMat; Canny(srcMat, desMat, 30.0, 90.0, 3, true ); // 通过JAVA层的Bitmap类,新建一个bitmap对象 jclass bitmapCls = env->FindClass( "android/graphics/Bitmap" ); jmethodID createBitmapFunction = env->GetStaticMethodID(bitmapCls, "createBitmap" , "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;" ); jstring configName = env->NewStringUTF( "ARGB_8888" ); jclass bitmapConfigClass = env->FindClass( "android/graphics/Bitmap$Config" ); jmethodID valueOfBitmapConfigFunction = env->GetStaticMethodID(bitmapConfigClass, "valueOf" , "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;" ); jobject bitmapConfig = env->CallStaticObjectMethod(bitmapConfigClass, valueOfBitmapConfigFunction, configName); jobject newBitmap = env->CallStaticObjectMethod(bitmapCls, createBitmapFunction, desMat.cols, desMat.rows, bitmapConfig); // 获取新bitmap的data数据指针 bitmapPixels = NULL; if ((ret = AndroidBitmap_lockPixels(env, newBitmap, &bitmapPixels)) < 0) { return NULL; } // 将Mat数据写入bitmap uint32_t* newBitmapPixels = (uint32_t*)bitmapPixels; pixelsCount = srcMat.cols * desMat.rows; for ( size_t i = 0; i < pixelsCount; i++) { memset (&(newBitmapPixels[i]), srcMat.data[i], 3); } AndroidBitmap_unlockPixels(env, newBitmap); return newBitmap; } // native函数所在的类 static const char *classPathName = "test/hc/cvtest/MainActivity" ; // static JNINativeMethod gMethods[] = { { "getEdge" , "(Ljava/lang/Object;)Ljava/lang/Object;" , ( void *)getEdge}, }; int registerNativeMethods(JNIEnv* env, const char * className, JNINativeMethod* gMethods, int numMethods) { jclass clazz; clazz = env->FindClass(className); if (clazz == NULL) { ALOGE( "Native registration unable to find class '%s'" , className); return JNI_FALSE; } if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) { ALOGE( "RegisterNatives failed for '%s'" , className); return JNI_FALSE; } return JNI_TRUE; } int register_jni_methods(JNIEnv* env) { return registerNativeMethods(env, classPathName, gMethods, NELEM(gMethods)); } jint JNI_OnLoad(JavaVM* vm, void *) { JNIEnv* env = NULL; jint result = -1; if (vm->GetEnv(( void **) &env, JNI_VERSION_1_4) != JNI_OK) { ALOGE( "ERROR: GetEnv failed\n" ); goto bail; } assert (env != NULL); if (register_jni_methods(env) < 0) { ALOGE( "ERROR: native registration failed\n" ); goto bail; } ALOGE( "SUCCESS: native registration successed\n" ); result = JNI_VERSION_1_4; bail: return result; } |
2、由于需要使用opencv的mk文件,这里我们不使用Android Studio的默认ndk编译,使用我们自己的mk文件。在jni目录中添加Android.mk和Application.mk文件。
Android.mk
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | LOCAL_PATH := $(call my- dir ) include $(CLEAR_VARS) #opencv OPENCVROOT:= /local/tools/OpenCV-2 .4.10-android-sdk /OpenCV-2 .4.10-android-sdk OPENCV_CAMERA_MODULES:=off OPENCV_INSTALL_MODULES:=on #OPENCV_LIB_TYPE:=SHARED #静态编译 OPENCV_LIB_TYPE:=STATIC include ${OPENCVROOT} /sdk/native/jni/OpenCV .mk LOCAL_SRC_FILES := ImgProcess.cpp LOCAL_LDLIBS += -llog LOCAL_LDLIBS += -ljnigraphics LOCAL_MODULE := imgprocess include $(BUILD_SHARED_LIBRARY) |
Application.mk
1 2 3 4 | # CPU架构,目前有arm64-v8a,需要64位ndk以及较新的opencv APP_ABI := armeabi, armeabi-v7a, x86 APP_PLATFORM := android-16 APP_STL := gnustl_static |
3、配置app的build.gradle,defaultConfig中添加一个编译ndk的task,生成的so文件将会生成在src/main/jniLibs这个目录中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | android { compileSdkVersion 23 buildToolsVersion "23.0.3" defaultConfig { applicationId "test.hc.cvtest" minSdkVersion 15 targetSdkVersion 23 versionCode 1 versionName "1.0" // add begin for ndk sourceSets.main.jni.srcDirs = [] task ndkBuild( type : Exec, description: 'Compile JNI source via NDK' ) { commandLine "$ndkDir/ndk-build" , 'NDK_PROJECT_PATH=build/intermediates/ndk' , 'NDK_LIBS_OUT=src/main/jniLibs' , 'APP_BUILD_SCRIPT=src/main/jni/Android.mk' , 'NDK_APPLICATION_MK=src/main/jni/Application.mk' } tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn ndkBuild } // add end for ndk } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile( 'proguard-android.txt' ), 'proguard-rules.pro' } } } |
4、在gradle.properties中添加:
android.useDeprecatedNdk=true
ndkDir=/local/tools/android-ndk-r10
并在在local.properties中配置好ndk路径
ndk.dir=/local/tools/android-ndk-r10
1
5、新建一个java类,load lib库,并添加native函数即可
如果是用CMakeLists.txt(用ndk-r16)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # For more information about using CMake with Android Studio, read the # documentation: https://d.android.com/studio/projects/add-native-code.html # Sets the minimum version of CMake required to build the native library. cmake_minimum_required(VERSION 3.4.1) #老版本的opencv用的gnu编译 #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") set (OPENCV "D:/OpenCV-2.4.9-android-sdk/sdk" ) include_directories(${OPENCV} /native/jni/include/ ) add_library(libopencv_java SHARED IMPORTED) set_target_properties(libopencv_java PROPERTIES IMPORTED_LOCATION ${OPENCV} /native/libs/armeabi-v7a/libopencv_java .so) add_library( native-lib SHARED native-lib.cpp NumOcr.cpp OpenCv.cpp JniMethods.cpp) target_link_libraries( native-lib android jnigraphics libopencv_java log) |
收藏的用户(0) X
正在加载信息~
推荐阅读
最新回复 (2)
-
上述mat数据拷贝bitmap貌似有问题,下面测试正常代码
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455void
matToBitmap(JNIEnv *env, Mat &mat, jobject &bitmap, jboolean needAlpha) {
AndroidBitmapInfo info;
void
*pixels = 0;
Mat &src = mat;
try
{
CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0);
CV_Assert(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888 ||
info.format == ANDROID_BITMAP_FORMAT_RGB_565);
CV_Assert(src.dims == 2 && info.height == (uint32_t) src.rows &&
info.width == (uint32_t) src.cols);
CV_Assert(src.type() == CV_8UC1 || src.type() == CV_8UC3 || src.type() == CV_8UC4);
CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0);
CV_Assert(pixels);
if
(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
Mat tmp(info.height, info.width, CV_8UC4, pixels);
if
(src.type() == CV_8UC1) {
LOGI(
"nMatToBitmap: CV_8UC1 -> RGBA_8888"
);
cvtColor(src, tmp, COLOR_GRAY2RGBA);
}
else
if
(src.type() == CV_8UC3) {
LOGI(
"nMatToBitmap: CV_8UC3 -> RGBA_8888"
);
//cvtColor(src, tmp, COLOR_RGB2RGBA);
cvtColor(src, tmp, COLOR_RGB2BGRA);
//有效
}
else
if
(src.type() == CV_8UC4) {
LOGI(
"nMatToBitmap: CV_8UC4 -> RGBA_8888"
);
if
(needAlpha)
cvtColor(src, tmp, COLOR_RGBA2mRGBA);
else
src.copyTo(tmp);
}
}
else
{
// info.format == ANDROID_BITMAP_FORMAT_RGB_565
Mat tmp(info.height, info.width, CV_8UC2, pixels);
if
(src.type() == CV_8UC1) {
LOGI(
"nMatToBitmap: CV_8UC1 -> RGB_565"
);
cvtColor(src, tmp, COLOR_GRAY2BGR565);
}
else
if
(src.type() == CV_8UC3) {
LOGI(
"nMatToBitmap: CV_8UC3 -> RGB_565"
);
cvtColor(src, tmp, COLOR_RGB2BGR565);
}
else
if
(src.type() == CV_8UC4) {
LOGI(
"nMatToBitmap: CV_8UC4 -> RGB_565"
);
cvtColor(src, tmp, COLOR_RGBA2BGR565);
}
}
AndroidBitmap_unlockPixels(env, bitmap);
return
;
}
catch
(
const
cv::Exception &e) {
AndroidBitmap_unlockPixels(env, bitmap);
LOGI(
"nMatToBitmap catched cv::Exception: %s"
, e.what());
return
;
}
catch
(...) {
AndroidBitmap_unlockPixels(env, bitmap);
LOGI(
"nMatToBitmap catched unknown exception (...)"
);
return
;
}
}
-
站点信息
- 文章2302
- 用户1336
- 访客10980498
每日一句
If you want to achieve greatness, stop asking for permission.
如果你想获得伟大,别再请求许可。
如果你想获得伟大,别再请求许可。
排名前5的开源在线机器学习
Android自定义蜂窝view
Android应用性能优化系列视图篇——隐藏在资源图片中的内存杀手
Java中的(耦合)控制反转
OpenGL读取帧缓存数据
首发:Thinkpad T550黑苹果10.13.4安装教程
反编译修改class文件变量
IntelliJ IDEA2018~2019.1激活码-注册码
p2p通信,打洞技术,穿越NAT的实现(附NAT环境检测工具)
【黑苹果安装】——如何在windows下操作EFI分区
Could not resolve io.flutter 解决方法
MPAndroidChart标记控件MarkerView的使用方法
imencode和imdecode使用
新会员