1. Create an NDK project
- Create an Android project:
Create an NDK project: - Configure NDK support:
- Open the file in the project
build.gradle
(usually inapp
the module's directory). - Add the following code in
android
the block, specifying the CMake version and NDK version:
- Open the file in the project
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'
}
-
Create C/C++ code folder:
- In the project's
app/src/main
directory, create acpp
folder named. - In
cpp
the folder, create a C or C++ file, egnative-lib.cpp
.
- In the project's
-
Configure CMakeLists.txt:
- In
app
the directory of the module, create aCMakeLists.txt
file 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.
- In
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)
- 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.
- 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})
- 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)
- Compile and generate a .so
compilation project. After the compilation is completed, the compiled .so file can be found in the specified place:
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)