news 2026/4/28 9:14:38

SuperResolution图像前后处理逻辑从C++迁移到Java的开发文档

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SuperResolution图像前后处理逻辑从C++迁移到Java的开发文档

本项目基于ai-engine-direct-helper (QAI_AppBuilder),相关 GitHub 链接:https://github.com/quic/ai-engine-direct-helper.git

本文档旨在阐述在 SuperResolution Android 示例项目中,将图像的预处理(Pre-processing)和后处理(Post-processing)逻辑从 C++ Native 层迁移到 Java 应用层的原因、实现方式以及相关的配置变更。

1.1迁移原因分析

将图像处理逻辑从 C++ 层迁移至 Java 层主要基于以下几点考虑,旨在优化项目的开发效率、可维护性及整体架构的清晰度。

1.1.1简化Native层职责

让 C++ Native 层(JNI)的职责更单一、更纯粹。迁移后,C++ 代码只负责核心的、计算密集型的推理任务(buffer-to-buffer),不再关心图像文件的读取、格式转换等 I/O 和图像处理操作。这使得 Native 层的代码更简洁,易于维护和复用。

1.1.2提升开发和调试效率

在 Android Studio 中,使用 Java/Kotlin 调试图像处理流程远比调试 C++ 代码方便。开发者可以轻松地设置断点、查看 Bitmap 对象、检查中间处理结果(例如,查看 Mat 对象的像素值),从而快速定位问题。

1.1.3更好地与Android UI框架集成

图像数据源于 Android UI(如图库选择),最终也要显示在 UI 上(如 ImageView)。将处理逻辑放在 Java 层,可以直接操作 Android 的 Bitmap 对象,避免了在 C++ 和 Java 之间频繁传递文件路径或进行复杂的对象转换,代码逻辑更顺畅。

1.1.4减少Native编译依赖

将 OpenCV 的使用完全移至 Java 层后,C++ 的构建系统(CMake)不再需要链接庞大的 OpenCV 库。这简化了 CMakeLists.txt 的配置,减少了 Native 库的体积和编译复杂度。

1.2实现方法

实现这一重构的核心思想是改变 JNI 接口的数据传递方式,从传递文件路径(String)改为传递直接内存缓冲区(java.nio.ByteBuffer)。

1.2.1修改C++ JNI接口

我们将原有的 JNI 函数签名从接收文件路径改为接收 ByteBuffer。

1.2.1.1修改前(Before)

cpp Java_com_example_superresolution_MainActivity_SuperResolution(JNIEnv* env,jobject,jstring j_libsDir,jstring j_model_path,jstring j_input_img,jstring j_output_img)

1.2.1.2修改后(After)

cpp Java_com_example_superresolution_MainActivity_SuperResolution(JNIEnv* env,jobject,jstring j_libsDir,jstring j_model_path,jobject j_inputBuffer,jobject j_outputBuffer)

在 C++ 实现中,我们使用 env->GetDirectBufferAddress() 来获取 ByteBuffer 的内存地址,从而直接读写由 Java 分配的内存。所有与 OpenCV 相关的代码(如 cv::imread, cv::resize, cv::imwrite 等)都已从 native-lib.cpp 中移除。

1.2.2Java中实现前后处理

我们在 MainActivity.java 中增加了两个核心方法:preprocess 和 postprocess。

1.2.2.1预处理(preprocess)

此方法负责将用户选择的 Bitmap 图像转换为模型推理所需的 ByteBuffer。

