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

在最外层里面加入:








  • 编写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

另一部分是手机的设置。

根据自己的手机设置,打开“开发者选项”设置“USB调试”,允许“USB”安装。就可以调试了。

  • Unknown failure (at android.os.Binder.execTransact(Binder.java:565))Error while Installing APKs

这问题开始也是不明白,上网查了一些方案以后,发现一种有效的方法,即——关闭MIUI优化

  • no implementation found for nativeProcessFrame:

这个是cpp文件中的写法有问题,后来我去掉了JNICALL JNIEXPORT就好了,出现了红绿小箭头,看起来建立了调用关联。

参考:

https://blog.csdn.net/sw5131899/article/details/77197133
https://blog.csdn.net/lplj717/article/details/77991365

你可能感兴趣的:(OpenCV for Android(3):使用Cmake生成调用c++代码)