Android integrated OpenCV (NDK)

1. Download the dynamic library (.so) of OpenCv

OpenCv official website , the download here is 4.6.0
insert image description here

2. Unzip opencv-4.6.0-android-sdk.zip

Copy the include folder of the directory opencv-4.6.0-android-sdk\OpenCV-android-sdk\sdk\native\jni, and opencv-4.6.0-android-sdk\OpenCV-android-sdk\sdk\native\libs The cpu architecture type corresponding to the \armeabi-v7a directory. There is no corresponding directory for the project, so create it manually.
insert image description here

3. Configure CMakeLists

You can refer to the Android NDK project CMakeLists.txt configuration to import third-party libraries

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

# Declares and names the project.

project("mnnmyh")

aux_source_directory(. SRC_LIST) # 当前文件夹下的所有文件,不包含子文件夹
add_library( # Sets the name of the library.
        mnnmyh
        # Sets the library as a shared library.
        SHARED
        # Provides a relative path to your source file(s).
        #native-lib.cpp)
        ${
    
    SRC_LIST})
        
#opencv
include_directories(${
    
    CMAKE_SOURCE_DIR}/opencv/include/)
add_library(libopencv_java4 SHARED IMPORTED)
set_target_properties(
        libopencv_java4
        PROPERTIES IMPORTED_LOCATION
        ${
    
    CMAKE_SOURCE_DIR}/../jniLibs/${
    
    ANDROID_ABI}/libopencv_java4.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)

target_link_libraries( # Specifies the target library.
        mnnmyh
        # Links the target library to the log library
        # included in the NDK.
        ${
    
    log-lib}
        #jnigraphics库可以用来从android.bitmap.Graphics类 访问C / C ++中的位图缓冲区
        jnigraphics
        libopencv_java4
        )

4. Test whether OpenCV is configured normally

Refer to github OpenCV to realize image grayscale conversion
First write a bitmap to mat tool class
utils.hpp

//
// Created by moyihen on 2022/8/17.
//

#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"

#ifdef __ANDROID__

#include <android/bitmap.h>
#include "android/log.h"

#define TAG "org.opencv.android.Utils"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
using namespace cv;

void BitmapToMat(JNIEnv *env, jobject bitmap, Mat &dst, jboolean needUnPremultiplyAlpha) {
    
    
    AndroidBitmapInfo info;
    void *pixels = 0;
    try {
    
    
        CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0);
        CV_Assert(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888 ||
                  info.format == ANDROID_BITMAP_FORMAT_RGB_565);
        CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0);
        CV_Assert(pixels);
        dst.create(info.height, info.width, CV_8UC4);
        if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
    
    
            LOGD("nBitmapToMat: RGBA_8888 -> CV_8UC4");
            Mat tmp(info.height, info.width, CV_8UC4, pixels);
            if (needUnPremultiplyAlpha) cvtColor(tmp, dst, COLOR_mRGBA2RGBA);
            else tmp.copyTo(dst);
        } else {
    
    
            // info.format == ANDROID_BITMAP_FORMAT_RGB_565
            LOGD("nBitmapToMat: RGB_565 -> CV_8UC4");
            Mat tmp(info.height, info.width, CV_8UC2, pixels);
            cvtColor(tmp, dst, COLOR_BGR5652RGBA);
        }
        AndroidBitmap_unlockPixels(env, bitmap);
        return;
    } catch (const cv::Exception &e) {
    
    
        AndroidBitmap_unlockPixels(env, bitmap);
        LOGE("nBitmapToMat caught cv::Exception: %s", e.what());
        jclass je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, e.what());
        return;
    } catch (...) {
    
    
        AndroidBitmap_unlockPixels(env, bitmap);
        LOGE("nBitmapToMat caught unknown exception (...)");
        jclass je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, "Unknown exception in JNI code {nBitmapToMat}");
        return;
    }
}
void MatToBitmap(JNIEnv *env, Mat &src, jobject bitmap, jboolean needPremultiplyAlpha) {
    
    
    AndroidBitmapInfo info;
    void *pixels = 0;
    try {
    
    
        LOGD("nMatToBitmap");
        CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0);
        CV_Assert(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888 ||
                  info.format == ANDROID_BITMAP_FORMAT_RGB_565);
        CV_Assert(src.dims == 2 && info.height == (uint32_t) src.rows &&
                  info.width == (uint32_t) src.cols);
        CV_Assert(src.type() == CV_8UC1 || src.type() == CV_8UC3 || src.type() == CV_8UC4);
        CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0);
        CV_Assert(pixels);
        if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
    
    
            Mat tmp(info.height, info.width, CV_8UC4, pixels);
            if (src.type() == CV_8UC1) {
    
    
                LOGD("nMatToBitmap: CV_8UC1 -> RGBA_8888");
                cvtColor(src, tmp, COLOR_GRAY2RGBA);
            } else if (src.type() == CV_8UC3) {
    
    
                LOGD("nMatToBitmap: CV_8UC3 -> RGBA_8888");
                cvtColor(src, tmp, COLOR_RGB2RGBA);
            } else if (src.type() == CV_8UC4) {
    
    
                LOGD("nMatToBitmap: CV_8UC4 -> RGBA_8888");
                if (needPremultiplyAlpha) cvtColor(src, tmp, COLOR_RGBA2mRGBA);
                else src.copyTo(tmp);
            }
        } else {
    
    
            // info.format == ANDROID_BITMAP_FORMAT_RGB_565
            Mat tmp(info.height, info.width, CV_8UC2, pixels);
            if (src.type() == CV_8UC1) {
    
    
                LOGD("nMatToBitmap: CV_8UC1 -> RGB_565");
                cvtColor(src, tmp, COLOR_GRAY2BGR565);
            } else if (src.type() == CV_8UC3) {
    
    
                LOGD("nMatToBitmap: CV_8UC3 -> RGB_565");
                cvtColor(src, tmp, COLOR_RGB2BGR565);
            } else if (src.type() == CV_8UC4) {
    
    
                LOGD("nMatToBitmap: CV_8UC4 -> RGB_565");
                cvtColor(src, tmp, COLOR_RGBA2BGR565);
            }
        }
        AndroidBitmap_unlockPixels(env, bitmap);
        return;
    } catch (const cv::Exception &e) {
    
    
        AndroidBitmap_unlockPixels(env, bitmap);
        LOGE("nMatToBitmap caught cv::Exception: %s", e.what());
        jclass je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, e.what());
        return;
    } catch (...) {
    
    
        AndroidBitmap_unlockPixels(env, bitmap);
        LOGE("nMatToBitmap caught unknown exception (...)");
        jclass je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, "Unknown exception in JNI code {nMatToBitmap}");
        return;
    }
}
#endif //__ANDROID__

