安卓NDK开发——使用CMake封装CPP文件成so库并调用so库

一、创建NDK工程

  1. 创建Android工程:
    创建一个NDK工程:安卓NDK开发——使用CMake封装CPP文件成so库并调用so库_第1张图片
  2. 配置NDK支持:
    • 打开项目中的build.gradle文件(通常是在app模块的目录下)。
    • android块中添加以下代码,指定CMake版本和NDK版本:
apply plugin: 'com.android.application'

android {
    compileSdkVersion 24
    buildToolsVersion "29.0.2"

    defaultConfig {
        applicationId "com.dashu.scanjia"
        archivesBaseName = "$applicationId"

        ndk {
            moduleName "ScanJiaLib"
            abiFilters "armeabi-v7a", "arm64-v8a"
        }
        minSdkVersion 24
    }

    externalNativeBuild {
        cmake {
            version "3.10.2"
            path file('src/main/jni/CMakeLists.txt')
        }
    }
}

dependencies {
    implementation 'com.android.support:support-annotations:28.0.0'
}

  1. 创建C/C++代码文件夹:

    • 在项目的app/src/main目录下,创建一个名为cpp的文件夹。
    • cpp文件夹中,创建一个C或C++文件,例如native-lib.cpp
      安卓NDK开发——使用CMake封装CPP文件成so库并调用so库_第2张图片
  2. 配置CMakeLists.txt:

    • app模块的目录下,创建一个名为CMakeLists.txt的文件,把所有用到的库依赖都使用这个make进行导入,我这里导入了onnxruntime ,OpenCV,Ncnn,还有把自己编写的C++代码也导入,导入的C++代码只要.cpp文件。
project(ScanJiaLib)
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fopenmp")

if(DEFINED ANDROID_NDK_MAJOR AND ${ANDROID_NDK_MAJOR} GREATER 20)
    set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-openmp")
endif()

## opencv 库
set(OpenCV_DIR "${CMAKE_SOURCE_DIR}/sdk/native/jni")
find_package(OpenCV REQUIRED)

if (OpenCV_FOUND)
    message(STATUS "OpenCV_LIBS: ${OpenCV_LIBS}")
    message(STATUS "OpenCV_INCLUDE_DIRS: ${OpenCV_INCLUDE_DIRS}")
else ()
    message(FATAL_ERROR "opencv Not Found!")
endif (OpenCV_FOUND)


include(${CMAKE_CURRENT_SOURCE_DIR}/onnxruntime-shared/OnnxRuntimeWrapper.cmake)

find_package(OnnxRuntime REQUIRED)
if (OnnxRuntime_FOUND)
    message(STATUS "OnnxRuntime_LIBS: ${OnnxRuntime_LIBS}")
    message(STATUS "OnnxRuntime_INCLUDE_DIRS: ${OnnxRuntime_INCLUDE_DIRS}")
else ()
    message(FATAL_ERROR "onnxruntime Not Found!")
endif (OnnxRuntime_FOUND)

#ncnn库
set(ncnn_DIR ${CMAKE_SOURCE_DIR}/ncnn-20221128-android-vulkan/${ANDROID_ABI}/lib/cmake/ncnn)
find_package(ncnn REQUIRED)
set_target_properties(
        ncnn PROPERTIES
        INTERFACE_COMPILE_OPTIONS "-frtti;-fexceptions"
        # ncnn.cmake 里面是关的,把它重新打开防止跟opencv2冲突,如果是重新编译ncnn的请自己尝试要开还是关
)


add_library(ScanJia-jni SHARED ScanJia_jni.cpp AngleNet.cpp BitmapUtils.cpp clipper.cpp CrnnNet.cpp DbNet.cpp DocDewarp.cpp
        DocLayout.cpp DocumentCorrection.cpp DocumentEdge.cpp LayoutUtils.cpp OCRDoc.cpp
        OcrLite.cpp OcrResultUtils.cpp OcrUtils.cpp parmas.cpp praxis.cpp TextDirection.cpp)

target_link_libraries(ScanJia-jni ${OnnxRuntime_LIBS} ncnn ${OpenCV_LIBS} jnigraphics)

  1. 配置native-lib.cpp:
    在Jni中调用CPP的代码:
extern "C" JNIEXPORT jstring JNICALL
Java_com_dashu_scanjia_ScanJiaSim_OCR(JNIEnv *env,jobject, jobject image)
{
    int padding = 50;
    float boxScoreThresh = 0.6;
    float boxThresh = 0.3;
    float unClipRatio = 2.0;
    bool doAngle = true;
    bool mostAngle = true;

    int maxSideLen = 1024;

    cv::Mat imgRGBA, imgBGR, imgOut;
    bitmapToMat(env, image, imgRGBA);
    cv::cvtColor(imgRGBA, imgBGR, cv::COLOR_RGBA2BGR);
    int originMaxSide = (std::max)(imgBGR.cols, imgBGR.rows);
    int resize;
    if (maxSideLen <= 0 || maxSideLen > originMaxSide) {
        resize = originMaxSide;
    } else {
        resize = maxSideLen;
    }

    resize += 2*padding;
    cv::Rect paddingRect(padding, padding, imgBGR.cols, imgBGR.rows);
    cv::Mat paddingSrc = makePadding(imgBGR, padding);
    //按比例缩小图像,减少文字分割时间
    ppocr::ScaleParam s = ppocr::getScaleParam(paddingSrc, resize);//例:按长或宽缩放 src.cols=不缩放,src.cols/2=长度缩小一半
    ppocr::OcrResult ocrResult = ocrLite->detect(paddingSrc, paddingRect, s, boxScoreThresh, boxThresh,
                                          unClipRatio, doAngle, mostAngle);



    cv::cvtColor(ocrResult.boxImg, imgOut, cv::COLOR_BGR2RGBA);
    cv::resize(imgOut,imgOut,imgRGBA.size());
    matToBitmap(env, imgOut, image);


    return env->NewStringUTF(ocrResult.strRes.c_str());
}

