Android基于CMake进行OpenCV开发配置

一、创建支持C++的android基础模版项目

新建项目,勾选【Include C++ support】,后续一直按【next】按钮取默认设置即可。

image.png

最终生成的目录结构如下图:

image.png

此时直接build并运行能正确看到效果。

打开MainActivity,可以看到生成的示例代码:

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Example of a call to a native method
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());
    }
    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();
}

再打开 native-lib.cpp 文件,也可以看到自动生成的JNI风格代码:

#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring

JNICALL
Java_com_woodstream_opencvdemo_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

其中Java_com_woodstream_opencvdemo_MainActivity_stringFromJNI就包含了java对应方法的不少信息。

为了看看IDE是怎么操作的,回到MainActivity,写入一个新的方法:

public native int[] gray(int[] buf, int w, int h);

image.png

此时方法名会标红,在IDE的智能提示下选择Create function xxx,会自动跳转到native-lib.cpp文件,并且自动添加了代码:

extern "C"
JNIEXPORT jintArray JNICALL
Java_com_woodstream_opencvdemo_MainActivity_gray(JNIEnv *env, jobject instance, jintArray buf_,
                                                 jint w, jint h) {
    jint *buf = env->GetIntArrayElements(buf_, NULL);

    // TODO

    env->ReleaseIntArrayElements(buf_, buf, 0);
}

就这么简单的步骤就可以进行JNI的开发,可见as大为减少我们编写JNI的工作量,而它又是怎么工作的?常规的makefile什么的不用了?

二、默认CMake的构建方式

  • 可以看到目录有一个CMakeLists.txt文件。
  • 打开build.gradle,看到有外部native编译的配置:

image.png

  • 打开app/build/intermediates/cmake目录,看到其子目录下生成了so文件:

image.png 从这几个信息可以知道as是通过CMake来完成上述操作的,是默认方式,当然也支持android.mk的方式,但比后者要方便不少。

三、用CMake的方式集成OpenCV

首先,可以先看下此文:Android 接入 OpenCV库的三种方式 除非你是用java写图像识别算法,不然不建议用第一种方式,因为图像识别实现考虑由C++来完成,所以选用第二种方式,此时如下步骤搭建OpenCV的环境:

  1. https://opencv.org/releases.html下载源码包并解压。
  2. 新建jniLibs文件夹,把OpenCV中sdk/native/libs内容复制到其下:

image.png

  1. 修改CMakeLists.txt文件:
# 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.
set(pathToOpenCv /Users/woodstream/Documents/dev/projects/android/OpenCV-android-sdk)#设置OpenCv的路径变量
cmake_minimum_required(VERSION 3.4.1)
#支持-std=gnu++11
set(CMAKE_VERBOSE_MAKEFILE on)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
#配置加载native依赖
include_directories(${pathToOpenCv}/sdk/native/jni/include)
#动态方式加载
add_library(lib_opencv STATIC IMPORTED ) #表示创建一个导入库,静态方式
#引入libopencv_java3.so文件
set_target_properties(lib_opencv
                       PROPERTIES
                       IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/src/main/jniLibs/${}/libopencv_java3.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).
             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.

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} android -ljnigraphics lib_opencv)

至此环境已经搭好了。

四、编写代码测试效果

完善C++文件native-lib.app先前生成的gray方法:

#include <jni.h>
#include <string>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

