Android NDK开发OpenCV系列:集成OpenCv(非OpenCv提供的Jni库)

目录

集成步骤

下载OpenCv的so库

OpenCV官网
这里我下载的是3.4版本

拷贝文件到项目

解压下载后的文件,opencv-3.4.13-android-sdk\OpenCV-android-sdk\sdk\native\libs这个目录下的便是OpenCv编译好的动态链接库,将整个libs文件夹拷贝到Android项目的cpp目录,由于为了减少apk大小,我这里只拷贝了"armeabi-v7a"和"arm64-v8a"目录下的库,然后找到opencv-3.4.2-android-sdk\OpenCV-android-sdk\sdk\native\jni\include目录,将include文件夹也拷贝到cpp目录,如下图所示:

配置CMake

对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.
#配置加载头文件
include_directories(include)
#动态方式加载
add_library(opencv SHARED IMPORTED)
#引入第三方.so库
set_target_properties(opencv PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libopencv_java3.so)


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
        #utils.cpp 这里不需要引入utils.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
        opencv 
        jnigraphics
        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})
配置gradle文件

这一步也很重要,目的是为了将so库打包到apk中,如果不配置这个会导致无法找到对应的库。

android {
...
    sourceSets {
            main {
                jniLibs.srcDirs = ['src/main/cpp/libs']
            }
        }
...
}

简单使用

这里简单的做一个将图片转换为灰度图的案例

创建转换工具

首先创建一个可以将Mat和Bitmap互相转换的工具,这里我参考了OpenCv给的案例(案例地址),自己简化了一下,代码如下:

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

#ifdef __ANDROID__

#include 
#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("org/opencv/core/CvException");
        if (!je) 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("org/opencv/core/CvException");
        if (!je) 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__
实现灰度转换

这里使用OpenCv的cvtColor方法进行转换
Java代码:

public class MainActivity extends AppCompatActivity {

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

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imgIcon = (ImageView) findViewById(R.id.img_icon);
    }

    /**
     * 将Bitmap转换为灰度图
     */
    public native void converImage2Gray(Bitmap bitmap);

    public void onClick(View view) {
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.icon);
        converImage2Gray(bitmap);
        imgIcon.setImageBitmap(bitmap);
    }
}

C++代码:

#include 
#include 
#include "utils.cpp"
extern "C"
JNIEXPORT void JNICALL
Java_com_itfitness_opencvincludedemo_MainActivity_converImage2Gray(JNIEnv *env, jobject instance,
                                                                   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();
}
效果展示

案例源码

https://gitee.com/itfitness/OpenCvIncludeDemo

你可能感兴趣的:(Android NDK开发OpenCV系列:集成OpenCv(非OpenCv提供的Jni库))