二、封装so库

上面大概演示了NDK项目是如何导入与调用C++代码的,但很多时候,C++的实现代码并不能都给别人,所要把.cpp文件封装成.so文件,只留api接口给用户调用。要封装成.so文件分步,一是指定so文件输出的目录,二是指定哪些.cpp文件封装成.so 。

  1. 指定SO输出目录
    在CMakeLists.txt里面添加下面语句来指定生成的so输出的路径:
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI})
  1. 指定生成so的.cpp文件
    我这里有很多个cpp文件,可以选择全部导入,也可以选择导入要封装的.cpp文件。
add_library(ScanJia-jni SHARED ScanJia_jni.cpp AngleNet.cpp BitmapUtils.cpp clipper.cpp CrnnNet.cpp DbNet.cpp DocDewarp.cpp
        DocLayout.cpp DocumentCorrection.cpp DocumentEdge.cpp LayoutUtils.cpp OCRDoc.cpp
        OcrLite.cpp OcrResultUtils.cpp OcrUtils.cpp parmas.cpp praxis.cpp TextDirection.cpp)
  1. 编译生成.so
    编译工程,编译完成之后可以在指定的地方找到的编译好的.so文件:
    安卓NDK开发——使用CMake封装CPP文件成so库并调用so库_第3张图片

三、调用库

在CMakeLists.txt里面指定so文件路径,就可以不用依赖.cpp文件:

##1.添加第三方库
add_library(ScanJia-lib SHARED IMPORTED ScanJia_jni.cpp)
##2.添加库的路径
set_target_properties(ScanJia-lib
        PROPERTIES IMPORTED_LOCATION
        ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI}/libScanJia-jni.so)
target_link_libraries(ScanJia-jni ScanJia-lib  ${OnnxRuntime_LIBS} ncnn ${OpenCV_LIBS} jnigraphics)

整体的CMakeLists.txt文件变化如下:

project(ScanJiaLib)
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fopenmp")

if(DEFINED ANDROID_NDK_MAJOR AND ${ANDROID_NDK_MAJOR} GREATER 20)
    set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-openmp")
endif()


##封装时用到,指定so库的输出路径
#set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI})


## opencv 库
set(OpenCV_DIR "${CMAKE_SOURCE_DIR}/sdk/native/jni")
find_package(OpenCV REQUIRED)

if (OpenCV_FOUND)
    message(STATUS "OpenCV_LIBS: ${OpenCV_LIBS}")
    message(STATUS "OpenCV_INCLUDE_DIRS: ${OpenCV_INCLUDE_DIRS}")
else ()
    message(FATAL_ERROR "opencv Not Found!")
endif (OpenCV_FOUND)


include(${CMAKE_CURRENT_SOURCE_DIR}/onnxruntime-shared/OnnxRuntimeWrapper.cmake)

find_package(OnnxRuntime REQUIRED)
if (OnnxRuntime_FOUND)
    message(STATUS "OnnxRuntime_LIBS: ${OnnxRuntime_LIBS}")
    message(STATUS "OnnxRuntime_INCLUDE_DIRS: ${OnnxRuntime_INCLUDE_DIRS}")
else ()
    message(FATAL_ERROR "onnxruntime Not Found!")
endif (OnnxRuntime_FOUND)

#ncnn库
set(ncnn_DIR ${CMAKE_SOURCE_DIR}/ncnn-20221128-android-vulkan/${ANDROID_ABI}/lib/cmake/ncnn)
find_package(ncnn REQUIRED)
set_target_properties(
        ncnn PROPERTIES
        INTERFACE_COMPILE_OPTIONS "-frtti;-fexceptions"
        # ncnn.cmake 里面是关的,把它重新打开防止跟opencv2冲突,如果是重新编译ncnn的请自己尝试要开还是关
)


##调用.so文件
##1.添加第三方库
#add_library(ScanJia-lib SHARED IMPORTED ScanJia_jni.cpp)
##2.添加库的路径
#set_target_properties(ScanJia-lib
#        PROPERTIES IMPORTED_LOCATION
#        ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI}/libScanJia-jni.so)

add_library(ScanJia-jni SHARED ScanJia_jni.cpp AngleNet.cpp BitmapUtils.cpp clipper.cpp CrnnNet.cpp DbNet.cpp DocDewarp.cpp
        DocLayout.cpp DocumentCorrection.cpp DocumentEdge.cpp LayoutUtils.cpp OCRDoc.cpp
        OcrLite.cpp OcrResultUtils.cpp OcrUtils.cpp parmas.cpp praxis.cpp TextDirection.cpp)

target_link_libraries(ScanJia-jni ${OnnxRuntime_LIBS} ncnn ${OpenCV_LIBS} jnigraphics)

##另外加的.cpp文件
#add_library(ScanJia-jni SHARED ScanJia_jni.cpp)
##整体调用 
#target_link_libraries(ScanJia-jni ScanJia-lib  ${OnnxRuntime_LIBS} ncnn ${OpenCV_LIBS} jnigraphics)

你可能感兴趣的:(安卓,android,NDK,SO,opencv)