You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python将多贴纸图片中的SKU与序列号条码按贴纸分组

问题描述

目标

使用Python实现产品条码自动扫描并入物流系统。

场景

每个产品的单张贴纸包含两个条码:SKU(标识产品线)、序列号(同产品线内产品的唯一ID)。图片中可能存在多组不同产品线的贴纸,每组贴纸对应唯一序列号。

现有进度

已通过pyzbar与cv2库实现图片中多个条码的扫描,代码如下:

from pyzbar.pyzbar import decode, ZBarSymbol
import cv2

testing_image_readin = cv2.imread(testing_image_path)
detected_barcodes = decode(testing_image_readin, symbols=[ZBarSymbol.CODE128, ZBarSymbol.EAN13])

if not detected_barcodes:
    print("Barcode Not Detected or your barcode is blank/corrupted!")
else:
    for barcode in detected_barcodes:
        # Locate the barcode position in image
        (x, y, w, h) = barcode.rect

        cv2.rectangle(testing_image_readin, (x - 10, y - 10),
                      (x + w + 10, y + h + 10),
                      (255, 0, 0), 2)

        if barcode.data != "":
            # Print the barcode data
            print(barcode.data)
            print(barcode.type)

当前问题

无法将识别出的SKU与序列号条码按所属单张贴纸进行分组,需实现分组方法与思路。

示例说明

  • 单张贴纸:包含一个CODE128类型的序列号、一个EAN13类型的SKU
  • 对应程序输出:
b'07FFD58D47189877'
CODE128
b'0871828002084'
EAN13
  • 多张贴纸俯视:图片中包含多组上述贴纸,需将每组的SKU与序列号配对。

解决方案

核心思路

利用条码在图片中的空间位置关系分组:同一张贴纸的两个条码距离更近,且大概率处于同一区域。具体步骤:

  1. 为每个条码计算中心坐标
  2. 计算条码间的欧氏距离,将距离小于阈值的条码归为一组
  3. 结合条码类型(SKU为EAN13,序列号为CODE128),每组内自动配对SKU和序列号

实现代码

from pyzbar.pyzbar import decode, ZBarSymbol
import cv2
import math

def calculate_center(x, y, w, h):
    """计算条码矩形的中心坐标"""
    return (x + w//2, y + h//2)

def distance_between_points(p1, p2):
    """计算两点间欧氏距离"""
    return math.hypot(p2[0] - p1[0], p2[1] - p1[1])

def group_barcodes(detected_barcodes, distance_threshold=100):
    """将同一张贴纸的条码分组"""
    groups = []
    # 按y轴中心排序,优化分组效率
    barcodes_sorted = sorted(detected_barcodes, key=lambda b: calculate_center(*b.rect)[1])
    
    for barcode in barcodes_sorted:
        added = False
        curr_center = calculate_center(*barcode.rect)
        # 遍历已有组,判断是否属于同一组
        for group in groups:
            group_center = calculate_center(*group[0].rect)
            if distance_between_points(curr_center, group_center) < distance_threshold:
                group.append(barcode)
                added = True
                break
        if not added:
            groups.append([barcode])
    
    # 过滤掉单个条码的组(识别不全的贴纸)
    return [g for g in groups if len(g) == 2]

def pair_sku_serial(group):
    """分组内配对SKU(EAN13)和序列号(CODE128)"""
    sku = None
    serial = None
    for barcode in group:
        if barcode.type == 'EAN13':
            sku = barcode.data.decode('utf-8')
        elif barcode.type == 'CODE128':
            serial = barcode.data.decode('utf-8')
    return {'SKU': sku, 'Serial': serial}

# 主流程
testing_image_path = "your_image_path.jpg"
testing_image_readin = cv2.imread(testing_image_path)
detected_barcodes = decode(testing_image_readin, symbols=[ZBarSymbol.CODE128, ZBarSymbol.EAN13])

if not detected_barcodes:
    print("未检测到条码,或条码为空/损坏!")
else:
    barcode_groups = group_barcodes(detected_barcodes)
    print("分组结果:")
    for idx, group in enumerate(barcode_groups, 1):
        pair = pair_sku_serial(group)
        print(f"第{idx}组:SKU={pair['SKU']}, 序列号={pair['Serial']}")
        
        # 可视化分组(不同组用不同颜色框)
        color = (0, 255*idx//len(barcode_groups), 0)
        for barcode in group:
            x, y, w, h = barcode.rect
            cv2.rectangle(testing_image_readin, (x-10, y-10), (x+w+10, y+h+10), color, 2)
    
    cv2.imshow("Grouped Barcodes", testing_image_readin)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

关键说明

  1. 距离阈值调整:distance_threshold需根据实际图片尺寸设置,建议先测试单张贴纸的两个条码中心距离,阈值设为该距离的1.2倍左右。
  2. 条码类型适配:若SKU和序列号的条码类型有变动,直接修改pair_sku_serial函数的判断逻辑即可。
  3. 异常处理:过滤单个条码的组,避免无效数据;若分组内缺少SKU/序列号,可添加日志提醒人工核对。

内容的提问来源于stack exchange,提问作者GenericName

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 19:48:26