Android JNI 开发 调用Opencv 的so库 您所在的位置:网站首页 怎么打出部首的拼音 Android JNI 开发 调用Opencv 的so库

Android JNI 开发 调用Opencv 的so库

2023-01-06 00:18| 来源: 网络整理| 查看: 265

Android JNI 开发 调用Opencv 的so库 Android JNI 开发简介:NDK 简介NDK 基础概念ABI 是什么 在Android Studio中调用OpenCV的so库补充图文教程

Android JNI 开发 简介:

JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)。从Java1.1开始,JNI标准成为Java平台的一部分,它允许Java代码和其他语言写的代码进行交互。

这里是一篇关于JNI开发的基本知识的辨析,我觉得很不错,讲清楚了java,NDK, 安卓,JNI几个的联系与区别,但需要注意的是,这篇文章里主要讲的是通过NDK来创建JNI调用,本文主要讲的是通过cmake来完成JNI调用。但还是要做一下应有的辨析。

NDK 简介

这部分内容参考了这里,在介绍 NDK 之前还是首推 Android 官方 NDK 文档。 官方文档分别从以下几个方面介绍了 NDK

NDK 的基础概念如何编译 NDK 项目ABI 是什么以及不同 CPU 指令集支持哪些 ABI如何使用您自己及其他预建的库 NDK 基础概念

首先先用简单的话分别解释下 JNI、NDK, 以及分别和 Android 开发、c/c++ 开发的配合。在解释过程中会对 Android.mk、Application.mk、ndk-build、CMake、CMakeList 这些常见名词进行扫盲。

JNI(Java Native Interface):Java本地接口。是为了方便Java调用c、c++等本地代码所封装的一层接口(也是一个标准)。大家都知道,Java的优点是跨平台,但是作为优点的同时,其在本地交互的时候就编程了缺点。Java的跨平台特性导致其本地交互的能力不够强大,一些和操作系统相关的特性Java无法完成,于是Java提供了jni专门用于和本地代码交互,这样就增强了Java语言的本地交互能力。

NDK(Native Development Kit) : 原生开发工具包,即帮助开发原生代码的一系列工具,包括但不限于编译工具、一些公共库、开发IDE等。

NDK 工具包中提供了完整的一套将 c/c++ 代码编译成静态/动态库的工具,而 Android.mk 和 Application.mk 你可以认为是描述编译参数和一些配置的文件。比如指定使用c++11还是c++14编译,会引用哪些共享库,并描述关系等,还会指定编译的 abi。只有有了这些 NDK 中的编译工具才能准确的编译 c/c++ 代码。

ndk-build 文件是 Android NDK r4 中引入的一个 shell 脚本。其用途是调用正确的 NDK 构建脚本。其实最终还是会去调用 NDK 自己的编译工具。

那 CMake 又是什么呢。脱离 Android 开发来看,c/c++ 的编译文件在不同平台是不一样的。Unix 下会使用 makefile 文件编译,Windows 下会使用 project 文件编译。而 CMake 则是一个跨平台的编译工具,它并不会直接编译出对象,而是根据自定义的语言规则(CMakeLists.txt)生成 对应 makefile 或 project 文件,然后再调用底层的编译。

在Android Studio 2.2 之后,工具中增加了 CMake 的支持,你可以这么认为,在 Android Studio 2.2 之后你有2种选择来编译你写的 c/c++ 代码。一个是 ndk-build + Android.mk + Application.mk 组合,另一个是 CMake + CMakeLists.txt 组合。这2个组合与Android代码和c/c++代码无关,只是不同的构建脚本和构建命令。本篇文章主要会描述后者的组合。(也是Android现在主推的)

ABI 是什么

ABI(Application binary interface)应用程序二进制接口。不同的CPU 与指令集的每种组合都有定义的 ABI (应用程序二进制接口),一段程序只有遵循这个接口规范才能在该 CPU 上运行,所以同样的程序代码为了兼容多个不同的CPU,需要为不同的 ABI 构建不同的库文件。当然对于CPU来说,不同的架构并不意味着一定互不兼容。

armeabi设备只兼容armeabi; armeabi-v7a设备兼容armeabi-v7a、armeabi; arm64-v8a设备兼容arm64-v8a、armeabi-v7a、armeabi; X86设备兼容X86、armeabi; X86_64设备兼容X86_64、X86、armeabi; mips64设备兼容mips64、mips; mips只兼容mips; 当我们开发 Android 应用的时候,由于 Java 代码运行在虚拟机上,所以我们从来没有关心过这方面的问题。但是当我们开发或者使用原生代码时就需要了解不同 ABI 以及为自己的程序选择接入不同 ABI 的库。(库越多,包越大,所以要有选择)

