# libjpeg-ai-c **Repository Path**: H-kernel/libjpeg-ai-c ## Basic Information - **Project Name**: libjpeg-ai-c - **Description**: JPEG-AI熵编码的C/C++实现,参考https://jpeg.org/jpegai/index.html规范 - **Primary Language**: Unknown - **License**: BSD-3-Clause - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-31 - **Last Updated**: 2026-06-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # libjpeg-ai-c ![License](https://img.shields.io/badge/license-BSD--style-blue) ![C++17](https://img.shields.io/badge/C++-17-00599C) ![CMake](https://img.shields.io/badge/build-CMake-064F8C) ![JPEG-AI](https://img.shields.io/badge/codec-JPEG--AI-2E7D32) ![ONNX Runtime](https://img.shields.io/badge/inference-ONNX%20Runtime-1f6feb) ![OpenCV](https://img.shields.io/badge/image-OpenCV-5C3EE8) ## 项目简介 `libjpeg-ai-c` 是 JPEG-AI 参考软件推理链路的 C/C++ 移植工作区,目标是在本仓库内提供可构建、可验证、可通过 C 接口调用的编码和解码实现。当前实现覆盖 CMake 工程、配置覆盖、图像读写、ONNX Runtime 会话封装、JPEG-AI ONNX 模型定位、开发码流封装、图像头、mANS 熵编码、命令行工具、共享库、静态库和发布目录生成脚本。 参考实现来源为 `https://gitlab.com/wg1/jpeg-ai/jpeg-ai-reference-software.git`。本项目优先移植编码器和解码器推理路径,训练逻辑和离线模型生成逻辑不作为运行时依赖。 当前码流是本项目用于开发和验证的结构,用来承载图像头、Z 子流、工具元数据子流和残差子流;它不声明为最终标准化 JPEG-AI bitstream ABI,也不应作为跨实现长期兼容承诺使用。 ## 编码与解码原理 编码端通过 OpenCV 读取输入图像,转换为内部 `jpegai::Image`,再按主亮度平面和辅助色度平面准备张量。每个平面调用对应的 `analysis_*` ONNX 模型得到潜变量 `y`,再调用超先验编码器得到超先验潜变量 `z`。 `z` 被量化为有限符号并交给 `third_party/jpeg-ai-mans` 中的因子化 mANS 后端编码。`y` 的残差由超先验解码器输出的均值、超先验尺度解码器输出的尺度索引、增益单元、RVS/GRFS、跳过掩码和立方掩码共同约束,再通过 SGM/R mANS 编码。编码器把图像头、模型选择、图像尺寸、平面元数据和各熵编码载荷写入当前开发码流。 解码端按相反顺序工作:先读取图像头和子流目录,恢复模型索引、分析阶段、图像尺寸、量化参数和工具元数据;再解码 Z 子流恢复 `z`,调用超先验解码器与超先验尺度解码器重建残差解码所需上下文;随后解码残差并反量化恢复潜变量 `y`;最后调用匹配的 `synthesis_*` ONNX 模型生成重建图像,并通过 OpenCV 写出 PNG 或 JPEG。 ## 依赖组件 - `CMake`:工程生成和目标构建。 - `C++17 compiler`:构建核心库、C 接口库、命令行工具和测试程序。 - `OpenSSL Crypto`:提供载荷 MD5 等校验辅助能力。 - `OpenCV core/imgcodecs/imgproc/photo`:负责图像读写、颜色转换、图像处理和解码后修复处理。 - `OpenMP`:在 mANS 后端可用时提供并行支持。 - `Python3`:CMake 配置阶段需要 `Python3 Interpreter`,测试模型生成和参考分析脚本也会使用它。 - `ONNX Runtime CPU EP`:默认推理后端。 - `ONNX Runtime CUDA EP`:可选 GPU 推理后端。 - `third_party/jpeg-ai-mans`:提供因子化 Z 和 SGM/R 残差的 mANS 熵编码实现。 - `models/jpeg-ai/onnx`:仓库内置 JPEG-AI ONNX 模型目录,也是默认模型根目录。 ## 构建命令与产物 ```bash cmake -S . -B build cmake --build build ``` 主要产物如下: - `build/libjpegai.so`:C 接口共享库。 - `build/libjpegai.a`:C 接口静态库;发布脚本会把 C 接口层、核心实现和 mANS 内部静态库合并为发布目录中的 `lib/libjpegai.a`。 - `build/jpegai_enc`:编码命令行工具。 - `build/jpegai_dec`:解码命令行工具。 指定 ONNX Runtime 安装目录时使用: ```bash cmake -S . -B build -DONNXRUNTIME_ROOT=/opt/timehunter/onnxruntime/1.24.4-cuda cmake --build build --target jpegai_shared jpegai_static jpegai_enc jpegai_dec ``` ## 命令行编码与解码 使用仓库内置模型目录: ```bash ./build/jpegai_enc --jpegai-model-root models/jpeg-ai/onnx tests/test.jpg tests/test.bits --model-index 0 --analysis-stage 1 --quant-step 1.0 --threads 1 ./build/jpegai_dec --jpegai-model-root models/jpeg-ai/onnx tests/test.bits tests/test-restored.jpg --quant-step 1.0 ``` 普通源码构建会通过 `JPEGAI_DEFAULT_MODEL_ROOT` 编译进源码树中的默认模型绝对路径,因此下面的形式依赖构建时记录的源码树位置,而不是简单查找当前目录下的相对 `models/jpeg-ai/onnx`: ```bash ./build/jpegai_enc --jpegai-model-root tests/test.jpg tests/test.bits --threads 1 ./build/jpegai_dec --jpegai-model-root tests/test.bits tests/test-restored.jpg ``` 发布目录中的示例和 C 接口配置应显式传入 `model_root="models/jpeg-ai/onnx"`,或者从发布目录根目录运行并使用 `config/jpegai.json` 中的相对配置。 ## 测试效率与还原结论 使用 `artifacts/test.jpg` 执行了一次新 C API 服务模式端到端压缩和还原测试,测试命令如下: ```bash mkdir -p artifacts/test_roundtrip cp artifacts/test.jpg artifacts/test_roundtrip/original.jpg ./build/jpegai_capi_perf --jpegai-model-root models/jpeg-ai/onnx --input artifacts/test_roundtrip/original.jpg --work-dir artifacts/test_roundtrip/perf --concurrency 1 --loops 1 --model-index 0 --analysis-stage 1 --quant-step 1.0 --threads 1 cp artifacts/test_roundtrip/perf/worker-0-loop-0.bits artifacts/test_roundtrip/test.bits cp artifacts/test_roundtrip/perf/worker-0-loop-0.png artifacts/test_roundtrip/test-restored.png ./build/jpegai_dec --jpegai-model-root models/jpeg-ai/onnx artifacts/test_roundtrip/test.bits artifacts/test_roundtrip/test-restored.jpg --quant-step 1.0 --output-quality 92 ``` 测试产物保存在 `artifacts/test_roundtrip/`,其中包含原始测试图、开发码流、还原 JPG、无损容器 PNG、差分热图、signed diff 和指标报告。 ### 图像对比 | 测试前原图 | 码流还原后 JPG | | --- | --- | | ![测试前原图](artifacts/test_roundtrip/original.jpg) | ![码流还原后 JPG](artifacts/test_roundtrip/test-restored.jpg) | | 差异热图,误差放大 12 倍 | Signed diff,差异值 +128 | | --- | --- | | ![差异热图](artifacts/test_roundtrip/test-diff-heatmap-x12.png) | ![Signed diff](artifacts/test_roundtrip/test-signed-diff-plus128.png) | | 指标 | 数值 | | --- | ---: | | 输入图像 | `artifacts/test_roundtrip/original.jpg` | | 图像尺寸 | 1280 x 720 x 3 | | 原始 JPEG 大小 | 152292 bytes | | 开发码流大小 | 76955 bytes | | 还原 JPG 大小 | 146634 bytes | | 还原 JPG / 原始 JPEG 大小比 | 0.9628 | | 码流 bpp | 0.6680 | | 原始 JPEG / 码流大小比 | 1.9790 | | 输出 JPEG 质量 | 92 | | 服务模式初始化延迟 | 537.021 ms | | 服务模式单次编码延迟 | 1579.870 ms | | 服务模式单次解码延迟 | 868.544 ms | | RGB PSNR | 30.841 dB | | 亮度 PSNR | 32.482 dB | | 亮度 SSIM | 0.889312 | | RGB MAE | 4.823 | | RGB RMSE | 7.320 | | 最大绝对误差 | 116 | | 95% 绝对误差 | 15 | | 99% 绝对误差 | 27 | | 还原亮度高频 RMS | 11.335 | | 原图亮度高频 RMS | 17.376 | | 平坦区域亮度误差标准差 | 1.581 | | 平坦区域亮度误差 MAE | 1.036 | 测试结论:当前参数下码流大小约为原始 JPEG 的 50.5%,压缩效率约为 1.98:1。默认 JPEG 输出质量为 92 时,还原 JPG 大小为原始 JPEG 的 96.28%。新 C API 服务模式把模型和 ONNX session 预热移动到 `jpegai_init()`,本次单次测试中初始化耗时约 537 ms,初始化后的编码约 1580 ms、解码约 869 ms;多轮服务进程测试中会进一步摊薄初始化成本。还原图 RGB PSNR 为 30.841 dB、亮度 SSIM 为 0.889312,平坦区域亮度误差 MAE 为 1.036,整体还原质量与旧测试保持一致。 ## C 接口 C 接口头文件为 `include/jpegai/jpegai_c.h`。动态库和静态库都导出同一组 `jpegai_*` 函数;使用静态库时可在调用方定义 `JPEGAI_STATIC`。 配置结构体为: ```c typedef struct jpegai_config { const char* model_root; int model_index; int analysis_stage; float quant_step; int threads; int output_quality; int concurrent_handles; } jpegai_config_t; ``` 字段含义如下: - `model_root`:JPEG-AI ONNX 模型根目录,通常为 `models/jpeg-ai/onnx`。 - `model_index`:模型索引,当前常用值为 `0`。 - `analysis_stage`:分析模型阶段,当前常用值为 `1`。 - `quant_step`:开发编解码器的量化步长。 - `threads`:熵编码等阶段可使用的线程数。 - `output_quality`:还原图写为 JPEG 时的质量参数,范围为 `1..100`,默认 `92`;PNG 等无损格式不使用该字段。 - `concurrent_handles`:服务进程内预分配的编码器和解码器句柄数,默认 `1`。 函数如下: - `jpegai_init()`:按 `jpegai_config_t` 创建服务上下文,并按 `concurrent_handles` 预分配编解码资源。 - `jpegai_destroy()`:释放 `jpegai_context_t` 和所有预分配资源。 - `jpegai_acquire_encoder()` / `jpegai_release_encoder()`:获取和释放编码器句柄。 - `jpegai_acquire_decoder()` / `jpegai_release_decoder()`:获取和释放解码器句柄。 - `jpegai_encode_file()`:用编码器句柄把输入图像编码为当前开发码流。 - `jpegai_decode_file()`:用解码器句柄把当前开发码流解码为输出图像。 - `jpegai_write_config()`:把配置写为 JSON 文件。 - `jpegai_last_error()`:读取上下文最近一次错误信息。 返回码如下: - `JPEGAI_STATUS_OK`:成功。 - `JPEGAI_STATUS_INVALID_ARGUMENT`:参数为空、路径无效或字段不合法。 - `JPEGAI_STATUS_IO_ERROR`:文件读写失败。 - `JPEGAI_STATUS_MODEL_ERROR`:ONNX 模型加载或推理失败。 - `JPEGAI_STATUS_CODEC_ERROR`:码流或编解码流程失败。 - `JPEGAI_STATUS_INTERNAL_ERROR`:未归类内部错误。 ## C 调用示例 ```c #include #include "jpegai/jpegai_c.h" int main(void) { jpegai_config_t cfg; cfg.model_root = "models/jpeg-ai/onnx"; cfg.model_index = 0; cfg.analysis_stage = 1; cfg.quant_step = 1.0f; cfg.threads = 1; cfg.output_quality = 92; cfg.concurrent_handles = 1; jpegai_context_t* ctx = NULL; jpegai_status_t st = jpegai_init(&cfg, &ctx); if (st != JPEGAI_STATUS_OK) { return 1; } jpegai_encoder_t* enc = NULL; jpegai_decoder_t* dec = NULL; st = jpegai_acquire_encoder(ctx, &enc); if (st != JPEGAI_STATUS_OK) { fprintf(stderr, "%s\n", jpegai_last_error(ctx)); jpegai_destroy(ctx); return 1; } st = jpegai_acquire_decoder(ctx, &dec); if (st != JPEGAI_STATUS_OK) { fprintf(stderr, "%s\n", jpegai_last_error(ctx)); jpegai_release_encoder(enc); jpegai_destroy(ctx); return 1; } st = jpegai_encode_file(enc, "tests/test.jpg", "tests/test.bits"); if (st != JPEGAI_STATUS_OK) { fprintf(stderr, "%s\n", jpegai_last_error(ctx)); jpegai_release_decoder(dec); jpegai_release_encoder(enc); jpegai_destroy(ctx); return 1; } st = jpegai_decode_file(dec, "tests/test.bits", "tests/test-restored.png"); if (st != JPEGAI_STATUS_OK) { fprintf(stderr, "%s\n", jpegai_last_error(ctx)); jpegai_release_decoder(dec); jpegai_release_encoder(enc); jpegai_destroy(ctx); return 1; } jpegai_release_decoder(dec); jpegai_release_encoder(enc); jpegai_destroy(ctx); return 0; } ``` ## 配置 JSON ```json { "model_root": "models/jpeg-ai/onnx", "model_index": 0, "analysis_stage": 1, "quant_step": 1.0, "threads": 1, "output_quality": 92, "concurrent_handles": 1 } ``` 字段说明如下: - `model_root`:模型根目录;普通源码构建在未显式传值时可使用编译进 `JPEGAI_DEFAULT_MODEL_ROOT` 的源码树默认路径,发布目录中应写为 `models/jpeg-ai/onnx` 并从发布目录根目录运行,或按运行位置改成对应路径。 - `model_index`:选择 JPEG-AI 模型族中的模型编号。 - `analysis_stage`:选择分析模型阶段,应与模型文件命名匹配。 - `quant_step`:控制当前开发编解码器的潜变量量化尺度。 - `threads`:控制可并行阶段的线程数;为了复现实验结果可设为 `1`。 - `output_quality`:控制还原图写为 JPEG 时的质量和文件大小;默认 `92` 通常能让输出大小接近常规 JPEG,`100` 保留最多细节但文件较大。输出 PNG 时该字段不会改变图像。 - `concurrent_handles`:控制服务上下文中可同时 acquire 的编码器/解码器句柄数量。 ## 一条命令生成发布目录 ```bash scripts/package_release.sh artifacts/libjpeg-ai-release ``` 脚本会重新配置 `build/package-release`,构建 `jpegai_shared`、`jpegai_static`、`jpegai_enc` 和 `jpegai_dec`,然后生成发布目录。输出目录必须位于 `artifacts/` 下;不传参数时默认写入 `artifacts/libjpeg-ai-release`。打包机必须安装 `patchelf` 或 `chrpath`,脚本需要其中一个工具重写发布目录内可执行文件和共享库的 `RPATH`/`RUNPATH`。发布目录中的 `lib/libjpegai.a` 会合并本工程内部静态实现;调用方仍需要按平台链接 C++ 标准库、OpenCV、OpenSSL、ONNX Runtime 和 OpenMP 等外部库。 发布目录结构如下: ```text artifacts/libjpeg-ai-release/ README.md bin/ jpegai_enc jpegai_dec config/ jpegai.json include/ jpegai/ jpegai_c.h lib/ libjpegai.so libjpegai.a libonnxruntime.so* libonnxruntime_providers*.so* libcudnn* libcublas* libcufft* libcurand* libnvrtc* libcuda* models/ jpeg-ai/ onnx/ ``` 脚本会用 `patchelf` 或 `chrpath` 设置相对 `RUNPATH`,让 `bin/jpegai_enc`、`bin/jpegai_dec` 和 `lib/libjpegai.so` 优先从发布目录内的 `lib/` 查找运行时库。脚本会复制 `ldd` 能解析到的运行依赖库,并额外复制 `ONNXRUNTIME_ROOT/lib` 下存在的 ONNX Runtime provider 库和 CUDA 相关库;这些文件是否进入发布目录取决于打包机安装内容。 ## CUDA 执行提供程序与 ONNX Runtime 路径 CMake 通过 `ONNXRUNTIME_ROOT` 查找 `onnxruntime_c_api.h` 和 `libonnxruntime.so`。未显式指定时,工程优先检查 `/opt/timehunter/onnxruntime/1.24.4-cuda`,不存在时使用 `/opt/timehunter/onnxruntime/1.24.2`。 使用 CUDA 执行提供程序时,`ONNXRUNTIME_ROOT/lib` 需要包含 `libonnxruntime_providers_cuda.so` 及其依赖库。发布脚本会在 `ONNXRUNTIME_ROOT/lib` 下存在匹配文件时复制 `libonnxruntime.so*`、`libonnxruntime_providers*.so*`、`libcudnn*`、`libcublas*`、`libcufft*`、`libcurand*`、`libnvrtc*` 和 `libcuda*`;这些文件是否进入发布目录取决于所选 ONNX Runtime 安装内容。如果 CUDA 执行提供程序初始化失败,运行时应使用 CPU 执行提供程序路径继续推理;实际后端可从命令行输出的 `onnx_execution_provider` 查看。 ## 验证命令 ```bash cmake -S . -B build cmake --build build ctest --test-dir build --output-on-failure python3 -m unittest tests/test_reference_analysis.py -v scripts/package_release.sh artifacts/libjpeg-ai-release ```