
简介JBIG2 编码器 0.2 源码包主要面向需要压缩二值图像或多页文档的开发者解决扫描件、传真、电子书在存储传输时体积偏大的问题。JBIG2 是用于单色图像压缩的国际标准相比传统 G4 编码压缩率更高编码器支持生成独立 JBIG2 文件或 PDF 数据流并能将多页文档作为整体压缩。压缩包共13个文件以 C 源文件和头文件为主另含 Makefile、README、HTML 说明与 Python 辅助脚本整体 24KB结构清晰便于按需查阅和编译。用户拿到的是可编译的编码器源码和构建配置解压后通过常规 configure、make 流程即可获得命令行工具通过阅读源码可了解 JBIG2 的模板匹配、区域编码、熵编码等关键实现也可在此基础上改造集成。目前已有 360 人学习/下载适合有开发基础、希望自建 JBIG2 压缩能力或研究其算法实现的技术人员。1. JBIG2 压缩和 jbig2enc-0.2扫描件体积治理绕不开的老技术如果你处理过老式复印机、高速扫描仪导出的 PDF大概率在文件属性里见过 Apago JBIG2 这个字样。Apago 是商业化的 JBIG2 压缩引擎很多一体机固件直接内置它而开源世界里和它对应的名字就是 jbig2enc-0.2.tar.gz。JBIG2ITU-T T.88 / ISO/IEC 14492是黑白二值图像的压缩标准专门为传真、扫描文档和 OCR 二值化页面设计。相比传真时代常用的 CCITT G4JBIG2 在文字页上通常能再小 2 到 5 倍代价是部分模式有损。下面按这个标题拆三件事讲清楚JBIG 和 JBIG2 到底差在哪、怎么把 0.2 这个老包编译出来、以及上生产前参数和验证怎么做。搜索栏里的 jbig2en 只是 jbig2enc 的截断写法指的都是同一个编码器。2. 理解 JBIG2 压缩原理符号字典、上下文建模和与 JBIG/G4 的差距2.1 JBIG2 的四种编码模式Symbol、Generic、Refinement 和 Halftone 分别管什么JBIG2 不是一个算法而是一族算法标准里按分段组织码流每种模式对应不同的内容类型。Symbol 模式是压缩率的绝对主力它把页面里的连通区域切出来形状相同的归成一个符号放进字典页面正文只记录「第几个符号出现在哪个位置」。Generic 模式是上下文算术编码直接用周围像素的概率模型逐位编码位平面适合没有重复结构的噪声、手写体和灰度图抖动后的半色调页面。Refinement 模式在已有符号基础上做逐位修正通常用来把有损结果补成无损。Halftone 模式用灰度级模式字典表示网点但这块在 0.2 里并没有实现遇到网纹图老老实实走 Generic 或先做去网。模式之间通过不同段类型组合在同一个文件里这也是后面组装 PDF 时能看到.sym和.jb2两个文件的原因.sym放全局字典段.jb2放各页面数据段。PDF 的 JBIG2Decode 过滤器要求全局段出现在所有引用它的页面前面jbig2enc -p输出的正是这种布局这个设计从一开始就是冲着文档嵌入去的。2.2 压缩率从哪来用连通域数量估算符号字典规模Symbol 模式的收益来源于版面大量重复。一页扫描文档里汉字、字母、标点在版面上出现几十上百次但互异的字形往往只有几百个。先用一个粗糙脚本估计重复度判断这页内容适不适合符号模式import numpy as np from PIL import Image from scipy import ndimage img np.array(Image.open(scan-01.png).convert(L)) bw img 128 # 二值化笔迹为 True labels, n ndimage.label(bw) # 给每个黑像素连通块编号 sizes np.bincount(labels.ravel())[1:] print(总连通域疑似笔画块:, n) print(大于 10 像素的块:, int((sizes 10).sum()))ndimage.label按四连通或八连通给黑色区域编号sizes存每个块的像素数。把小于 10 像素的块视为噪点后剩下的连通域数量就是符号字典规模的上限估计。假设一页有 3000 个字符、500 个互异字形那么字符位置索引的编码成本远低于把每个字符重新画一遍字典内容还可以在几百页里共享这是 JBIG2 相对逐像素方案的核心优势。Generic 模式的上下文建模则负责把「索引之外的细节」压紧两者配合才有 2 到 5 倍的体积差距。真正切符号时还涉及笔画切合和匹配阈值脚本只给你量级判断别拿它当精确切分结果。2.3 JBIG 与 JBIG2 的差别从传真标准到文档格式的演进JBIGJBIG1ITU-T T.82只有 Generic 模式的渐进编码设计目标是传真和渐进传输无损但有损能力很弱也没有符号字典所以它在文字扫描件上对 G4 的优势并不大。JBIG2 是 2000 年前后的重写版目标直接就是文档支持有损符号模式、支持全局字典与页面段分离、码流结构适合 PDF 嵌入。维度CCITT G4 (MMR)JBIG1 (T.82)JBIG2 (T.88)编码思路二维游程 霍夫曼上下文算术编码符号字典 上下文算术编码有损能力无基本无符号合并可大比例有损文字页压缩率基准比 G4 小 15%~30%有损模式比 G4 小 60%~80%段结构不分段分层层级全局字典 / 页面 / 细化段分离PDF 嵌入CCITTFaxDecode极少见JBIG2Decode主流支持表格里的倍数都是经验区间文字密度、噪声底、分辨率都会影响实际收益真正拿数字请用第四章的对比脚本跑一遍自己的样本。理解这些模式差异之后再去看 jbig2enc 的命令行参数每个开关背后的动机就清楚了。3. 编译 jbig2enc-0.2依赖、configure 命令和三个高频报错3.1 先准备好 leptonica 相关依赖版本比路径更值得注意jbig2enc 是 C/C 代码图像读写不自己实现而是走 Leptonica 的 pix 系列接口再通过 libtiff、libpng 落地文件。0.2 这个包对接的是 Leptonica 1.6x 时代的内部接口新版 Leptonica1.76 之后把不少内部函数收进了私有头文件直接编新版往往在链接阶段报 undefined reference。常见做法有两条系统库足够旧就直接用系统包否则在项目里固定编译一份 Leptonica 1.62。Debian/Ubuntu 下装依赖通常是这一行apt-get install -y build-essential autoconf automake libtool \ pkg-config libtiff-dev libpng-dev zlib1g-dev libjpeg-dev \ libwebp-dev libopenjp2-7-dev liblept-dev装完先用pkg-config --modversion lept看版本号。如果发行版给的是 1.80 以上的新版编译 jbig2enc 之前就要有心理准备多半需要回到 3.3 的修复步骤。RHEL 系对应yum install leptonica-devel libtiff-devel libpng-devel思路一样只是包管理器的名字不同。3.2 从 tar.gz 到 make install最小命令序列拿到 jbig2enc-0.2.tar.gz 之后的操作通常是解包、生成 configure老包有时不自带、配置、编译tar xzf jbig2enc-0.2.tar.gz cd jbig2enc-0.2 if [ ! -x configure ]; then ./autogen.sh; fi ./configure --prefix/usr/local make -j$(nproc) sudo make install jbig2enc -hautogen.sh依赖 autoconf/automake/libtool缺哪个就装哪个。configure 用 pkg-config 探测 tiff、png、lept探测失败时手动补路径./configure CPPFLAGS-I/usr/local/include LDFLAGS-L/usr/local/libLeptonica 不是装在系统默认位置时再加--with-leptonica...。make -j并行编译老代码偶尔会遇到头文件依赖没写全的竞态报错诡异就先去并行重编一次。最后jbig2enc -h能打出 usage 说明库和二进制都正常。源码包同时会构建一个叫jbig2的组页工具后面组装 PDF 全靠它。3.3 三个高频报错和一个版本固定策略把网上编译这个包最常卡住的三个位置列出来报错特征根本原因处理方式configure: error: leptonica not foundpkg-config 找不到 lept 或没装 dev 包装liblept-dev或export PKG_CONFIG_PATH/usr/local/lib/pkgconfig后重配autoreconf: command not found缺 autotools 工具链安装 autoconf automake libtool链接期undefined reference to pix*Leptonica 版本太新接口对不上固定编译 Leptonica 1.62再用--with-leptonica指定提示与其在系统库里反复折腾不如用一个固定镜像或独立编译目录同时放 Leptonica 1.62 和 jbig2enc-0.2环境变量只在这套构建里生效。这样不会污染系统里其他依赖新版 leptonica 的应用。最后补一句经验别把 0.2 的二进制直接拷进和构建环境差距很大的容器老二进制是动态链接缺liblept.so.4这类运行时问题比编译期报错难排查得多构建机器和运行机器尽量保持一致。4. 用 jbig2enc 压缩扫描 TIFF 并组装 PDF命令、参数表和实测对比4.1 最小工作流先二值化再 jbig2enc最后用 jbig2 组 PDFjbig2enc 的输入是二值图。扫描件通常是灰度或彩色 TIFF先转成 1-bit 再喂给它否则它会按像素深度报错或输出不可控。ImageMagick 7 之前的命令是convert7 之后换成magick参数写法不变convert scan-01.tif -colorspace Gray -threshold 50% -depth 1 scan-01-1bit.tif jbig2enc -s -p -2 -b out_compressed scan-01-1bit.tif scan-02-1bit.tif jbig2 -o out_compressed.pdf out_compressed.jb2 out_compressed.sym-colorspace Gray -threshold 50%把灰度图硬切成黑白这一步属于二值化预处理阈值直接决定后续质量和体积。jbig2enc -s -p -2的含义看参数表符号模式、面向 PDF 的段结构、两遍编码-b给出产物前缀生成out_compressed.sym全局符号字典和out_compressed.jb2页面段。最后jbig2把字典和页面段按 PDF 要求的顺序装进外壳产出单文件 PDF分辨率不变页码按命令行传入顺序排列。多页 TIFF 也可以直接传Leptonica 会按页切分处理。4.2 参数表-s、-p、-b、-T、-2、-P 的取舍参数作用典型取值与建议-s开启符号模式有损压缩主力纯文字、表格扫描页必开-p输出 PDF 风格的全全局段 页面段布局和jbig2组页工具配合时必须开-b name产物前缀生成 name.sym 与 name.jb2用任务名做前缀避免多任务覆盖-T 值符号合并的相似度阈值默认 0.850.80 激进合并、0.95 保守保真-2两遍编码先收全局字典再编码页面多页文档推荐开单页收益不明显-P n每 n 页重建一次符号字典需要按页随机读取时调小例如 -P 100-v输出进度和统计批量跑时观察单页耗时与字典规模一个常见误用是把-P当并行参数。-P 100的含义是每攒够 100 页就结束当前字典、重新开始下一本目的是限制单字典体积、方便 PDF 按页切分和随机访问。并行提速要靠外部工具把文件分片后各自跑最后合并 PDFjbig2enc 本身不并行。4.3 和 G4 的实测对比脚本用数字决定要不要换空谈 2 到 5 倍没意义拿真实扫描件跑一遍最稳。下面脚本对同一批文件分别做 G4 压缩和 JBIG2 有损压缩输出体积对比#!/usr/bin/env bash set -euo pipefail mkdir -p g4 jb2 for f in scan-*-1bit.tif; do tiffcp -c g4 $f g4/${f%.tif}_g4.tif done jbig2enc -s -p -2 -T 0.85 -b jb2/scan scan-*-1bit.tif jbig2 -o jb2/scan.pdf jb2/scan.jb2 jb2/scan.sym g4_size$(du -cb g4/*.tif | tail -1 | cut -f1) jb2_size$(stat -c %s jb2/scan.pdf) echo G4 合计: $((g4_size/1024)) KB echo JBIG2 PDF: $((jb2_size/1024)) KB echo 压缩比提升: $(awk BEGIN{printf \%.2f\, $g4_size/$jb2_size})xtiffcp -c g4生成 MMR 压缩的参照 TIFFdu -cb统计目录总字节stat -c %s取 PDF 体积。这个对比不是实验室精度TIFF 带头开销、PDF 带外壳但两者量级一致足够判断值不值得为改造换编码方式。实测中纯文字 300dpi 页面JBIG2 比 G4 小 60% 以上很常见页面含大量反白、印章或灰底噪点时差距会缩小甚至 Generic 模式更合适。5. 调参、验证和批处理把 jbig2enc-0.2 挂进生产管线5.1 先看字符边缘再动 -Tthreshold 的调参节奏牺牲质量前先确认牺牲的位置。二值化之后字符边缘的毛刺、断点来自扫描噪声-T调低会让更多相似字形合并文件更小代价是细小笔画点、横折钩、衬线装饰可能被错误归并。我的经验是固定 0.85 跑一遍用 5.2 的解码步骤生成验证图放大看三个位置小字号汉字的横折处、英文 c/e/o 的闭合区域、表格线交叉点。出现粘连或缺口就把-T上调到 0.90 甚至 0.95页面干净且体积还有要求再降到 0.80 试。每次调整同时记录体积和视觉差异比拍脑袋定死一个值可靠。5.2 用 Ghostscript 解码验证有损压缩必须回读检查组装出的 PDF 用 Ghostscript 解码回 PNG再和原图做像素级对比gs -q -sDEVICEpnggray -r300 -o verify-%02d.png out_compressed.pdf compare -metric AE scan-01-1bit.tif verify-01.png null: 21compare -metric AE输出两图差异像素数数字越大表示改动越多。注意别用无损标准卡它有损符号模式本来就会把相同字形合并差异像素集中在字符形状边缘只要笔画没断、闭合区域没开大的 AE 值也正常。如果产物是裸.jb2而不是 PDF可以用 Artifex 的 jbig2dec 单独解码验证全局符号段需要作为全局输入传给解码器这个工具随 Ghostscript 发布源码附带。5.3 批处理模板把预处理、压缩、验证串成一条命令最后给一个能放进 cron 或 CI 的模板输入目录下的多页 TIFF输出压缩 PDF 和验证图for f in input/*.tif; do convert $f -colorspace Gray -threshold 50% -depth 1 bin/$(basename $f) done jbig2enc -s -p -2 -T 0.90 -b result/batch bin/*.tif jbig2 -o result/batch.pdf result/batch.jb2 result/batch.sym gs -q -sDEVICEpnggray -r300 -o verify/page-%02d.png result/batch.pdf生产环境里我一般还会加两条一是convert之前先看灰度直方图确认文件确实是文档扫描件而不是照片照片类内容别走符号模式二是把-T写进配置项而不是硬编码批量处理几十种扫描仪来源后你会发现不同设备的噪声底差异很大值得给每个来源维护一个阈值。OCR 不要放在 jbig2 解码后的图像上做二值化已经损失信息文字识别请用原始灰度图或专门的二值化算法jbig2enc 只管压缩不管识别。本文还有配套的精品资源点击获取