extern "C"
JNIEXPORT jintArray JNICALL
Java_com_eastcom_cpptest_MainActivity_gray(JNIEnv *env, jobject instance, jintArray buf, jint w,
                                           jint h) {
    jint *cbuf = env->GetIntArrayElements(buf, JNI_FALSE );
    if (cbuf == NULL) {
        return 0;
    }

    Mat imgData(h, w, CV_8UC4, (unsigned char *) cbuf);

    uchar* ptr = imgData.ptr(0);
    for(int i = 0; i < w*h; i ++){
        //计算公式:Y(亮度) = 0.299*R + 0.587*G + 0.114*B
        //对于一个int四字节,其彩色值存储方式为:BGRA
        int grayScale = (int)(ptr[4*i+2]*0.299 + ptr[4*i+1]*0.587 + ptr[4*i+0]*0.114);
        ptr[4*i+1] = grayScale;
        ptr[4*i+2] = grayScale;
        ptr[4*i+0] = grayScale;
    }

    int size = w * h;
    jintArray result = env->NewIntArray(size);
    env->SetIntArrayRegion(result, 0, size, cbuf);
    env->ReleaseIntArrayElements(buf, cbuf, 0);
    return result;
}

MainActivity改成:

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        Bitmap bitmap = ((BitmapDrawable) getResources().getDrawable(
                R.mipmap.pic_test)).getBitmap();
        int w = bitmap.getWidth(), h = bitmap.getHeight();
        int[] pix = new int[w * h];
        bitmap.getPixels(pix, 0, w, 0, 0, w, h);
        int [] resultPixes=gray(pix,w,h);
        Bitmap result = Bitmap.createBitmap(w,h, Bitmap.Config.RGB_565);
        result.setPixels(resultPixes, 0, w, 0, 0,w, h);

        ImageView img = (ImageView)findViewById(R.id.img2);
        img.setImageBitmap(result);
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native int[] gray(int[] buf, int w, int h);

    public native String stringFromJNI();
}

activity_main.xml布局文件改成:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="16dp"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:paddingTop="16dp"
    android:orientation="vertical"
    tools:context="com.eastcom.cpptest.MainActivity">

    <TextView
        android:id="@+id/txt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="原图:" />

    <ImageView
        android:id="@+id/img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/pic_test"/>


    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="处理后的图:" />

    <ImageView
        android:id="@+id/img2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

此时编译运行可以看到效果如图:

运行图

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏一个会写诗的程序员的博客

《Kotin 极简教程》第13章 使用 Kotlin 和 Anko 的Android 开发

Anko (https://github.com/Kotlin/anko) 是一个用 Kotlin 写的Android DSL (Domain-Specifi...

2453
来自专栏技术墨客

Spring核心——IOC功能扩展点 原

上一篇文章介绍了非侵入式的框架的概念以及IOC的功能扩展点之一——BeanPostProcessor,我们接下来的内容继续说明IoC更多的扩展方法。

952
来自专栏挖坑填坑

使用.net core ABP和Angular模板构建博客管理系统(创建前端菜单及页面)

1152
来自专栏JavaEdge

IoC容器初始化过程(下)1 BeanDefinition的载入和解析 2 BeanDefinition在IoC容器中的注册

3858
来自专栏wOw的Android小站

[Android][Framework] 添加系统服务

做系统开发,有时候需要自己定义一些接口供App使用, 同时为了方便维护管理,就会需要自己建立一个服务,把新的功能集中在一起。下面就是新建一个系统服务的基本步骤。

2852
来自专栏编程之路

羊皮书APP(Android版)开发系列(三)APP引导页启动控制类

1183
来自专栏mukekeheart的iOS之旅

android service 学习(下)

android service 学习(下)   通常每个应用程序都在它自己的进程内运行,但有时需要在进程间传递对象,你可以通过应用程序UI的方式写个运行在一...

2639
来自专栏程序猿DD

Spring Cloud Zuul重试机制探秘

作者:李刚 原文:http://www.spring4all.com/article/208 简介 本文章对应spring cloud的版本为(Dalston....

1.9K10
来自专栏技术小黑屋

Android处理崩溃的一些实践

对于任何程序来说,崩溃都是一件很难避免的事情,当然Android程序也不例外。在Android程序中,引起崩溃的多属于运行时异常或者错误,对于这些异常我们很难做...

1322
来自专栏androidBlog

ARouter 使用教程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/de...

2771

扫码关注云+社区

领取腾讯云代金券