Android OpenCV Native C++实现灰度图片

最近帮一位读者朋友在Android平台上集成部分OpenCV功能,考虑到移动端的特殊性,不建议采用全包集成,同时,市面上Android手机基本上都是基于arm的指令集。综合考虑,采用c++ & native库的方式来实现OpenCV能力的集成。

环境

  • Android Studio 4.1.1
  • OpenCV 4.5.0
  • NDK 21.1.6352462
  • CMake 3.10.2

环境搭建

下载opencv-4.5.0-android-sdk

下载地址:https://sourceforge.net/projects/opencvlibrary/files/4.5.0/opencv-4.5.0-android-sdk.zip/download

目录结构:

OpenCV-android-sdk
|_ samples
|_ sdk
|    |_ etc
|    |_ java
|    |_ libcxx_helper
|    |_ native
|          |_ 3rdparty
|          |_ jni
|          |_ libs
|               |_ arm64-v8a
|               |_ armeabi-v7a
|               |_ x86
|               |_ x86_64
|          |_ staticlibs
|               |_ arm64-v8a
|               |_ armeabi-v7a
|               |_ x86
|               |_ x86_64
|
|_ LICENSE
|_ README.android
目录文件
samplesOpenCV运行案例
sdkOpenCV API以及依赖库
sdk/etcHaar和LBP级联分类器
sdk/javaOpenCV Java API
sdk/libcxx_helperbring libc++_shared.so into packages
sdk/nativeOpenCV 静态库、动态库以及JNI文件

后续会使用到的静态库在sdk/native/staticlibs、sdk/native/3rdparty目录下,然后编译依赖关系参考sdk/native/jni目录下的cmake文件。

下载安装NDK

NDK下载安装

下载安装CMake

CMake下载安装

创建Android Native C++ Project

Native C++工程创建

OpenCV Native C++集成

编译配置

  • 设置NDK版本

    ndkVersion '21.1.6352462'
    
  • 设置cmake编译条件

    defaultConfig {
        ……
        // defaultConfig节点下
        externalNativeBuild {
            cmake {
                abiFilters 'armeabi-v7a','arm64-v8a'
                cppFlags "-std=c++11","-frtti", "-fexceptions -lz"
            }
        }
        ndk {
            abiFilters 'armeabi-v7a','arm64-v8a'
        }
    }
    // android 节点下
    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
            version "3.10.2"
        }
    }
    // 新建jniLibs存放静态库
    sourceSets {
        main {
            jniLibs.srcDirs = ['src/main/jniLibs', 'lib']
        }
    }
    

OpenCV头文件拷贝

Native 库头文件(sdk/native/jni/include)拷贝到 src/main/cpp目录下。结果如下:

OpenCV 头文件导入

OpenCV静态库拷贝

由于我们实现灰度图片,只需要使用opencv_core和opencv_imgproc两个库,根据编译文件梳理大致的依赖关系,需要导入的库不多,具体如下。

OpenCV静态库导入

CMakeLists.txt

参考sdk/native/jni下的OpenCVModules.cmake文件调整对应的编译内容。

# 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.

cmake_minimum_required(VERSION 3.10.2)

# Declares and names the project.

project("simplifyopencv")


set(libs ${CMAKE_SOURCE_DIR}/..)
include_directories(${libs}/cpp/include)

# Create imported target libcpufeatures
add_library(libcpufeatures STATIC IMPORTED)
set_target_properties(libcpufeatures PROPERTIES
        IMPORTED_LOCATION "${libs}/jniLibs/${ANDROID_ABI}/libcpufeatures.a")

# Create imported target ittnotify
add_library(ittnotify STATIC IMPORTED)
set_target_properties(ittnotify PROPERTIES
        INTERFACE_LINK_LIBRARIES "dl"
        )
set_target_properties(ittnotify PROPERTIES
        IMPORTED_LOCATION "${libs}/jniLibs/${ANDROID_ABI}/libittnotify.a")

# Create imported target tegra_hal
add_library(tegra_hal STATIC IMPORTED)
set_target_properties(tegra_hal PROPERTIES
        IMPORTED_LOCATION "${libs}/jniLibs/${ANDROID_ABI}/libtegra_hal.a")

# Create imported target tbb
add_library(tbb STATIC IMPORTED)
set_target_properties(tbb PROPERTIES
        INTERFACE_COMPILE_DEFINITIONS "TBB_USE_GCC_BUILTINS=1;__TBB_GCC_BUILTIN_ATOMICS_PRESENT=1;TBB_SUPPRESS_DEPRECATED_MESSAGES=1"
        INTERFACE_LINK_LIBRARIES "c;m;dl"
        )
