一. 前言:为什么你需要自己构建图像处理 .dylib
我正在开发一款桌面端的图像编辑器 Monica(https://github.com/fengzhizi715/Monica),它使用的 UI 框架是 Compose Desktop。
在 Compose Desktop 中开发图像处理应用,往往意味着需要同时兼顾跨平台体验与图像处理性能。虽然 Kotlin 提供了丰富的 UI 构建能力,但在涉及图像编解码(如 HEIF)、图像分析(如 OpenCV)甚至 RAW 文件读取时,Java 层本身的能力极为有限,依赖的第三方库要么过于老旧,要么功能单一,性能表现也不尽如人意。
相比之下,C++ 在图像处理领域已经拥有成熟的生态:OpenCV 提供了全面的计算机视觉能力,libraw 支持绝大多数相机 RAW 文件格式,而 libheif 则是处理 HEIC/HEIF 图像的事实标准。这些库本身稳定且高效,但问题在于:它们并非为 Kotlin 或 Java 生态设计,也无法直接在 Compose Desktop 项目中使用。
我尝试过通过 JNI 来调用这些 C++ 库,但很快就会陷入一系列 macOS 特有的问题:
动态库路径错乱(@rpath、@loader_path 混用)
系统架构不一致(Apple Silicon 与 Intel 的 ABI 差异)
Homebrew 依赖冲突
打包后 .dylib 丢失等等。
这些都让简单的图像处理功能变得异常复杂,甚至直接影响了应用的可部署性。
另外,macOS 并没有像 Linux 那样统一的包管理方式可以在所有用户机器上复用 .so 文件,因此最终必须将所需的 .dylib 一并打包。也正因如此,构建一个跨架构、自包含、免安装的图像处理 dylib 成为了解决 Compose Desktop 图像处理问题的关键。本文从源码编译 OpenCV、libheif ,结合 CMake 构建 JNI 接口,并修复所有动态库引用,最终得到一个可以直接部署的图像处理模块。
二. 图像处理库的架构概览
在我的图像编辑器 Monica 中,很多核心的图像处理逻辑都是用 OpenCV C++ 实现的,所以我单独创建了一个项目 https://github.com/fengzhizi715/MonicaImageProcess
它用于实现图像的参数调整(对比度、色调、饱和度、亮度、色温、高光、阴影)、快速验证 OpenCV 算法(如各种滤波、图形增强、形态学操作、模板匹配等)、加载相机 RAW 文件、HEIC 图像等等。
其项目目录结构如下:
MonicaImageProcess/
├── README.md # 项目简介
├── LICENSE # 项目许可证文件
├── CMakeLists.txt # CMake构建脚本(主)
├── include/ # 项目公共头文件目录
│ ├── colorcorrection/ # 图像调色相关算法模块的头文件目录
│ ├── matchTemplate/ # 模版匹配的头文件目录
│ └── utils/ # 工具类模块的头文件目录
├── jni/ # 给 Java/Kotlin 调用的本地算法
│ ├── cn_netdiscovery_monica_opencv_ImageProcess.h # jni 对应用层暴露接口的头文件
│ └── cn_netdiscovery_monica_opencv_ImageProcess.cpp # jni 对应用层暴露接口的源文件
├── script/ # 构建脚本
│ ├── build_libheif_deps.sh # 从源码编译 libheif 及其依赖的库
│ ├── build_opencv_world_4100.sh # 从源码编译 opencv 库,包含了 contrib
│ └── fix_dylib_dependencies.sh # macOS 下修复主库引用 opencv 的路径
├── src/ # 项目主源代码目录
│ └── colorcorrection/ # 图像调色相关算法模块
│ └── ColorCorrection.cpp # 图像调色相关算法的源文件
│ └── matchTemplate/ # 模版匹配模块
│ └── MatchTemplate.cpp # 模版匹配相关算法的源文件
│ ├── utils/ # 工具类模块
│ ├── CMakeLists.txt # 本地算法的构建脚本
│ └── library.cpp # 常规使用 OpenCV 图像算法相关的源文件
├── thirdparty/ # 外部依赖库
│ └── heif-suite/ # libheif 库,需要基于 build_libheif_deps.sh 脚本编译,不同的芯片架构编译出来不同,所以没有放
│ └── libraw/ # libraw 库
│ └── opencv-install/ # opencv 库,需要基于 build_opencv_world_4100.sh 脚本编译,不同的芯片架构编译出来不同,所以没有放
└── .gitignore # git 忽略和不追踪的文件
三. 构建目标明确:跨架构、纯离线部署、适配 Compose Desktop
3.1 支持 Apple Silicon 与 Intel 架构双构建
macOS 正处于从 Intel 向 Apple Silicon(M 系芯片)过渡的阶段,但大量用户与开发者仍在使用 Intel 芯片机器。因此,构建的动态库必须支持 aarch64(arm64)与 x86_64 两种架构,以便在所有用户设备上运行。
这意味着所有依赖库(如 libheif、OpenCV)都必须支持交叉编译或分别构建。
编译参数中要精确控制 -arch 和 CMAKE_OSX_ARCHITECTURES,避免自动识别出错。
3.2 完整的纯离线部署能力
在 Compose Desktop 项目中,Native 动态库并不会像 jar 包那样可以通过 Gradle 自动解析依赖。这就要求打包的 .dylib 是:
自包含的,即所有依赖(如 libopencv_world、libz、libaom 等)都已经通过 @loader_path 正确链接。无需 Homebrew、无需 pkgconfig、无需系统路径解析。
可以被直接打包进 Compose Desktop 项目中的 resources,通过 System.loadLibrary() 加载。
为实现这一目标,在我们构建的脚本中专门设置了:
所有库使用 -static 或设置 BUILD_SHARED_LIBS=OFF(除 OpenCV)。
通过 install_name_tool 重写 dylib 引用为 @loader_path。
所有

最低0.47元/天 解锁文章
1992

被折叠的 条评论
为什么被折叠?



