对于熟悉c++的人来说,如果能在android中写c++代码会是比较方便的,这时得用Cmake,配置并不复杂,甚至比ndk-build要简单,所以这一篇,就写用cmake导入opencv并在里面写一个例子的过程。我也是一边学一边摸索,一边踩坑一边记录经验,同时和大家分享交流一下。
bird2.jpg
我的环境工具版本:
Android 3.0.1
OpenCV 3.4.1
创建工程
首先,创建项目工程的时候,勾选include c++ support
1.jpg
一路next,到最后一步,如下:
10.jpg
如果没有装CMake,建好以后会报错,那么安装Cmake
安装CMake
点击File—Settings—Appearance & Behavior – System Settings – Android SDK – SDK Tools,会看到Cmake选项,后面是Not Installed,说明没有安装CMake。勾选CMake,点击OK,联网情况下将会自动完成安装。
2.jpg
将OpenCV下面的java文件夹导入项目中,File – New – Import Module 选择OpenCV-android-sdk\sdk\java导入,然后分别修改app和opencv的gradle中的版本号(详见前一篇)例如我的手机是android7.1.2的,所以SDK版本号全部设为了25。
在主模块中加入对OpenCV Library的依赖,File – Project structure –app –点右边“+”号,choose Module,选择opencvLibary341导入。
获得NDK/native的OpenCV支持。
具体来说:
把路径OpenCV-android-sdk\sdk\native\jni\include这个include文件夹整个拷到路径\app\src\main\cpp目录下
把路径为OpenCV-android-sdk\sdk\native\libs里面的几个文件夹,拷贝到\src\main\jniLibs下面。
检查App的gradle文件
android { compileSdkVersion 25 defaultConfig { applicationId "scr.face_detection4" minSdkVersion 25 targetSdkVersion 25 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" externalNativeBuild { cmake { cppFlags "-frtti -fexceptions" } } ndk{ abiFilters 'armeabi-v7a' } sourceSets.main{ jniLibs.srcDir 'src/main/jniLibs' // set .so files directory to libs jni.srcDirs = [] //disable automatic ndk-build call } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } externalNativeBuild { cmake { path "CMakeLists.txt" } } }
修改CmakeLists.txt
我的代码如下:
cmake_minimum_required(VERSION 3.4.1)# 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). src/main/cpp/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.set(OpenCV_DIR D:/AndroidSDK/OpenCV-android-sdk/sdk/native/jni) find_package(OpenCV REQUIRED)if(OpenCV_FOUND) include_directories(${OpenCV_INCLUDE_DIRS}) message(STATUS "OpenCV library status:") message(STATUS " version: ${OpenCV_VERSION}") message(STATUS " libraries: ${OpenCV_LIBS}") message(STATUS " include path: ${OpenCV_INCLUDE_DIRS}")else(OpenCV_FOUND) message(FATAL_ERROR "OpenCV library not found") endif(OpenCV_FOUND)set(CMAKE_VERBOSE_MAKEFILE on)set(libs "${CMAKE_SOURCE_DIR}/src/main/jniLibs") include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/include) add_library(libopencv_java3 SHARED IMPORTED ) set_target_properties(libopencv_java3 PROPERTIES IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_java3.so") 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.target_link_libraries( # Specifies the target library. native-lib # Links the target library to the log library # included in the NDK. ${log-lib} ${OpenCV_LIBS} -ljnigraphics libopencv_java3 )
去除OpenCV Manager 依赖
检查\src\main\jniLibs下面对应的发布平台的文件夹里是否有libopencv_java3.so文件,没有的话去OpenCV-android-sdk\sdk\native\libs里面拷一个
到MainActivity.java里面onCreate()函数前面加一句话
static { System.loadLibrary("opencv_java3"); System.loadLibrary("native-lib"); }
写个例子测试一下
调用相机,要修改几个文件
AndroidManifest.xml
在最外层里面加入:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.CAMERA"/><uses-feature android:name="android.hardware.camera" android:required="true"/><uses-feature android:name="android.hardware.camera.autofocus" android:required="true"/><uses-feature android:name="android.hardware.camera.front" android:required="true"/><uses-feature android:name="android.hardware.camera.front.autofocus" android:required="true"/>
编写native-lib.cpp
写了个简单测试,就做边缘Canny检测吧
jstring Java_scr_face_1detection4_MainActivity_stringFromJNI( JNIEnv *env, jobject /* this */) { std::string hello = "Hello from C++ Again"; return env->NewStringUTF(hello.c_str()); } void Java_scr_face_1detection4_MainActivity_nativeProcessFrame(JNIEnv *, jobject, jlong addrGray, jlong addrRgba) { Mat &mGr = *(Mat *) addrGray; Mat &mRgb = *(Mat *) addrRgba; Canny(mGr,mRgb,50,200); }
回到MainActivity.java,调用native-lib
public class MainActivity extends AppCompatActivity implements CvCameraViewListener2{ private static final String TAG = "MainActivity"; private Mat mRgba; private Mat mIntermediateMat; private Mat mGray; private CameraBridgeViewBase mOpenCvCameraView; // Used to load the 'native-lib' library on application startup. static { System.loadLibrary("opencv_java3"); System.loadLibrary("native-lib"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // Example of a call to a native method TextView tv = (TextView) findViewById(R.id.sample_text); tv.setText(stringFromJNI()); //add mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.tutorial2_activity_surface_view); mOpenCvCameraView.setVisibility(CameraBridgeViewBase.VISIBLE); mOpenCvCameraView.setCvCameraViewListener(this); mOpenCvCameraView.setClickable(true); } @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"); } else { Log.d(TAG, "OpenCV library found inside package. Using it!"); mOpenCvCameraView.enableView(); } } public void onDestroy() { super.onDestroy(); if (mOpenCvCameraView != null) mOpenCvCameraView.disableView(); } public void onCameraViewStarted(int width, int height) { mRgba = new Mat(height, width, CvType.CV_8UC4); mIntermediateMat = new Mat(height, width, CvType.CV_8UC4); mGray = new Mat(height, width, CvType.CV_8UC1); } public void onCameraViewStopped() { mRgba.release(); mGray.release(); mIntermediateMat.release(); } public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) { mRgba = inputFrame.rgba(); mGray = inputFrame.gray(); nativeProcessFrame(mGray.getNativeObjAddr(), mRgba.getNativeObjAddr()); return mRgba; }/** * A native method that is implemented by the 'native-lib' native library, * which is packaged with this application. */ public native String stringFromJNI(); public native void nativeProcessFrame(long matAddrGr, long matAddrRgba); }
运行就可以了。这样就是通过CMake调用cpp里面的OpenCV代码了。
运行效果图.jpg
调试中出现过的问题
Execution failed for task ':app:externalNativeBuildDebug'.
这个问题是上文中CmakeLists.txt中的set后的路径没写对造成的,仔细检查修改后就过了。
This adb server's $ADB_VENDOR_KEYS is not set
真机调试的时候遇到过这个问题,因为自己对AS调试还不太了解,连接好了就解决了。连接分两部分,一方面是AS里面的设置,按下图调好
3.jpg
作者:晚晴风_
链接:https://www.jianshu.com/p/439d8cc479bf