一、开发环境准备
1.1 本文开发环境版本对照表
| 工具 | 版本信息 |
|---|---|
| Android Studio | Hedgehog 2023.1.1 Patch 1 |
| JDK | JetBrains Runtime version 17.0.7 |
| Gradle Version | 8.2 |
| Android Gradle Plugin Version | 8.2.1 |
| Kotlin | 1.7.0 |
1.2 Java & Kotlin & Gradle 官方版本对照表:
1.3 新建一个Android Project
AndroidStudio中新建一个Native C++工程,C++ Standard选择的是C++ 11
Tips: Kotlin版本兼容问题
新建的Android Project由于一些本地的Gradle、Kotlin等配置差异,可能会存在Gradle、JDK、Kotlin版本不兼容导致APP构建失败,可以参考 版本信息对照表 修改一下版本号,列表中的版本都是相互兼容的,可以正常Build。默认新建项目时的Kotlin版本应该是1.9.x,与Gradle 8.2不兼容,因此此处像Kotlin版本改成了1.7.0(理论上小于等于1.8.20应该都行)
修改kotlin版本:
1) app/build.gradle.kts
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.7.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.0")
2) project/build.gradle.kts
id("org.jetbrains.kotlin.android") version "1.7.0" apply false
新建完的Project目录结构如下:(real-esrgan原名为nativa-lib, 我重命名过了)
连接真机调试:
第一阶段准备完毕🏆🏆🏆
二、NCNN部署
2.1 下载ncnn动态库
说明:-shared结尾的是动态库(.so),不带-shared是的静态库(.a)Adnroid端一般使用动态链接库(so库),然后通过JNI在Java层调用
2.2 将ncnn资源拷贝到项目中
1) 拷贝libncnn.so
在app/libs目录下新建需要的ABI父文件夹,然后将解压后的不同ABI目录下的libncnn.so拷贝到对应的目录下
2) 拷贝ncnn库文件
将解压后的任意一个ABI类型的下的 include 文件夹拷贝到 app/src/mian/cpp
2.3 修改JNI相关代码
1) 修改 CMakeLists.txt
cmake_minimum_required(VERSION 3.22.1)
project("realesrgan")
# 打印 ANDROID_ABI 以调试
message(STATUS "ANDROID_ABI: ${ANDROID_ABI}")
# 包含头文件
include_directories(${CMAKE_SOURCE_DIR})
include_directories(${CMAKE_SOURCE_DIR}/include)
# 引入 ncnn 动态库
add_library(ncnn SHARED IMPORTED)
set_target_properties(ncnn PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/../../../libs/${ANDROID_ABI}/libncnn.so)
# ../ 是一个相对路径符号,用于表示当前目录的上级目录,务必要和项目中的libncnn.so文件路径对应上
# 添加你的本地库
add_library(${CMAKE_PROJECT_NAME} SHARED real-esrgan.cpp)
# 链接库
target_link_libraries(
${CMAKE_PROJECT_NAME}
ncnn
android
log
)
2) 修改 app/build.gralde.kts
android {
defaultConfig{ ... }
...
//新增的内容
sourceSets {
getByName("main") {
jniLibs.srcDirs("libs")
}
}
}
3) Sync 一下项目,然后 Run 'app'
2.4 常见错误
* What went wrong:
Execution failed for task ':app:buildCMakeDebug[arm64-v8a]'.
> com.android.ide.common.process.ProcessException: ninja: Entering directory
`E:\Project\RealESRGAN\app\.cxx\Debug\1j5l121y\arm64-v8a'
C++ build system [build] failed while executing: @echo off "D:\\AndroidSdk\\sdk\\cmake\\3.22.1\\bin\\ninja.exe" ^ -C ^ "E:\\Project\\RealESRGAN\\app\\.cxx\\Debug\\1j5l121y\\arm64-v8a" ^ realesrgan from E:\Project\RealESRGAN\app
ninja: error: 'E:/Project/RealESRGAN/app/src/main/libs/arm64-v8a/libncnn.so', needed by 'E:/Project/RealESRGAN/app/build/intermediates/cxx/Debug/1j5l121y/obj/arm64-v8a/librealesrgan.so', missing and no known rule to make it
* Try:
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.
根据错误信息,问题在于CMake无法找到libncnn.so文件,需要检查CMakeLists.txt中指定的库文件路径是否正确
第二阶段部署完毕🏆🏆🏆
三、验证
3.1 简单验证一下ncnn的基础API
在文件app/src/main/cpp/real-esrgan.cpp添加一个Jni方法testNcnnBasicApi
extern "C"
JNIEXPORT void JNICALL
Java_com_xtc_realesrgan_engine_RealESRGANModel_testNcnnBasicApi(JNIEnv *env, jobject thiz) {
// 创建一个 ncnn::Mat 对象,尺寸为 3x3,包含 1 个通道
ncnn::Mat mat(3, 3, 1);
// 填充 Mat 数据
for (int y = 0; y < mat.h; y++) {
for (int x = 0; x < mat.w; x++) {
mat.channel(0)[y * mat.w + x] = static_cast<float>(x + y * mat.w);
}
}
// 执行一些简单的操作,例如矩阵加法
// 这里创建另一个相同尺寸的 Mat 并填充数据
ncnn::Mat mat2(3, 3, 1);
for (int y = 0; y < mat2.h; y++) {
for (int x = 0; x < mat2.w; x++) {
mat2.channel(0)[y * mat2.w + x] = static_cast<float>((x + y * mat2.w) * 2);
}
}
// 创建结果 Mat
ncnn::Mat result(3, 3, 1);
// 简单的逐元素相加
for (int y = 0; y < mat.h; y++) {
for (int x = 0; x < mat.w; x++) {
float val1 = mat.channel(0)[y * mat.w + x];
float val2 = mat2.channel(0)[y * mat2.w + x];
result.channel(0)[y * result.w + x] = val1 + val2;
}
}
// 将结果转换为字符串
std::string resultStr = "ncnn 基础 API 验证!\n结果矩阵:\n";
for (int y = 0; y < result.h; y++) {
for (int x = 0; x < result.w; x++) {
char buffer[16];
sprintf(buffer, "%.1f ", result.channel(0)[y * result.w + x]);
resultStr += buffer;
}
resultStr += "\n";
}
LOGI("%s", resultStr.c_str());
}
同步匹配的Native方法如下:
object RealESRGANModel {
init {
System.loadLibrary("realesrgan")
}
external fun stringFromJNI(): String
external fun testNcnnBasicApi()
}
最后运行代码后观察日志输出如下结果说明ncnn集成成功:
18:08:11.284 I ncnn 基础 API 测试成功!
结果矩阵:
0.0 3.0 6.0
9.0 12.0 15.0
18.0 21.0 24.0
3.2 模型加载及推理验证
//todo