简介:包裹与条码实例分割数据集是一份面向物流场景的YOLO格式实例分割数据,包含条码、单个包裹、多个包裹三类标注,适用于自动化分拣、智能库存管理、物流监控等场景。资源共322个文件,以160张JPEG原图、160个TXT标注文件为主,另含1个yaml配置文件和1个docx说明文档,压缩包大小13.97MB;其中TXT标注采用YOLO格式多边形坐标点,便于直接训练实例分割模型。目前已有206人学习浏览,适合物流算法工程师、计算机视觉学习者及科研人员作为基准数据集使用。标注贴近真实物流环境,既覆盖单包裹与多包裹场景,又精确刻画条码轮廓,可直接接入YOLO系列框架进行训练与评估;对物流自动化设备识别、货物追踪、异常包裹检测和学术算法验证都有实际帮助,能够显著降低数据预处理与标注成本。
1. 包裹与条码实例分割数据集:从图像到可操作的包裹与条码轮廓
在一套分拣视觉系统里,相机拍到的画面从来不是干净的“一个包裹+一条条码”,而是传送带上相互遮挡的纸箱、反光胶带、深浅不一的标签底色,以及印在纸箱上的装饰性图案。如果只做目标检测,模型给出的是“这里有个包裹”“这里有条码”的矩形框,框里往往混入大量背景和相邻物体,后续无论做机械臂抓取还是条码解码,都要再写一堆手工逻辑去裁剪和过滤。包裹与条码实例分割数据集要解决的,正是把“像素属于哪个物体”这件事直接交给模型:输出每个包裹的掩码,以及每个条码的精确轮廓,下游再用掩码去算位姿、切ROI、送解码器,整个链路就干净很多。
这个压缩包的名字看起来只是“一份数据”,实际代表了一套完整的标签规范和训练前置流程:包裹类别的掩码标注、条码类别的小目标标注、COCO或YOLO格式的组织方式,以及如何把这些原始标注变成模型能吃的训练集。本文就按“数据集理解→数据体检→格式转换→YOLOv8-seg训练→部署前验证”这条路展开,面向准备在物流分拣、包裹计数、条码识别场景里自己训练实例分割模型的工程师。如果你手上刚好有类似的数据包,不管是不是同一份,这套处理流程都可以直接照搬。
2. 实例分割数据集的结构与标注校验:拿到zip后先做数据体检
2.1 解压后的常见目录结构与命名约定
这类数据集zip解压后,常见做法是根目录下放images和annotations两个大目录,或者按train/val/test分好三个子集,每个子集里再分images和labels。命名上,图片可能是P001_01.jpg这样的流水号,标注文件可能是同名.json(COCO格式)或同名.txt(YOLO格式)。先别急着打开看图,用一条命令把目录树拉出来,确认标签文件与图片文件是否一一对应。
unzip 包裹与条码实例分割数据集.zip -d parcel_barcode_dataset cd parcel_barcode_dataset find . -type f | sed 's|/[^/]*$||' | sort | uniq -c第一段命令解压到指定目录,第二段统计每个子目录下的文件数量。如果images/train有 1200 个jpg,而labels/train只有 1100 个txt,说明有 100 张图缺标注,或者标注文件命名不匹配,这会直接导致训练时数据加载报错或静默跳过部分样本。检查命名是否一一对应,可以用下面这段脚本,找出“有图无标”和“有标无图”的文件名。
import os img_dir = 'parcel_barcode_dataset/images/train' label_dir = 'parcel_barcode_dataset/labels/train' imgs = {os.path.splitext(f)[0] for f in os.listdir(img_dir)} labels = {os.path.splitext(f)[0] for f in os.listdir(label_dir)} print('缺标注的图片:', len(imgs - labels), '张') print('缺图片的标注:', len(labels - imgs), '个')imgs - labels得到的是在图片目录存在、但没有对应标注文件的样本;反过来就是孤儿标注。实际处理时,我一般会把这两类文件都直接过滤掉,而不是补标注,因为标注缺失的原因可能是原始采集时画面模糊或物体太密集,强行补标反而引入噪声。
2.2 标注格式识别:polygon、RLE还是YOLO txt
数据集的标注格式决定了后续所有处理逻辑。打开一个标注文件看前几行,比读说明文档更快。
如果根目录有annotations/instances_train.json,这是标准的COCO格式,内部结构包含images、annotations、categories三个主要数组。images里记录了每张图的id、file_name、width、height;annotations里每个对象有image_id、category_id、bbox、segmentation、area;segmentation字段可能是多边形坐标列表,也可能是RLE编码。判断是polygon还是RLE,看它是[[x1,y1,x2,y2,...]]这种嵌套列表,还是{"counts": [...], "size": [h, w]}这种字典结构。
如果打开.txt看到每行是class_id x1 y1 x2 y2 ...的浮点数序列,那就是YOLO分割格式。注意这里的坐标是归一化的,除以了图片宽高,所以0.5 0.5表示图像中心点。还有一种容易混淆的情况:标注工具导出的txt里坐标是逗号分隔而不是空格分隔,Ultralytics YOLO训练时两种都能解析,但如果你自己写转换脚本,一定要统一成分隔符。
RLE格式在包裹场景里比较少见,因为包裹和条码的轮廓大多是规整多边形,标注工具默认输出polygon。但如果数据来自Mask R-CNN流水线或某些自动标注平台,很可能存成RLE。判断方法很简单:segmentation是字典就是RLE,是列表就是polygon。RLE转polygon需要额外做解码,不推荐在训练前急着转,先确认数量占比,如果只有几百个,直接保留RLE并用pycocotools读取即可。
2.3 用脚本统计类别、面积分布与图像异常
数据体检的核心目的,是回答三个问题:类别是否平衡、条码这类小目标占比多少、有没有损坏图片。下面这段脚本用COCO格式举例,读入标注后打印类别统计、标注数量、以及按像素面积划分的目标尺寸分布。
import json from collections import Counter with open('annotations/instances_train.json') as f: coco = json.load(f) cat_id2name = {c['id']: c['name'] for c in coco['categories']} ann_per_cat = Counter() area_bins = {'tiny(<32x32)': 0, 'small(<96x96)': 0, 'medium(<224x224)': 0, 'large': 0} for ann in coco['annotations']: cat_name = cat_id2name[ann['category_id']] ann_per_cat[cat_name] += 1 w, h = ann['bbox'][2], ann['bbox'][3] area = w * h if area < 32 * 32: area_bins['tiny(<32x32)'] += 1 elif area < 96 * 96: area_bins['small(<96x96)'] += 1 elif area < 224 * 224: area_bins['medium(<224x224)'] += 1 else: area_bins['large'] += 1 print('各类别标注数量:', dict(ann_per_cat)) print('目标面积分布:', area_bins)这段脚本的核心价值在area_bins统计。条码目标在1080p图像里通常只有 200×80 像素左右,属于small甚至tiny级别;如果统计结果显示条码类别有大量tiny目标,后续训练就必须开启并保留马赛克增强、考虑用更大输入分辨率,否则模型很容易漏检。如果只有large和medium而没有小目标,反而要怀疑标注是否把整张贴纸当成条码画了,这是实践中常见的标注歧义——条码的掩码应该只覆盖条码本体,不包括下方的白色基底和文字。
2.4 掩码标注质量如何影响条码解码与抓取精度
实例分割的标注质量直接传导到下游。包裹掩码如果边缘多出 5~8 个像素,机械臂抓取点计算出来的质心偏差可能达到一两厘米,对于吸盘抓取来说问题不大,但对于需要精确插入的夹爪就是致命误差。条码掩码的问题更隐蔽:如果标注把条码外围的白色留白也圈了进去,模型学到的掩码会偏大,按掩码裁剪出的ROI送入解码器时,周围多出来的空白会影响边缘定位,尤其在二维码版本较高、模块较密时,解码率会明显下降。
检查掩码质量的一个实用办法是把标注polygon叠加到原图上,用半透明填充可视化,人为观察边缘贴合度。下面这段脚本用pycocotools的annToMask把polygon转成掩码,再叠到原图上保存。
import json import cv2 import numpy as np from pycocotools import mask as mask_utils with open('annotations/instances_train.json') as f: coco = json.load(f) img_id2info = {img['id']: img for img in coco['images']} anns_by_img = {} for ann in coco['annotations']: anns_by_img.setdefault(ann['image_id'], []).append(ann) # 挑一张包含条码类别的图做可视化 for img_id, anns in anns_by_img.items(): if len(anns) < 2: continue img_path = 'images/train/' + img_id2info[img_id]['file_name'] img = cv2.imread(img_path) overlay = img.copy() for ann in anns: rle = mask_utils.frPyObjects(ann['segmentation'], img.shape[0], img.shape[1]) m = mask_utils.decode(rle) color = (0, 255, 0) if ann['category_id'] == 1 else (0, 0, 255) overlay[m > 0] = overlay[m > 0] * 0.5 + np.array(color) * 0.5 out = 'vis_' + img_id2info[img_id]['file_name'] cv2.imwrite(out, cv2.addWeighted(img, 0.5, overlay, 0.5, 0)) print('已保存可视化文件:', out) breakfrPyObjects会把polygon或RLE都统一转成RLE,decode再还原成二值掩码。这样处理的好处是不用手写多边形填充算法。可视化时重点关注条码掩码是否明显超出条码区域,以及包裹掩码在遮挡边缘处是否出现凹陷或破损——如果大量样本都有这类问题,建议先做一轮标注修正再训练,而不是指望模型自己学好。数据量不大时,用LabelMe或X-AnyLabeling打开对应图片手工修正;数据量大时,可以先训练一版模型,用模型的预测结果辅助筛选差异最大的样本,再针对性修标。
3. 格式转换实操:把COCO标注转成YOLOv8-seg能直接训练的数据集
3.1 为什么需要转格式:YOLO训练加载逻辑决定了目录与标签结构
Ultralytics YOLOv8-seg训练时,data.yaml里的train和val路径指向图片目录,程序会自动去同级的labels目录找同名txt。比如图片路径是dataset/images/train/001.jpg,对应的标注必须放在dataset/labels/train/001.txt。这种“图片目录+标签目录”的配对方式,决定了你拿到COCO格式后必须先做转换,且输出目录结构必须严格对齐。
YOLO分割txt的每行格式是class_id x1 y1 x2 y2 ... xn yn,所有坐标除以图片宽高做归一化。与目标检测的class_id cx cy w h不同,分割格式里没有中心点和宽高的概念,直接用多边形的顶点坐标序列描述轮廓。顶点数量没有上限,但实践中超过 200 个点对模型训练没有额外帮助,反而增加IO开销,转格式时可以做个降采样。还有一点容易踩坑:COCO的polygon坐标是绝对像素值,且是按 (x, y) 成对存储的,顺序无所谓,但YOLO要求归一化,且必须保证所有坐标在 0~1 之间。
3.2 COCO转YOLO-seg的转换脚本与坐标归一化细节
下面这段脚本把COCO格式的polygon标注转换成YOLO分割txt。数据集的categories可能包含parcel和barcode两个类别,转换时按categories数组的顺序生成类别ID。
import json import os import numpy as np with open('annotations/instances_train.json') as f: coco = json.load(f) cat_id2new = {c['id']: idx for idx, c in enumerate(coco['categories'])} img_id2info = {img['id']: img for img in coco['images']} anns_by_img = {} for ann in coco['annotations']: anns_by_img.setdefault(ann['image_id'], []).append(ann) out_label_dir = 'parcel_barcode_dataset/labels/train' os.makedirs(out_label_dir, exist_ok=True) def downsample_polygon(pts, max_points=100): if len(pts) <= max_points * 2: return pts step = len(pts) // (max_points * 2) return pts[::step] for img_id, anns in anns_by_img.items(): img_info = img_id2info[img_id] w, h = img_info['width'], img_info['height'] lines = [] for ann in anns: seg = ann['segmentation'] if isinstance(seg, dict): continue # RLE先跳过,本例只处理polygon for poly in seg: pts = np.array(poly, dtype=np.float64).reshape(-1, 2) pts[:, 0] /= w pts[:, 1] /= h pts = np.clip(pts, 0.0, 1.0) pts = downsample_polygon(pts) new_id = cat_id2new[ann['category_id']] coord_str = ' '.join(f'{x:.6f} {y:.6f}' for x, y in pts) lines.append(f'{new_id} {coord_str}') if lines: name = os.path.splitext(img_info['file_name'])[0] + '.txt' with open(os.path.join(out_label_dir, name), 'w') as f: f.write('\n'.join(lines)) print('转换完成,共处理', len(anns_by_img), '张图片')脚本里两个细节值得单独说明。第一,np.clip(pts, 0.0, 1.0)把越界坐标截断到 [0,1],防止标注工具画的polygon边缘超出图像边界,导致YOLO加载时坐标溢出报错。第二,downsample_polygon对超过 100 个顶点的多边形做等间隔抽样,条码和包裹的轮廓一般都在 20~80 个顶点之间,这个函数主要防御性的,避免某些手工标注极其精细的多边形拖慢训练数据加载。RLE标注在这个脚本里被直接跳过,如果数据集中RLE占比较高,需要用mask_utils.decode解码后找轮廓再转换,逻辑会复杂一截,建议单独处理。
3.3 按场景切分train/val,避免同一包裹出现在两个集合
数据集zip里如果已经分好了train/val目录,直接用;但很多情况下只有一个总目录或一份COCO json,需要自己切分。切分时最容易犯的错误是随机打乱后直接按比例分,导致同一个包裹的不同角度照片同时出现在训练集和验证集里。实例分割模型对同一物体的不同视角很容易过拟合,验证集里出现训练集同款包裹,评估出来的mAP会虚高,部署到新场景时性能打回原形。
正确做法是按图片名中的包裹ID或场景ID做分组切分。如果文件名是PACKET_0001_01.jpg这种结构,可以用下划线分段提取ID:
import os import random from collections import defaultdict img_files = [f for f in os.listdir('images/all') if f.endswith('.jpg')] grouped = defaultdict(list) for f in img_files: scene_id = f.split('_')[1] # 按具体命名规则调整 grouped[scene_id].append(f) all_groups = list(grouped.keys()) random.seed(42) random.shuffle(all_groups) split = int(len(all_groups) * 0.85) train_groups = set(all_groups[:split]) val_groups = set(all_groups[split:]) os.makedirs('dataset/images/train', exist_ok=True) os.makedirs('dataset/images/val', exist_ok=True) # 再同样建 labels/train 和 labels/val,然后按组别复制文件random.seed(42)固定随机种子保证切分结果可复现。按组切分后,验证集里的包裹与训练集完全不同,评估指标才真正反映模型面对新包裹的表现。如果文件名里没有可用的分组ID,退而求其次的做法是直接按文件名的哈希值取模切分,保证同一前缀的文件落到同一集合的概率更高,但这只是妥协方案。
3.4 转换后的自查:用脚本读回txt验证坐标合法性
转换完成不要直接开训,先跑一个合法性检查,定位“标签文件为空”“坐标越界”“类别ID超范围”这三类常见问题。下面的脚本遍历所有txt,检查每行的格式和数值范围。
import os label_dir = 'dataset/labels/train' num_classes = 2 # 换成你的实际类别数 for fname in os.listdir(label_dir): path = os.path.join(label_dir, fname) with open(path) as f: for line in f: parts = line.strip().split() if len(parts) < 5: print(f'{fname}: 多边形坐标点数不足: {line.strip()}') continue cls_id = int(parts[0]) coords = list(map(float, parts[1:])) if cls_id >= num_classes: print(f'{fname}: 类别ID越界: {cls_id}') if any(abs(c) > 1.0 for c in coords): print(f'{fname}: 坐标越界') if len(coords) % 2 != 0: print(f'{fname}: 坐标数量不完整(应为偶数)') print('检查完成')重点关注“坐标数量不完整”的报错。YOLO分割格式要求顶点成对出现,如果某个polygon少了一个坐标,Ultralytics会在训练时直接跳过这张图,并且只在日志里打一条warning,不容易察觉。这类问题多由COCO polygon中偶发的坐标缺失引起,检查脚本在数据集较大时能省下不少排错时间。
4. 模型选型与YOLOv8-seg训练参数:把包裹和条码分割跑起来
4.1 为什么选YOLOv8-seg而不是Mask R-CNN或YOLACT
在包裹与条码这个场景里,选型主要看三点:推理速度、小目标能力、部署友好度。Mask R-CNN的分割精度确实高,但ResNet50+Faster R-CNN骨架在1080p图像上单帧推理耗时普遍在 80~150ms,分拣线相机帧率稍高就扛不住。YOLACT速度快一些,但掩码质量受原型掩码数量限制,遇到紧密相邻的条码和包裹时轮廓容易粘连。YOLOv8-seg在速度和精度的平衡上更省心,加上Ultralytics提供了从训练到ONNX导出再到TensorRT部署的完整链路,物流场景落地时不用自己拼装推理框架。
如果数据集中条码非常小,或者训练资源充足,也可以考虑YOLO11-seg或RT-DETR-seg,但基线模型建议从yolov8s-seg或yolov8m-seg开始。不要一上来就用yolov8l-seg,包裹和条码的类别特征比较明确,大模型带来的精度收益有限,推理延迟却会成倍增加。先跑通流程,再看指标决定是否升级模型尺寸。
4.2 关键训练参数:分辨率、epochs、batch、mosaic的取舍
下面的表格列出了包裹与条码场景里最值得用心的几个参数,以及我看到异常表现时的调参方向。
| 参数 | 推荐值 | 说明与调参依据 |
|---|---|---|
imgsz | 1280 或 1536 | 条码是小目标,640分辨率下最小条码可能只有8×8像素,分割掩码几乎学不到细节 |
epochs | 150~300 | 包裹类收敛快,条码小目标需要更多epoch才能稳定 |
batch | 8~16 | 由显存决定;模型大、分辨率高时batch过小会导致BN统计不准 |
mosaic | 1.0 训练阶段开启 | 马赛克增强能把条码拼接到其他包裹图上,提升小目标多样性 |
close_mosaic | 10~15 | 最后10~15个epoch关闭马赛克,让模型适应真实分布 |
lr0 | 0.001~0.01 | 预训练权重微调建议0.01以下;从头训练用0.01 |
patience | 30~50 | 验证集seg_loss连续N个epoch不下降就早停 |
imgsz是最值得手动调的参数。条码掩码分割要的是边界精度,640分辨率下条码的像素宽度可能只有10~15像素,模型输出的掩码边缘稍偏一点就完全无法解码。我一般会把输入分辨率提到1280,如果GPU显存紧张,宁可用更大imgsz+ 更小batch的组合,也不要为了batch大小牺牲分辨率。mosaic和close_mosaic是配套的,前者在训练早期扩大数据多样性,后者在训练末期还原真实的单图分布,避免模型对马赛克拼贴产生依赖。
4.3 数据组织与训练启动:data.yaml与最小可行训练命令
先把数据集按YOLO期望的目录结构整理好,然后准备data.yaml。
path: /path/to/parcel_barcode_dataset train: images/train val: images/val nc: 2 names: 0: parcel 1: barcodepath指向数据集根目录,train和val是相对该目录的图片路径,程序会到对应的labels目录找txt。nc是类别总数,names里的索引必须与txt中的class_id对应——如果转换脚本里parcel是0、barcode是1,这里就必须保持这个顺序,否则模型学到的是错位的标签映射,评估分数再高也毫无意义。
目录和yaml准备好后,先跑一个1~2个epoch的最小训练,验证数据加载、标签解析和模型forward都没有问题:
yolo segment train \ model=yolov8s-seg.pt \ data=parcel_barcode.yaml \ imgsz=1280 \ epochs=2 \ batch=8 \ device=0 \ workers=8 \ project=runs/parcel_seg \ name=smoke_testworkers=8让数据加载并行化,如果机器有十几个CPU核心,这个值可以调到max(8, cpu_count//2)。看到每个epoch跑完后Box(P),Box(R),mAP50-95,Mask(P),Mask(R)这几个指标都正常更新,再停掉,进入正式训练。如果workers过高导致CPU成为瓶颈,显存利用率反而上不去,这个值的设置需要结合nvidia-smi实际观察。
4.4 训练过程监控:从loss曲线和Mask指标预判分割质量
正式训练开始后,我一般只看四个指标:cls_loss是否快速下降、seg_loss是否在前20个epoch明显收敛、mask_mAP50-95是否稳步上升、以及验证集mask_mAP50是否达到 0.85 以上。如果cls_loss降得很快但seg_loss长时间不动,问题多半出在标注上——检查一下条码掩码是否有大量非闭合多边形或面积过小的anno,这类标注会让分割头学到“输出空掩码”的捷径,loss下降但预测全是背景。
训练日志里还有一个容易被忽略的细节:Ultralytics默认会在每个epoch末尾输出一张验证集的可视化图,保存在runs/parcel_seg/train/val_batch*.jpg。打开看掩码是否覆盖条码边界、包裹之间是否有粘连。如果掩码边缘呈现明显的锯齿状,说明epoch不够或imgsz偏小;如果条码频繁漏检,优先加分辨率,而不是加epoch。训练收敛后,best.pt和last.pt会保存在runs/parcel_seg/train下,后续推理和导出都用best.pt。
5. 部署前验证:用训练好的模型自查掩码质量与条码可解性
5.1 单图推理与可视化:看掩码在条码边界上的贴合程度
训练完成后不要直接上生产,先在验证集上跑一遍推理,把掩码画出来逐一过目。这一步的成本很低,但能发现mAP指标反映不出来的问题,比如掩码是否包含条码周围的白色区域、条码与包裹边缘是否粘连。
from ultralytics import YOLO import cv2 model = YOLO('runs/parcel_seg/train/best.pt') img = cv2.imread('val_sample.jpg') results = model.predict(img, imgsz=1280, conf=0.5, device=0) for r in results: for box, mask in zip(r.boxes, r.masks): cls_id = int(box.cls[0]) label = model.names[cls_id] color = (0, 200, 0) if label == 'parcel' else (0, 0, 255) mask_np = mask.data[0].cpu().numpy() mask_np = cv2.resize(mask_np, (img.shape[1], img.shape[0])) img[mask_np > 0.5] = img[mask_np > 0.5] * 0.6 + np.array(color) * 0.4 cv2.imwrite('inspect_result.jpg', img)mask.data[0]拿到的是模型输出的二值掩码,尺寸与输入分辨率一致,需要resize回原图尺寸才能叠加显示。conf=0.5是置信度阈值,条码类别如果被大量过滤掉,可以降到 0.25 再跑一遍,对比漏检情况。掩码与条码边界如果偏差在 5 个像素以上,或者掩码把条码上下两端的空白静区也包含了,说明模型学到的边界偏好需要修正——要么加更多边缘清晰的标注样本,要么提高imgsz重新微调。
5.2 小目标条码的补强推理:切片推理提升小掩码召回
如果条码在整图中的像素占比太小,训练数据里此类样本又不够,推理时可以考虑切片辅助推理。把原图按 512×512 的窗口切块,分别推理再合并结果,小目标的像素占比相当于被放大了 4~6 倍。Ultralytics没有内置这个功能,但用sahi库可以很方便地实现:
pip install sahifrom sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction model = AutoDetectionModel.from_pretrained( model_type='ultralytics', model_path='runs/parcel_seg/train/best.pt', confidence_threshold=0.4, image_size=1280, device='cuda:0' ) result = get_sliced_prediction( 'val_sample.jpg', model, slice_height=640, slice_width=640, overlap_height_ratio=0.2, overlap_width_ratio=0.2, ) result.export_visuals(export_dir='sahi_out/')slice_height和slice_width设置切片尺寸,overlap_height_ratio设重叠率,避免条码恰好被切在切片边界上导致检不出来。重叠率推荐 0.2,太小会漏检边界目标,太大则重复推理区域变多,推理耗时上升。切片推理适合数量不多的关键图像做检出,整体吞吐会明显下降,线上实时处理不要直接套用,更合理的做法是先用全局模型跑一遍,对置信度低或掩码面积过小的区域再做局部放大推理。
5.3 掩码驱动的条码裁剪:把手动ROI逻辑换成掩码ROI
部署时,分割掩码最直接的用途就是把条码从复杂背景中精确裁出来送入解码库。传统检测框裁出来的区域包含大量包装图案,解码器经常被干扰;掩码裁剪则只保留条码本体像素,背景被置黑,解码成功率会有明显提升。
import cv2 import numpy as np mask_np = ... # 模型输出的二值掩码,uint8且已resize到原图尺寸 img = cv2.imread('parcel_with_barcode.jpg') contours, _ = cv2.findContours(mask_np, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) c = max(contours, key=cv2.contourArea) x, y, w, h = cv2.boundingRect(c) roi = img[y:y+h, x:x+w] mask_roi = mask_np[y:y+h, x:x+w] barcode_clean = cv2.bitwise_and(roi, roi, mask=mask_roi) barcode_clean = cv2.copyMakeBorder(barcode_clean, 20, 20, 20, 20, cv2.BORDER_CONSTANT, value=(255, 255, 255)) # 此处把 barcode_clean 送入 pyzbar 或 ZXing 解码cv2.findContours从掩码提取外轮廓,boundingRect取最小外接矩形,bitwise_and保留掩码区域的像素,最后加一圈白色边框模拟条码的静区要求——条码解码标准要求边缘有足够的空白区域,直接裁到掩码边界反而会降低解码率。这一步做完,验证一下在各类光照和遮挡条件下,掩码裁剪后的条码解码成功率是否比检测框裁剪有明显提升。如果提升不明显,重点检查掩码是否把条码边缘的深浅模块切掉了,必要时对掩码做一次cv2.dilate膨胀两三个像素再裁剪。
本文还有配套的精品资源,点击获取