Android NDK development - use CMake to encapsulate CPP files into so libraries and call so libraries

1. Create an NDK project

  1. Create an Android project:
    Create an NDK project:Insert image description here
  2. Configure NDK support:
    • Open the file in the project build.gradle(usually in appthe module's directory).
    • Add the following code in androidthe block, specifying the CMake version and NDK version:
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. Create C/C++ code folder:

    • In the project's app/src/maindirectory, create a cppfolder named.
    • In cppthe folder, create a C or C++ file, eg native-lib.cpp.
      Insert image description here
  2. Configure CMakeLists.txt:

    • In appthe directory of the module, create a CMakeLists.txtfile named, and use this make to import all the library dependencies used. I imported onnxruntime, OpenCV, Ncnn, and also imported the C++ code I wrote. C++ code only needs .cpp files.
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. Configure native-lib.cpp:
    Code for calling CPP in Jni:
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());
}

2. Package so library

The above roughly demonstrates how the NDK project imports and calls C++ code, but in many cases, the C++ implementation code cannot be given to others, so the .cpp file must be encapsulated into a .so file, leaving only the api interface for users to call. To encapsulate it into a .so file, the first step is to specify the directory where the so file is output, and the second is to specify which .cpp files are encapsulated into .so.

  1. Specify the SO output directory.
    Add the following statement in CMakeLists.txt to specify the path of the generated so output:
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI})
  1. Specify the .cpp file to generate so.
    I have many cpp files here. You can choose to import them all, or you can choose to import the .cpp file to be encapsulated.
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. Compile and generate a .so
    compilation project. After the compilation is completed, the compiled .so file can be found in the specified place:
    Insert image description here

3. Call library

By specifying the so file path in CMakeLists.txt, you don't need to rely on the .cpp file:

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

The overall CMakeLists.txt file changes as follows:

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)

Guess you like

Origin blog.csdn.net/matt45m/article/details/135096689