The nativ method implements
native-lib.cpp

#include <jni.h>
#include <string>
#include "utils.hpp"

extern "C"
JNIEXPORT jstring JNICALL
Java_com_moyihen_mnnmyh_JniBitmapUtil_stringFromJNI(JNIEnv *env, jclass clazz) {
    
    
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}



extern "C"
JNIEXPORT void JNICALL
Java_com_moyihen_mnnmyh_JniBitmapUtil_coverImg2Gray(JNIEnv *env, jclass clazz, jobject bitmap) {
    
    
    //源图像
    Mat src;
    //将Bitmap转换为Mat
    BitmapToMat(env,bitmap,src, JNI_FALSE);

    //转换的灰度图
    Mat gray_image;
    cvtColor(src, gray_image, COLOR_BGR2GRAY );

    //将Mat转换为Bitmap
    MatToBitmap(env,gray_image,bitmap, JNI_FALSE);

    //释放Mat
    src.release();
    gray_image.release();
}
import android.graphics.Bitmap;

public class JniBitmapUtil {
    
    
    // Used to load the 'mnnmyh' library on application startup.
    static {
    
    
        System.loadLibrary("mnnmyh");
    }

    /**
     * A native method that is implemented by the 'mnnmyh' native library,
     * which is packaged with this application.
     */
    public static native String stringFromJNI();

    public static native void coverImg2Gray(Bitmap bitmap);
}

transfer

 mCvBinding.button.setOnClickListener(v -> {
    
    
            Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.hzw);
            JniBitmapUtil.coverImg2Gray(bitmap);

            mCvBinding.img2.setImageBitmap(bitmap);

        });

Effect
insert image description here

Note: There are several problems during debugging, you can refer to the following
1. dlopen failed: library “libc++_shared.so” not found You need to configure arguments “-DANDROID_STL=c++_shared”
insert image description here
in the app’s build.gradle

defaultConfig {
    
    
        externalNativeBuild {
    
    
            cmake {
    
    
                cppFlags '-std=c++11'
                arguments "-DANDROID_STL=c++_shared"
            }
        }
    }

2.error: undefined symbol: AndroidBitmap_getInfo
insert image description here
needs to add jnigraphics to target_link_libraries of CMakeLists.txt

target_link_libraries( # Specifies the target library.
        mnnmyh
        # Links the target library to the log library
        # included in the NDK.
        ${
    
    log-lib}
        #jnigraphics库可以用来从android.bitmap.Graphics类 访问C / C ++中的位图缓冲区
        jnigraphics
        libopencv_java4
        )

Guess you like

Origin blog.csdn.net/qq_35193677/article/details/126385431