总结一下: android 调用JNI 分为静态调用与动态调用(不论动态还是静态前提都是NDK环境已经配置好的前提下) 一、静态主要就是将c(.c)或者c++(cpp)的源文件直接加到项目中进行调用,然后在CMakeLists.txt中进行配置。 二、动态调用 动态调用使用已经编译好的动态库.so文件

在Android Studio中调用OpenCV的so库

以下是创建具有Native OpenCV 支持的新 Android Studio 项目的步骤:

下载并安装 Android Studio安装 NDK 和 CMake创建一个新的 Native Android Studio 项目:

从主菜单中选择File -> New -> New Project...。

单击手机和平板选项卡,选择Native C++,然后单击下一步。

选择一个应用程序名称,选择语言(Kotlin 或 Java),选择最低 API 级别(此处为 28),然后选择下一步。

选择 Toolchain default as C++ standard 并单击 Finish。

创建成功后,直接build运行,手机界面出现 Hello from C++

build后会生产.so文件,一般在build->intermediates->cmake->debug->obj下,这里即为把本地的c++文件打包成了so库

安装 OpenCV Android 版本: 下载 OpenCV 4.6.0 Android 版本或在 OpenCV 网站上下载最新可用的 Android 版本。解压缩下载的文件并将 OpenCV-android-sdk 目录放在您选择的路径上。 将 OpenCV Android SDK 作为模块添加到您的项目中: 打开 setting.gradle 文件并附加这两行。 include ':opencv' project(':opencv').projectDir = new File(opencvsdk + '/sdk') 打开 gradle.properties 文件并附加以下行。不要忘记为您的机器使用刚才下载到本地的正确的 OpenCV Android SDK 路径。 opencvsdk=/Users/Example/Downloads/OpenCV-android-sdk 新增Module:File -> New -> Improt Module 选择opencv库的路径:D:\你的路径\OpenCV-android-sdk\sdk打开 build.gradle 文件并将implementation project(path: ':opencv')添加到依赖项部分: dependencies { ... implementation project(path: ':opencv') }

或者可以点击 File -> Project Structure -> Dependencies -> app -> + -> Module Dependency -> opencv 会自动在build.gradle中添加依赖

点击 File -> Sync Project with Gradle Files. 将以下配置添加到应用程序 build.gradle 文件中: 在 android -> defaultConfig -> externalNativeBuild -> cmake 部分,放入以下三行: cppFlags "-frtti -fexceptions" abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' arguments "-DOpenCV_DIR=" + opencvsdk + "/sdk/native" 将以下配置添加到 CMakeLists.txt 文件: 在 add_library 之前,添加以下三行: include_directories(${OpenCV_DIR}/jni/include) add_library( lib_opencv SHARED IMPORTED ) set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION ${OpenCV_DIR}/libs/${ANDROID_ABI}/libopencv_java4.so) 在 target_link_libraries 指令参数中,添加以下行: lib_opencv 最终的CMake文件如下: # 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) # 引入头文件搜索路径,这里找到的是opencv2 # OpenCV_DIR在app的buildgradle设置的 include_directories(${OpenCV_DIR}/jni/include) # 起名字为lib_opencv,SHARED,动态库 STATIC,静态库, IMPORTED外部导入 add_library( lib_opencv SHARED IMPORTED ) # 二进制文件ANDROID_ABI也在app的buildgradle设置的 set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION ${OpenCV_DIR}/libs/${ANDROID_ABI}/libopencv_java4.so) # Creates and names a library, sets it as either STATIC # or SHARED, and provides the relative paths to its source code. # You can define multiple libraries, and CMake builds them for you. # Gradle automatically packages shared libraries with your APK. # 本地库 add_library( # Sets the name of the library. native-lib # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). native-lib.cpp) # Searches for a specified prebuilt library and stores the path as a # variable. Because CMake includes system libraries in the search path by # default, you only need to specify the name of the public NDK library # you want to add. CMake verifies that the library exists before # completing its build. find_library( # Sets the name of the path variable. log-lib # Specifies the name of the NDK library that # you want CMake to locate. log) # Specifies libraries CMake should link to your target library. You # can link multiple libraries, such as libraries you define in this # build script, prebuilt third-party libraries, or system libraries. # link本地库和动态链接库 target_link_libraries( # Specifies the target library. native-lib lib_opencv # Links the target library to the log library # included in the NDK. ${log-lib})