set_target_properties(tbb PROPERTIES
        IMPORTED_LOCATION "${libs}/jniLibs/${ANDROID_ABI}/libtbb.a")

# Create imported target opencv_core
add_library(opencv_core STATIC IMPORTED)
set_target_properties(opencv_core PROPERTIES
        INTERFACE_LINK_LIBRARIES "\$<LINK_ONLY:dl>;\$<LINK_ONLY:m>;\$<LINK_ONLY:log>;\$<LINK_ONLY:tegra_hal>;\$<LINK_ONLY:tbb>;\$<LINK_ONLY:z>;\$<LINK_ONLY:libcpufeatures>;\$<LINK_ONLY:ittnotify>;\$<LINK_ONLY:tegra_hal>"
        )
set_target_properties(opencv_core PROPERTIES
        IMPORTED_LOCATION "${libs}/jniLibs/${ANDROID_ABI}/libopencv_core.a")

# Create imported target opencv_imgproc
add_library(opencv_imgproc STATIC IMPORTED)
set_target_properties(opencv_imgproc PROPERTIES
        INTERFACE_LINK_LIBRARIES "\$<LINK_ONLY:opencv_core>;opencv_core;\$<LINK_ONLY:dl>;\$<LINK_ONLY:m>;\$<LINK_ONLY:log>;\$<LINK_ONLY:tegra_hal>"
        )
set_target_properties(opencv_imgproc PROPERTIES
        IMPORTED_LOCATION "${libs}/jniLibs/${ANDROID_ABI}/libopencv_imgproc.a")


# 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).
        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

        opencv_core
        opencv_imgproc
        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

代码

定义Native方法

private external fun doGray(imgData: IntArray?, width: Int, height: Int): IntArray?

实现Native方法

#include <jni.h>
#include <string>
#include "include/opencv2/core/hal/interface.h"
#include "include/opencv2/imgproc/types_c.h"
#include "include/opencv2/imgproc.hpp"

using namespace std;
using namespace cv;

extern "C" JNIEXPORT jintArray  JNICALL
Java_tech_kicky_simplifyopencv_MainActivity_doGray(
        JNIEnv *env, jobject thiz, jintArray source,
        jint width, jint height) {
    // TODO: implement convertToGray()
    jint *input;
    input = env->GetIntArrayElements(source, JNI_FALSE);

    Mat sourceMat(height, width, CV_8UC4, (unsigned char *) input);

    Mat grayMat;
    cvtColor(sourceMat, grayMat, CV_BGRA2GRAY);

    Mat resultMat;
    cvtColor(grayMat, resultMat, CV_GRAY2BGRA);
    int size = width * height;
    jintArray result = env->NewIntArray(size);
    uchar *output = resultMat.data;
    env->SetIntArrayRegion(result, 0, size, (const jint *) output);
    env->ReleaseIntArrayElements(source, input, 0);
    return result;
}

Kotlin调用

class MainActivity : AppCompatActivity() {
    companion object {
        // Used to load the 'native-lib' library on application startup.
        init {
            System.loadLibrary("native-lib")
        }
    }

    private val mBinding by lazy { ActivityMainBinding.inflate(layoutInflater) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(mBinding.root)

        mBinding.ivLena.setImageResource(R.drawable.lena)
        val source = BitmapFactory.decodeResource(resources, R.drawable.lena)
            .copy(Bitmap.Config.ARGB_8888, true)
        val width = source.width
        val height = source.height
        val pixel = IntArray(width * height)
        source.getPixels(pixel, 0, width, 0, 0, width, height)
        val grayPixels: IntArray? = doGray(pixel, width, height)

        val gray = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
        gray.setPixels(grayPixels, 0, width, 0, 0, width, height)
        mBinding.ivResult.setImageBitmap(gray)
    }

    private external fun doGray(imgData: IntArray?, width: Int, height: Int): IntArray?

}

效果

灰度图片

源码

https://github.com/onlyloveyd/SimplifyOpenCV

onlyloveyd CSDN认证博客专家 Android Kotlin OpenCV
个人公众号【OpenCV or Android】,热爱Android、Kotlin、Flutter和OpenCV。毕业于华中科技大学计算机专业,曾就职于华为武汉研究所。目前在三线小城市生活,专注技术与研发。
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页