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

另一部分是手机的设置。

根据自己的手机设置,打开“开发者选项”设置“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

最后编辑于
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352

推荐阅读更多精彩内容