5.29. 条形码和 Data Matrix 码

另外两类码完善了摄像头的解码器集合。一维条形码 ——麦片盒侧面的条纹、医院的腕带、运输标签——是至今仍在日常使用的最古老的机器可读符号。Data Matrix 码像 QR 码一样是二维的,但在相同负载大小下更密集,并面向工业标记——激光蚀刻在电路板上的制造标记,而非贴在墙上的海报。image 模块为每一类都提供了专用解码器,覆盖了消费级二维码从未真正涉足的工业、零售和库存应用。

5.29.1. 一维条形码

一维 条形码将其负载编码为一系列宽度不等的竖条,从左到右读取(对于竖向排列的码则从上到下读取)。这些宽度量化为一小组取值之一,宽度序列按照打印机所选用的码制拼出字符:UPC 产品码使用数字,仓库零件号使用字母数字,Code 128 标签则使用任意文本。

find_barcodes() 在帧中扫描所有受支持码制的一维条形码,并返回一个 BarCode 结果对象的列表:

codes = img.find_barcodes()

for c in codes:
    img.draw_rectangle(c.rect, color=(0, 255, 0))
    print(c.payload, c.type, c.quality)

解码器在一次调用中同时横向和纵向扫描整帧,因此以任意角度打印的条形码都能在一遍扫描中被找到,应用程序无需旋转输入。roi 用于限制搜索范围;不存在其他调节参数——该解码器是自包含的。

受支持的 码制 涵盖了常见的消费级和工业级类别。零售类包括 image.EAN2image.EAN5image.EAN8image.UPCEimage.UPCAimage.EAN13(大多数消费品包装上的定长数字码),以及 image.ISBN10image.ISBN13(同一类码在图书上的再利用)。通用类包括 image.I25(Interleaved 2 of 5,常见于运输标签)、image.CODABAR(用于图书馆和血库)、image.CODE39image.CODE93image.CODE128(用于任意文本的变长字母数字码制)。货架边缘类的 image.DATABAR(RSS-14)和 image.DATABAR_EXP(RSS-Expanded)补全了整个列表。

每个检测结果都带有边界框相关属性—— xywhrectcorners ——以及作为字符串的解码后 payloadtype 是上述列表中的码制常量,当应用程序特别关心解码出的是哪一类码时会检查它(例如,对于杂货扫描应用程序只接受 EAN13)。

对于过滤而言重要的两个字段是 rotationqualityrotation 是条形码在图像平面内的角度,以弧度为单位:解码器能够应对任意旋转,但希望干净地显示检测结果的下游代码可能想要过滤掉倾斜超过某个阈值的码。

quality 是解码计数:成功解出相同负载的扫描线数量。解码器在帧中每一行(和每一列)与条形码相交的位置进行扫描,每次解码成功时就将计数器加一。一张对焦清晰、光照良好的打印条形码会产生几十级的 quality;而一张部分遮挡或模糊的条形码可能仅在一两条扫描线上解码成功,报告的 quality 为 1 -- 2。过滤掉 quality > 5 以下的检测结果可以剔除短暂的单扫描线误解码,而不会损失真正的检测结果。

一个一维条形码应用程序很小。采集一帧,调用 find_barcodes(),遍历返回的列表,按 c.typec.quality 进行过滤,再将 c.payload 通过 UART 或 USB 转发给负责记录或结算扫描结果的下游环节。

5.29.2. Data Matrix

Data Matrix 码是一种二维符号,它像 QR 码那样将负载编码为黑白单元格组成的网格。它与 QR 码在两个实际方面有所不同:在相同负载大小下它 更小(编码更密集),并且它面向 工业 用途而非消费用途(消费领域由 QR 码主导)。激光蚀刻在工厂车间金属零件上的图案、印在集成电路封装上的标签、打在医用注射器上的标记——这些通常都是 Data Matrix 码,而非 QR 码。

find_datamatrices() 在帧中扫描 Data Matrix 码,并返回一个 DataMatrix 结果对象的列表:

codes = img.find_datamatrices()

for c in codes:
    img.draw_rectangle(c.rect, color=(0, 255, 0))
    print(c.payload, c.rows, c.columns)

roi 以常规方式限制搜索范围。唯一一个解码器专属的调节旋钮是 effort,这是一个整数,控制解码器为找到匹配而付出的努力程度。更高的值能改善对模糊、受损或倾斜的码的检测,代价是帧率降低;更低的值运行更快,但可能漏掉更高努力程度本可找到的码。低于约 160 的值实际上无法检测出码;高于约 240 的值则收益递减。默认值 200 对于清晰图像是一个合理的平衡,新应用程序的合适起点是在默认值基础上加减 20,取决于目标是干净的(取较低值)还是受损的(取较高值)。

每个检测结果都带有边界框相关属性和四个检测到的角点、解码后的 payload,以及以弧度表示的图像平面内 rotation布局元数据 描述了解码器读取到的符号的尺寸和密度:rowscolumns 是数据网格的单元格数量;capacity 是该尺寸下符号所能承载的最大负载字符数;padding 是其中有多少个槽位未被使用(capacity - len(payload))。

布局字段对于需要验证蚀刻标记 格式 而非其内容的应用程序很有用。一个零件追踪系统可能要求所有标记都是 12×12 的码且填充字符不超过两个;一个检测结果若返回为 8×8(比规范要求更小的符号)或带有 10 个填充字符(大部分为空),则会被标记为需要重新标记。

5.29.3. 如何选择

QR 码与 AprilTag 之间的选择归结于 负载类型(任意数据与小型 ID);而条形码与 Data Matrix 码之间的选择则归结于 物理密度行业

当应用程序面向消费者,且这些码已经存在于现实场景中——杂货、图书、运输标签、馆藏图书——正确的检测器是 find_barcodes()。应用程序所读取的码是为另一套系统读取而打印的,标准化的零售码制正是那套系统所期望的。

当应用程序面向工业,且这些码 是为该应用程序而打印的 ——工厂车间的库存追踪、蚀刻在零件上的批次码、医疗设备上的可追溯性标记——正确的检测器是 find_datamatrices()find_qrcodes(),取决于应用程序需要的是 Data Matrix 的更高密度,还是 QR 更广泛的工具支持。

少数应用程序会在一条流水线中混用全部四种检测器。一个包裹检测摄像头可能会运行一遍 find_barcodes() 来读取打印的 UPC、一遍 find_qrcodes() 来读取同一包裹上的运输 QR 码、以及一遍 find_datamatrices() 来读取蚀刻的零件码,全部在同一采集帧上完成;三个结果列表按边界框位置进行关联,并作为单条检测记录上报。每个检测器的开销会累加,因此这样做的应用程序通常会用合适的 roi 收窄每一遍扫描,而不是为每一类码都搜索整帧。