安卓NDK开发——使用CMake封装CPP文件成so库并调用so库
2023-12-20 07:31:01
一、创建NDK工程
- 创建Android工程:
创建一个NDK工程: - 配置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'
}
-
创建C/C++代码文件夹:
- 在项目的
app/src/main
目录下,创建一个名为cpp
的文件夹。 - 在
cpp
文件夹中,创建一个C或C++文件,例如native-lib.cpp
。
- 在项目的
-
配置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)
- 配置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 。
- 指定SO输出目录
在CMakeLists.txt里面添加下面语句来指定生成的so输出的路径:
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI})
- 指定生成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)
- 编译生成.so
编译工程,编译完成之后可以在指定的地方找到的编译好的.so文件:
三、调用库
在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)
文章来源:https://blog.csdn.net/matt45m/article/details/135096689
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!