private ByteBuffer preprocess(Bitmap bitmap) { // 1. 将 Bitmap 转换为 OpenCV Mat Mat mat = new Mat(); Utils.bitmapToMat(bitmap, mat); // 2. 颜色空间转换 (Android Bitmap 是 RGBA, 模型可能需要 RGB) Imgproc.cvtColor(mat, mat, Imgproc.COLOR_RGBA2RGB); // 3. 归一化 (将像素值从 0-255 映射到 0.0-1.0) 并转为 32 位浮点型 mat.convertTo(mat, CvType.CV_32F, 1.0 / 255.0); // 4. 分配直接 ByteBuffer int bufferSize = IMAGE_WIDTH * IMAGE_HEIGHT * 3 * 4; // 3通道, float32 ByteBuffer byteBuffer = ByteBuffer.allocateDirect(bufferSize); byteBuffer.order(ByteOrder.nativeOrder()); // 5. 将 Mat 数据写入 ByteBuffer float[] data = new float[IMAGE_WIDTH * IMAGE_HEIGHT * 3]; mat.get(0, 0, data); byteBuffer.asFloatBuffer().put(data); return byteBuffer; }

1.2.2.2后处理(postprocess)

此方法负责将 Native 层返回的 ByteBuffer 结果转换回可供显示的 Bitmap 图像。

private Bitmap postprocess(ByteBuffer buffer, int width, int height) { // 1. 重置 buffer 指针,并读取 float 数据 buffer.rewind(); FloatBuffer floatBuffer = buffer.asFloatBuffer(); float[] data = new float[width * height * 3]; floatBuffer.get(data); // 2. 反归一化 (将 0.0-1.0 的值映射回 0-255) 并进行 clip byte[] byteData = new byte[data.length]; for (int i = 0; i < data.length; i++) { byteData[i] = (byte) Math.max(0, Math.min(255, data[i] * 255.0f)); } // 3. 将数据写入 Mat Mat mat = new Mat(height, width, CvType.CV_8UC3); mat.put(0, 0, byteData); // 4. 颜色空间转换 (从 RGB 转回 RGBA 以正确生成 Bitmap) Imgproc.cvtColor(mat, mat, Imgproc.COLOR_RGB2RGBA); // 5. 将 Mat 转换回 Bitmap Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Utils.matToBitmap(mat, bitmap); return bitmap; }

1.2.3更新Java层的调用逻辑

在 covertImageButton 的点击事件中,我们更新了调用流程,串联起预处理、Native 推理和后处理。

// ... 在 covertImageButton.setOnClickListener 中 ... // 1. 加载和缩放原始图片 Bitmap bitmap = BitmapFactory.decodeFile(input_img); Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, IMAGE_WIDTH, IMAGE_HEIGHT, true); // 2. 预处理 ByteBuffer inputBuffer = preprocess(resizedBitmap); // 3. 准备输出 Buffer int srWidth = SCALE * IMAGE_WIDTH; int srHeight = SCALE * IMAGE_HEIGHT; int outputBufferSize = srWidth * srHeight * 3 * 4; // float32 ByteBuffer outputBuffer = ByteBuffer.allocateDirect(outputBufferSize); outputBuffer.order(ByteOrder.nativeOrder()); // 4. 调用 Native 方法进行推理 SuperResolution(nativeLibPath, model_name, inputBuffer, outputBuffer); // 5. 后处理并显示结果 Bitmap outputBitmap = postprocess(outputBuffer, srWidth, srHeight); outputImagePreview.setImageBitmap(outputBitmap);

1.3依赖与配置变更

为了支持 Java 层的 OpenCV 功能,需要进行以下配置变更。

1.3.1 app/build.gradle.kts

在 dependencies 代码块中,添加 OpenCV 的官方 Android 依赖。

dependencies { // ... 其他依赖 implementation("org.opencv:opencv:4.9.0") }

1.3.2 MainActivity.java

为了让 App 在运行时能找到并加载 OpenCV 的 Native 库 (.so 文件),必须在调用任何 OpenCV API 之前对其进行初始化。这通常在 Activity 的 onCreate 方法的开头完成。

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 初始化 OpenCV if (!OpenCVLoader.initDebug()) { Log.e(TAG, "OpenCV initialization failed!"); } else { Log.d(TAG, "OpenCV initialization successful!"); } // ... 后续代码 }

1.3.3 app/src/main/cpp/CMakeLists.txt

由于 C++ 代码不再使用 OpenCV,我们移除了相关的头文件包含路径和库链接配置,以简化 Native 构建。

  • 移除 include_directories 中的 OpenCV 路径。
  • 移除 add_library(opencv ...) 的定义。
  • 移除 target_link_libraries 中的 opencv 链接。

通过以上步骤,我们成功地将前后处理逻辑迁移到了 Java 层,实现了更清晰的架构和更高效的开发流程。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/28 9:13:00

3步开启OBS RTSP服务器:让直播信号轻松分发到任意设备

3步开启OBS RTSP服务器&#xff1a;让直播信号轻松分发到任意设备 【免费下载链接】obs-rtspserver RTSP server plugin for obs-studio 项目地址: https://gitcode.com/gh_mirrors/ob/obs-rtspserver 你是否曾想过将OBS Studio的专业直播画面实时推送到监控系统、智能电…

作者头像 李华
网站建设 2026/4/28 9:12:23

Windows窗口置顶工具PinWin:告别频繁切换,提升多任务效率5倍

Windows窗口置顶工具PinWin&#xff1a;告别频繁切换&#xff0c;提升多任务效率5倍 【免费下载链接】PinWin Pin any window to be always on top of the screen 项目地址: https://gitcode.com/gh_mirrors/pin/PinWin 你是否厌倦了在多个窗口间来回切换的繁琐操作&…

作者头像 李华
网站建设 2026/4/28 9:08:53

保姆级教程:用UniApp连接智能手环(BLE),实现数据收发与监听

UniApp智能手环BLE开发实战&#xff1a;从数据采集到实时监控 在智能穿戴设备爆发的时代&#xff0c;手环类产品凭借其便携性和健康监测功能成为市场宠儿。作为开发者&#xff0c;如何快速构建与之配套的移动应用&#xff1f;UniApp跨平台框架配合BLE&#xff08;低功耗蓝牙&am…

作者头像 李华
网站建设 2026/4/28 9:07:00

SecureCRT与Xshell自动化脚本对比:VBS在两种终端下的实战写法与避坑指南

SecureCRT与Xshell自动化脚本深度对比&#xff1a;VBS实战技巧与工具选型指南 在运维工程师的日常工作中&#xff0c;终端工具的自动化能力往往决定了工作效率的上限。当需要管理数百台网络设备时&#xff0c;手动输入命令就像用勺子舀干游泳池——理论上可行&#xff0c;但没人…

作者头像 李华
网站建设 2026/4/28 9:00:58

【Unity】项目部署Linux服务器

1 Unity打包到Linux平台最近需要将Unity项目放到Linux平台上运行&#xff0c;也是研究了下。目前成功在linux的图形界面上正常运行&#xff1b;在命令行模式下因为虚拟桌面暂时没找到切换为独显渲染的方法&#xff0c;所以还不能运行。下面记录一下部署过程。系统版本&#xff…

作者头像 李华