手记

OpenCV for Android(3):使用Cmake生成调用c++代码

对于熟悉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


0人推荐
随时随地看视频
慕课网APP