如果你对其中的具体细节有不清楚的地方,这里的附录是CMake的使用

将以下权限添加到您的 AndroidManifest.xml 文件中,这里主要是相机的使用权限。 创建您的 MainActivity : 参考代码: package com.example.nativeopencvandroidtemplate; import android.Manifest; import android.app.Activity; import android.content.pm.PackageManager; import android.os.Bundle; import androidx.core.app.ActivityCompat; import android.util.Log; import android.view.SurfaceView; import android.view.WindowManager; import android.widget.Toast; import org.jetbrains.annotations.NotNull; import org.opencv.android.BaseLoaderCallback; import org.opencv.android.CameraBridgeViewBase; import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame; import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2; import org.opencv.android.LoaderCallbackInterface; import org.opencv.android.OpenCVLoader; import org.opencv.core.Mat; public class MainActivity extends Activity implements CvCameraViewListener2 { private static final String TAG = "MainActivity"; private static final int CAMERA_PERMISSION_REQUEST = 1; private CameraBridgeViewBase mOpenCvCameraView; private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) { @Override public void onManagerConnected(int status) { if (status == LoaderCallbackInterface.SUCCESS) { Log.i(TAG, "OpenCV loaded successfully"); // Load native library after(!) OpenCV initialization System.loadLibrary("native-lib"); mOpenCvCameraView.enableView(); } else { super.onManagerConnected(status); } } }; @Override public void onCreate(Bundle savedInstanceState) { Log.i(TAG, "called onCreate"); super.onCreate(savedInstanceState); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // Permissions for Android 6+ ActivityCompat.requestPermissions( this, new String[]{Manifest.permission.CAMERA}, CAMERA_PERMISSION_REQUEST ); setContentView(R.layout.activity_main); mOpenCvCameraView = findViewById(R.id.main_surface); mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE); mOpenCvCameraView.setCvCameraViewListener(this); } @Override public void onRequestPermissionsResult(int requestCode, @NotNull String[] permissions, @NotNull int[] grantResults) { if (requestCode == CAMERA_PERMISSION_REQUEST) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { mOpenCvCameraView.setCameraPermissionGranted(); } else { String message = "Camera permission was not granted"; Log.e(TAG, message); Toast.makeText(this, message, Toast.LENGTH_LONG).show(); } } else { Log.e(TAG, "Unexpected permission request"); } } @Override public void onPause() { super.onPause(); if (mOpenCvCameraView != null) mOpenCvCameraView.disableView(); } @Override public void onResume() { super.onResume(); if (!OpenCVLoader.initDebug()) { Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization"); OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION, this, mLoaderCallback); } else { Log.d(TAG, "OpenCV library found inside package. Using it!"); mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS); } } @Override public void onDestroy() { super.onDestroy(); if (mOpenCvCameraView != null) mOpenCvCameraView.disableView(); } @Override public void onCameraViewStarted(int width, int height) { } @Override public void onCameraViewStopped() { } @Override public Mat onCameraFrame(CvCameraViewFrame frame) { // get current camera frame as OpenCV Mat object Mat mat = frame.gray(); // native call to process current camera frame adaptiveThresholdFromJNI(mat.getNativeObjAddr()); // return processed frame for live preview return mat; } private native void adaptiveThresholdFromJNI(long mat); } 创建您的 activity_main.xml : 在 native-lib.cpp 中添加原生代码: #include #include #include #include #define TAG "NativeLib" using namespace std; using namespace cv; extern "C" { void JNICALL Java_com_example_nativeopencvandroidtemplate_MainActivity_adaptiveThresholdFromJNI(JNIEnv *env, jobject instance, jlong matAddr) { // get Mat from raw address Mat &mat = *(Mat *) matAddr; clock_t begin = clock(); cv::adaptiveThreshold(mat, mat, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 21, 5); // log computation time to Android Logcat double totalTime = double(clock() - begin) / CLOCKS_PER_SEC; __android_log_print(ANDROID_LOG_INFO, TAG, "adaptiveThreshold computation time = %f seconds\n", totalTime); } } 补充图文教程

关于图文教程,网上有很多,但是随着opencv和android studio的更新,有一些已经不适用了,所以上面主要写的着重于Cmakelist.txt的配置,build.gradle对于cmake配置, gradle.properties的配置以及它们之间的关系,学会了之后就不是照搬,可以举一反三。

具体的图文教程我觉得也有一篇不错的,但是肯定会遇到问题,所以在那边遇到问题的时候可以到这边来参考。

OpenCV 在 Android Studio 的使用教程



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有