动机
externalNativeBuild { cmake { path "src/main/cpp/CMakeLists.txt" version "3.18.1" } } 备注2: MyOpenCv Module 和app 可以相连是通过settings.gradle 后两行的include。
pluginManagement { repositories { gradlePluginPortal() google() mavenCentral() } } dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() } } rootProject.name = "MyApp" include ':app' include ':MyOpenCv'
cmake_minimum_required(VERSION 3.18.1) set(OpenCV_DIR "/home/jason9075/Documents/OpenCV-android-sdk/sdk/native/jni") find_package(OpenCV REQUIRED) project("myopencv") add_library(myopencv SHARED myopencv.cpp) include_directories(${OpenCV_INCLUDE_DIRS}) find_library(log-lib log) # For Android Bitvert to cv::Mat find_library(jnigraphics-lib jnigraphics) target_link_libraries(myopencv ${OpenCV_LIBS} ${jnigraphics-lib} # For Android Bitmap Covert to cv::Mat ${log-lib}) 这时你会发现Sync 失败, 问题出在CMakeLists.txt 第五行没有抓到OpenCV 套件。检视警告页面提示为:无法找到abi binary,位置在OpenCVConfig.cmake:47然后…经过我漫长的寻找…不断的在cmake file 里用message() 确认各个变数,终于发现在OpenCVConfig.cmake 这个档案里的第39行中ANDROID_NDK_ABI_NAME 的值都是空的!理论上它应该会是:[arm64-v8a, armeabi-v7a, x86, x86_64] ,各代表在不同环境的CPU架构。OpenCVConfig.cmake尝试注解掉原本的ANDROID_NDK_ABI_NAME 后,替换成 ANDROID_ABI 就可以成功的sync。等 sync 完成后,就可以在myopencv.cpp 档案里引用OpenCV 而不会出错。备注:如果你自己的电脑本身有安装OpenCV ,不使用官网下载的OpenCV_DIR ,它可能会自己跑去找系统的版本(/usr/local/lib/cmake/opencv4),而发生错误。我自己有发生set(OpenCV_DIR path) 路径打错,跑去抓/usr/local 底下的版本,然后不断出现:
C/C++: CMakeFiles/cvmodule.dir/cvmodule.cpp.o(.data+0x0): error: undefined reference to 'typeinfo for cv::Exception' 的错误。
extern "C" JNIEXPORT void JNICALL Java_com_jason9075_myopencv_NativeLib_toGrey( JNIEnv *env, jobject, jobject bitmapIn, jobject bitmapOut) { Mat src, greyOut; bitmapToMat(env, bitmapIn, src, false); cvtColor(src, greyOut, CV_BGR2GRAY); matToBitmap(env, greyOut, bitmapOut, false); } 然后在NativeLib.java 里要宣告与C++对应的function。
public class NativeLib { static { System.loadLibrary("myopencv"); } public native String stringFromJNI(); public native void toGrey(Bitmap bitmapIn, Bitmap bitmapOut); } 为了测试我们到应用程式app文件夹,把Android绿色机器人图片放到drawable 文件夹,然后在MainActivity 转成灰色。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); setSupportActionBar(binding.toolbar); NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main); appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build(); NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration); ImageView iv = findViewById(R.id.imageView); binding.fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) .setAction("Action", null).show(); } }); // Test Hello World From MyOpenCv NativeLib cv = new NativeLib(); System.out.println(cv.stringFromJNI()); Bitmap image = BitmapFactory.decodeResource(getResources(), R.drawable.android); cv.toGrey(image, image); iv.setImageBitmap(image); }
package com.jason9075.myopencv; public class ImageInfo { private final int width; private final int height; public ImageInfo(int width, int height) { this.width = width; this.height = height; } public int getWidth() { return width; } public int getHeight() { return height; } @Override public String toString() { return "ImageInfo{" + "width=" + width + ", height=" + height + '}'; } } 在 NativeLib.java 宣告相对应的 function getInfo()
package com.jason9075.myopencv; import android.graphics.Bitmap; public class NativeLib { static { System.loadLibrary("myopencv"); } public native String stringFromJNI(); public native void toGrey(Bitmap bitmapIn, Bitmap bitmapOut); public native ImageInfo getInfo(Bitmap bitmap); } 然后在myopencv.cpp 新增C++ 实做方式。须注意的一点是,因为我们最终要回传 Java Object,所以在C++这边要定义clsPath ,要找你预期回传的 Java Class 长的怎么样,还有这个Class 的Constructor 需要什么样的signature (这边 width 和 height 都是 Int 所以是(II)V)。
extern "C" JNIEXPORT jobject JNICALL Java_com_jason9075_myopencv_NativeLib_getInfo(JNIEnv *env, jobject thiz, jobject bitmap) { Mat src; bitmapToMat(env, bitmap, src, false); int width = src.cols; int height = src.rows; // return java object const char *clsPath = "com/jason9075/myopencv/ImageInfo"; jclass cls = env->FindClass(clsPath); jmethodID constructor = env->GetMethodID(cls, "
// Test Hello World From MyOpenCv NativeLib cv = new NativeLib(); System.out.println(cv.stringFromJNI()); Bitmap image = BitmapFactory.decodeResource(getResources(), R.drawable.android); cv.toGrey(image, image); iv.setImageBitmap(image); System.out.println(">>> " + cv.getInfo(image)); 我们可以成功读取到宽高分别为2688 和3197。(这边和原图宽高不同的原因是Android 的Drawable 会自动缩放,若想测试原图可以改放Asset文件夹)
审核编辑:汤梓红
全部0条评论
快来发表一下你的评论吧 !