- 图像处理
- 通信
【免费下载链接】libcimbar
Optimized implementation for color-icon-matrix barcodes
导读:本篇文章以 libcimbar 官方性能文档 PERFORMANCE.md 为骨架,系统拆解 cimbar 彩色图标矩阵条码的吞吐指标、容量与纠错参数、各 mode 配置的演进与取舍,并结合仓库源码(GridConf.h、Config.h、send.cpp 等)印证参数如何落地、瓶颈为何落在相机端。读完你将掌握:cimbar 每帧 7500 字节是如何由 tile 与颜色比特推演出来的、B/4C/8C/S 四种模式的性能对比与适用场景、以及如何用
cimbar_send/cimbar命令行复现「显示器 + 手机相机」这一高速信道。
一、先说结论:一个值得记住的基准数字
cimbar 是一种面向「气隙(air-gapped)数据传输」的实验性条码格式:编码器在电脑/手机屏幕上播放动画条码,解码端用手机摄像头直接读取,全程不依赖互联网、蓝牙或 NFC。libcimbar 是这一格式的优化 C++ 实现,其性能文档给出的核心结论是:
- 当前可稳定复现的吞吐:约 850 kbit/s(≈106 KB/s),由
mode B(8x8、4 色、ecc=30/155)在压缩后数据口径下测得; - 更早的
mode 4C为 ~838 kbit/s(≈104 KB/s); - 已被移除的
mode 8C曾达到 ~943 kbit/s(≈118 KB/s),但因 8 色解码不稳定而弃用; - 尚处 beta 的
mode S(5x5 4 色、ecc=40/216)已可稳定超过1 Mbit/s,格式仍在打磨中。
这些数字的含义与计算方式,正是本文要逐层展开的内容。完整基准记录见 PERFORMANCE.md。
二、容量从哪来:1024×1024 画面里塞进 7500 字节
2.1 码面几何:8x8 tile、9x9 栅格
mode B的码面规格为:
- 整张码图1024×1024 像素;
- 单个 tile 为8×8 像素,tile 间距按 9×9 栅格排布(即每个 tile 四周留 1 像素空行/空列);
- 四角留出锚点(anchor)区域,用于解码端定位。
源码 GridConf.h 中Conf8x8给出了精确参数:cell_size = 8、cell_spacing_x/y = 9、cells_per_col_x = 112,画面尺寸1024×1024,与文档描述完全对应。而 tile 总数则要去掉四角锚点区域:total_cells() = cells_per_col_x*cells_per_col_y - corner_padding_x()*corner_padding_y()*4。
2.2 每 tile 编码 6 bit:4 bit 符号 + 2 bit 颜色
每个 tile 承载两路信息:
- 符号位(symbol bits):4 bit——从 16 个预定义的 8×8 图标符号中选一个。这 16 个符号彼此之间的 image-hash 汉明距离约 20 bit,即便在模糊、失真的情况下也能保持可区分性(见 DETAILS.md);
- 颜色位(color bits):2 bit——4 色模式在 4 种颜色中选一个,额外带来 2 bit/tile(8 色模式则为 3 bit/tile)。
合计6 bit/tile。码面有效 tile 数约 12400 个(DETAILS.md),于是理论容量:
12400 tiles × 6 bit ÷ 8 =9300 bytes/帧
2.3 ECC 开销:从 9300 到 7500
但 9300 字节不能全部用于数据,因为视频→数字的损失性链路会产生误码,必须预留纠错空间。libcimbar 默认采用Reed-Solomon 纠错,参数为 30/155:即每 155 字节块中,125 字节为真实数据、30 字节为纠错码(DETAILS.md)。由此:
9300 × 125/155 =7500 字节/帧
这正是文档「Numbers of note」一节的核心算式。在 GridConf.h 中,Conf8x8的ecc_bytes = 30、ecc_block_size = 155即为该参数的源码落点。
需要说明的是,文档明确点出 Reed-Solomon 对此场景并非最优:它按字节纠错,而 cimbar 的误码往往一次涉及 1~3 bit。之所以仍选它,是因为 Reed-Solomon 实现随处可得、生态成熟(本仓库使用 libcorrect 作为实现)。ECC 块还会经过交织(interleave),把相邻字节的纠错块分散到整幅图的不同位置,避免「一根手指挡住一小块」就造成成片错误(DETAILS.md)。
2.4 流式传输:zstd 压缩 + fountain 喷泉码
比 7500 字节大得多的文件怎么传?libcimbar 的协议栈分三层:
- zstd 压缩:发送前先压缩。性能文档强调,所测吞吐的口径是「压缩后的线上比特」,即真实有效数据经过 zstd 压缩后的传输速率。压缩等级默认取 16(Config.h),压缩器按 16 KB(
CHUNK_SIZE = 0x4000)分块流式压缩(zstd_compressor.h); - fountain(wirehair)喷泉码:把文件切成 N 个数据块,编码出任意数量的喷泉帧。解码端只要收到 N+1 个任意顺序的帧就能重建整个文件,缺帧、乱序都无所谓(DETAILS.md)。其代价是:文件内容需整体驻留内存,单文件上限被限制在 33.55 MB(压缩后);
- Reed-Solomon ECC:对每一帧做信道级纠错。
这层协议栈的源码位于 fountain(封装 wirehair 编解码,见 FountainEncoder.h、FountainDecoder.h)与 compression 目录。
三、吞吐基准全景:四种模式逐一拆解
PERFORMANCE.md 记录的四组基准数据整理如下:
| 模式 | 配置 | ECC | 吞吐(压缩后) | 状态 |
|---|---|---|---|---|
| mode B | 8x8 4色 | 30/155 | 4,689,084 B / 44s ≈852 kbit/s(~106 KB/s) | 0.6.0 引入,当前推荐 |
| mode 4C(legacy) | 8x8 4色 | 30/155 | 4,717,525 B / 45s ≈838 kbit/s(~104 KB/s) | 原始配置,基本被 B 取代 |
| mode 8C(deprecated) | 8x8 8色 | 30/155 | 4,717,525 B / 40s ≈943 kbit/s(~118 KB/s) | 0.6.0 移除,8 色不稳定 |
| mode S(beta) | 5x5 4色 | 40/216 | 稳定 >1 Mbit/s | 未定稿,需特殊构建 |
3.1 mode B 与 mode 4C 的差别到底在哪
两者都是 8x8、4 色、ecc=30/155,吞吐也几乎一致,区别主要在内部协议细节。从 Config.h 的temp_conf()可以看到:
mode 4C(config_mode = 4):color_bits = 2、legacy_mode = true、fountain_chunks_scalar = -10(负值表示每帧固定 10 个喷泉块);mode B(config_mode = 68,默认分支):使用Conf8x8(),非 legacy 模式。
文档给出的取舍结论是:mode B 是首选,可靠性最好;mode 4C 在某些场景下可能给出更稳定的传输速率,但保留它主要是为了向后兼容。
3.2 为什么 8 色(mode 8C)被放弃
8 色模式每 tile 可编码 7 bit,理论上每帧可达约 10850 字节,吞吐理应更高(实测 ~943 kbit/s 也确实是最高的)。但文档直言:8 色一直不稳定,颜色识别在相机色偏、光照变化下误判率更高,因此 0.6.0 起被移除,需要未来重新研究。这也解释了为什么temp_conf()中不再提供 8 色分支(case 8仅保留在 legacy 通道)。
3.3 beta 的 mode S:5x5 小 tile + 更高 ECC
mode S 是未定稿格式(需要特殊构建),关键参数在 GridConf.h 的Conf5x5中:cell_size = 5、symbol_bits = 2、color_bits = 2(共 4 bit/tile)、ecc_bytes = 40、ecc_block_size = 216、cells_per_col_x = 162。更高的 ECC 比例(40/216 ≈ 18.5%,高于 mode B 的 30/155 ≈ 19.4%,接近)配合更小的 tile,换取更高的帧率与更细密的码面,目标是稳定超过 1 Mbit/s。注意:该模式尚未定稿,参数与性能数据仍可能变化,不宜作为生产依据。
四、测量环境与「850 kbit/s」的边界条件
要让基准数字可复现、可理解,必须知道它的测量环境:
- 解码端:Android 应用 cfc,运行在 4 个 CPU 线程的骁龙 625(Qualcomm Snapdragon 625)上——这是一颗面向中端机型的旧 SoC。文档特别指出:更新的手机 CPU 能更快跑解码器,但对整体吞吐帮助不大,因为真正的瓶颈是摄像头;
- 发送端:cimbar.org 的 WASM 实现(等价的命令行是
./cimbar_send /path/to/file,见 send.cpp)。cimbar.org 启用了shakycam选项,让接收端在扫描阶段就能检测并丢弃「中间过渡帧」,从而把更多处理时间花在解码真实数据上; - 口径:所有数字都是压缩后数据的线上比特速率,即用户实际能拿到的文件数据量;
- 瞬时速率(burst rate)可以更高或更低:更低的 ECC 设置能提升突发速率,文档作者的目标是在性能与可靠性之间取平衡,因此默认 30/155 并非为「极限突发」调优。
五、实践操作:如何复现这条高速信道
5.1 命令行发送端
构建后(Linux 下cmake . && make -j7 && make install,产物默认装入./dist/bin/,详见 README.md),用cimbar_send直接在屏幕上播放动画条码:
./cimbar_send inputfile.pdfcimbar_send支持的与性能强相关的参数(send.cpp):
| 参数 | 含义 | 默认值 |
|---|---|---|
-f, --fps | 目标帧率 | 15 |
-m, --mode | 模式:B / Bm / Bu / 4C | B |
-p, --padding | 码图周围黑色留白(像素) | 32 |
-z, --compression | 压缩等级,0 表示不压缩 | 16 |
-i, --in | 源文件(位置参数) | — |
其中-m Bm(对应Conf8x8_mini,1024×720)与-m Bu(对应Conf8x8_micro,736×637)是 0.6.x 新增的横屏变体配置(GridConf.h),供不同屏幕比例下使用。
5.2 命令行解码端
用cimbar工具解码一张或多张编码图,并把还原的文件写入输出目录:
./cimbar outputprefix*.png -o /tmp也可以从 stdin 读入文件列表:
echo outputprefix*.png | ./cimbar -o /tmp解码端常用开关(cimbar.cpp):--color-correct(颜色校正,2=完整模式,1=简单,0=关闭)、--color-correction-file(调试用,导出校正矩阵)、--no-fountain(关闭喷泉码,同时禁用压缩)。
5.3 编码为静态图片
无需屏幕,也可把文件编码为一组 PNG:
./cimbar --encode -i inputfile.txt -o outputprefix注意:大文件可能生成大量 PNG,注意磁盘空间(README 已明确提醒)。
六、影响实拍吞吐的 7 个实操因素
性能文档的「other notes」部分是实拍经验的高度浓缩,逐条对应着源码与信号链路:
- 光照是最大的变量:更好的环境光通常带来更稳定的结果。cimbar.org 采用(近乎)白色背景正是为此;cfc 使用 Android 自动曝光/自动对焦,充足的环境光——或白色背景——能带来更一致的画质。屏幕亮度够用,但环境光优于屏幕光;
- 横竖屏:由于曝光问题,横屏(landscape)可能优于竖屏(portrait)——这解释了
Bm/Bu横屏模式的存在; - 让码图尽量占满屏幕:跟随画面中的 guide 定位框(bitmap/guide-horizontal-、guide-vertical-即其素材)。该格式设计上可低至700×700 分辨率解码,但性能会下降;
- 尽量正对拍摄:斜角拍摄仍可解码,但「较小」一侧的图区错误可能超过 ECC 纠错能力;
- 警惕光源眩光(glare);
- 手抖会影响帧间对齐与扫描稳定;
- 帧率与相位:
shakycam这类「丢弃过渡帧」的策略(发送端 WASM 版本启用)能显著提升有效解码占比。
这些因素之所以关键,根源在于 DETAILS.md 指出的误差模型:tile 模糊、过暗、错位、镜头畸变、图太小导致 tile 失去定义,且这些问题往往局部化(图的一半好解码、另一半全是误读级联)。交织与 ECC 能缓解,但超过一定程度就只能靠提高 ECC 或输入分辨率。
七、从解码器看性能成本的来源
吞吐数字背后是解码端持续在做的工作。核心解码循环(伪代码,见 DETAILS.md):
for i, bits, distance, drift in next_decode(): results[deinterleave(i)] = bits position_tracker.update(i, drift, distance) decoded_data = error_correct(results)- 每个 cell 通过 image-hash 距离(
distance,越低越可信)选择最佳符号,距离同时充当置信度指标——高置信度的 cell 优先解码; drift是一个 (x,y) 偏移,跟踪局部形变,上限 ±7px;高置信度 cell 的 drift 会被优先采用,用于校正邻近 cell 的采样位置;- 解码顺序是乱序的,配合
deinterleave还原真实位序——这正是 mode B/4C 在解码阶段最耗 CPU 的部分之一。
源码对应物为 CimbDecoder.cpp(get_best_symbol/decode_symbol/ 颜色校正矩阵update_color_correction)与 extractor(锚点定位 + Deskewer 透视变换)。这也解释了为什么解码端跑在骁龙 625 这种老 SoC 上时,4 线程即够用且瓶颈仍在相机。
八、结论与可引用的事实清单
围绕 PERFORMANCE.md,可以安全引用的事实包括:
- 码面规格:1024×1024 像素,8×8 tile 按 9×9 栅格排布,mode B 每帧 7500 字节(ECC 后);
- 比特账目:16 符号/tile(4 bit)+ 4 色(2 bit)= 6 bit/tile → 理论 9300 B/帧,ECC 30/155 折减为 7500 B/帧;
- 当前基准:mode B 约 852 kbit/s(~106 KB/s);mode S(beta)可超 1 Mbit/s;mode 8C 因不稳定已移除;
- 链路构成:zstd 压缩 → wirehair fountain 喷泉码(上限 33.55 MB,需整文件驻留内存)→ Reed-Solomon ECC(30/155);
- 瓶颈归属:解码 CPU(4 线程骁龙 625 已够)不是主瓶颈,相机才是;光照、取景占比、拍摄角度直接影响吞吐。
适用前提与限制:上述吞吐为「压缩后数据」口径,测于显示器 + 骁龙 625 解码端 + WASM 发送端(cimbar.org,启用 shakycam)的固定组合;burst 速率可随 ECC 设置浮动;mode S 未定稿、需特殊构建;所有参数以当前仓库 GridConf.h 与 Config.h 为准。
延伸阅读:README.md(构建与用法总览)、DETAILS.md(编码/解码原理)、TODO.md(后续改进方向)。
- 图像处理
- 通信
【免费下载链接】libcimbar
Optimized implementation for color-icon-matrix barcodes
相关推荐
解决 OCaml 多核心难题:Riot 调度器如何实现高效负载均衡
解决 OCaml 多核心难题:Riot 调度器如何实现高效负载均衡 Riot 是 OCaml 5 的 actor model 多核调度器,专为解决 OCaml
如何快速上手Lore:面向初学者的完整入门教程 🚀
如何快速上手Lore:面向初学者的完整入门教程 🚀 Lore 是一款由Epic Games开发的新一代开源版本控制系统,专为处理大规模代码和二进制资产而设计。
版本控制后端DragonflyDB性能优化实战:25倍吞吐量背后的秘密
DragonflyDB性能优化实战:25倍吞吐量背后的秘密 DragonflyDB通过创新的多线程架构、内存效率优化和网络协议栈改进,实现了相比传统Redis
数据库KV存储缓存
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考