简体   繁体   English

使用 python 将具有多个贴纸的图像中的多个条形码组合在一起

[英]Group multiple barcodes on a single sticker together from an image with multiple stickers using python

Objective: Automate the scanning of our product's barcodes into our shipping program using the Python language.目标:使用 Python 语言将我们产品的条形码自动扫描到我们的运输程序中。

Situation: Each sticker on a product has two barcodes.情况:产品上的每个标签都有两个条形码。 One (the SKU) identifies what the product line is, and the other (serial number) is a unique ID identifying it from the others in the same product line.一个(SKU)标识产品线是什么,另一个(序列号)是一个唯一的 ID,将它与同一产品线中的其他产品区分开来。 For example, in an image, there could be ten stickers with the same SKU of, say, "Product A" and all ten of those stickers have unique serial numbers.例如,在一张图片中,可能有十张具有相同 SKU 的贴纸,例如“产品 A”,并且所有这十张贴纸都有唯一的序列号。 There could also be "Product B" and "Product C" in the image as well.图像中也可能有“产品 B”和“产品 C”。

Progress: I can use pyzbar and cv2 to scan multiple barcodes in an image successfully.进展:我可以使用 pyzbar 和 cv2 成功扫描图像中的多个条形码。

Issue: I want to group the SKU and Serial number barcodes by sticker, but I don't know how to do this or where to start.问题:我想按贴纸对 SKU 和序列号条形码进行分组,但我不知道如何执行此操作或从哪里开始。

Code I am using我正在使用的代码

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)

UPDATE - Adding Example Images:更新 - 添加示例图像:

I dont have an example of the exact image I am describing so I have made one in with graphics.我没有我所描述的确切图像的示例,所以我用图形制作了一个。 This would be a top-down image looking at the stickers on the Product Boxes.这将是一个自上而下的图像,查看产品盒上的贴纸。

Example Box:示例框:

示例框

Program output:程序 output:

b'07FFD58D47189877'
CODE128
b'0871828002084'
EAN13

程序输出

Generated Top Down view of multiple boxes together All with unique serial numbers:生成多个盒子的自上而下视图 全部带有唯一的序列号:

生成多个盒子的自上而下视图 全部带有唯一的序列号。

Ok since pyzbar / zbar seems to have bugs that cause its bounding boxes to catch multiple codes, or not detect codes that are rotated too much, I'll use OpenCV's barcode detection, rectify the codes, then use pyzbar for decoding.好的,因为pyzbar / zbar似乎存在导致其边界框捕获多个代码的错误,或者没有检测到旋转过多的代码,我将使用 OpenCV 的条形码检测,纠正代码,然后使用 pyzbar 进行解码。 OpenCV can also decode, but not as many different types. OpenCV 也可以解码,但没有那么多不同的类型。

Approach:方法:

  • find codes, get bounding boxes查找代码,获取边界框
  • for each bounding box, enlarge it width-wise and see what other codes it overlaps with对于每个边界框,将其按宽度放大并查看它与哪些其他代码重叠
  • build "groups"建立“团体”

Input:输入:

输入

Detect barcodes with OpenCV:使用 OpenCV 检测条形码:

det = cv.barcode.BarcodeDetector()
(rv, detections) = det.detect(im)
# detections: four corner points per detection

Extract rectangle:提取矩形:

def extract_region_from_corners(image, corners):
    # order:
    #   [1] top left     [2] top right
    #   [0] bottom left  [3] bottom right
    (bl, tl, tr, br) = corners

    # axis vectors
    vx = tr - tl
    vy = bl - tl

    lx = np.linalg.norm(vx)
    ly = np.linalg.norm(vy)

    H = np.eye(3)
    H[0:2,2] = tl # origin
    H[:2,0] = vx / lx
    H[:2,1] = vy / ly

    dst = cv.warpAffine(src=image,
        M=H[:2], dsize=(int(lx), int(ly)),
        flags=cv.INTER_LINEAR | cv.WARP_INVERSE_MAP)

    return dst

Utility function:实用程序 function:

def corners_to_rrect(corners):
    # order:
    #   [1] top left     [2] top right
    #   [0] bottom left  [3] bottom right
    (bl, tl, tr, br) = corners

    vx = ((tr - tl) + (br - bl)) / 2
    vy = ((bl - tl) + (br - tr)) / 2
    lx = np.linalg.norm(vx)
    ly = np.linalg.norm(vy)

    center = tuple(corners.mean(axis=0))
    size = (lx, ly)
    angle = np.arctan2(vx[1], vx[0]) / np.pi * 180 # degrees

    return (center, size, angle)

Extract codes, decode, note their RotatedRect positions:提取代码,解码,记下它们的RotatedRect位置:

found_codes = []
canvas = im.copy()

for detection_corners in detections:
    rrect = corners_to_rrect(detection_corners)

    (rrect_width, rrect_height) = rrect[1]
    assert rrect_width > rrect_height, ("assuming barcode lies lengthwise", rrect)

    roi = extract_region_from_corners(image=im, corners=detection_corners)

    [code] = pyzbar.decode(roi, symbols=[ZBarSymbol.CODE128, ZBarSymbol.EAN13])
    print(code.type, code.data, rrect)

    found_codes.append( (rrect, code) )

    cv.polylines(img=canvas, pts=[detection_corners.astype(np.int32)], isClosed=True, color=(255, 0, 0), thickness=2)
CODE128 b'07FFD58D47189879' ((706.9937, 355.28094), (434.7604, 65.09412), 15.141749040805594)
CODE128 b'07FFD58D47189878' ((266.48895, 361.89154), (435.78812, 65.95062), -15.051276355059604)
CODE128 b'07FFD58D47189876' ((237.65492, 816.5005), (434.7883, 65.28357), 15.058296081979087)
CODE128 b'07FFD58D47189877' ((731.69257, 817.5774), (435.56052, 62.905884), -15.084296904602034)
EAN13 b'0871828002084' ((228.3433, 239.54503), (235.90378, 66.31835), -15.219580753945182)
EAN13 b'0871828002077' ((705.7166, 693.0964), (236.39447, 65.9507), -15.102472037983436)
EAN13 b'0871828002091' ((742.64703, 237.18982), (240.23358, 67.790794), 15.171352788215723)
EAN13 b'0871828002060' ((270.11478, 696.054), (236.27463, 64.16398), 15.201185346963047)

found_codes

More utility functions:更多实用功能:

def enlarge_rrect(rrect, factor=1, fx=1, fy=1):
    (center, size, angle) = rrect
    (width, height) = size
    new_size = (width * factor * fx, height * factor * fy)
    return (center, new_size, angle)


def merge_intersecting_sets(sets):
    # sets = set(map(frozenset, sets))
    while True:
        oldcount = len(sets)

        # merge or add
        newsets = set()
        for thisset in sets:
            for thatset in newsets:
                if thisset & thatset:
                    newsets.remove(thatset)
                    newsets.add(thisset | thatset)
                    break
            else:
                newsets.add(thisset)

        sets = newsets
        if len(sets) == oldcount:
            break
    
    return sets

# assert merge_intersecting_sets([{1,2}, {2,3}, {3,4}, {5,6}]) == {frozenset({1,2,3,4}), frozenset({5,6})}

Determine groups using enlarged RotatedRect and intersection test:使用放大RotatedRect和相交测试确定组:

def associate_rrects(rrects, fx=1, fy=1):
    "associate RotatedRect instances, given enlargement factors in horizontal and vertical direction"

    # build connected components by adjacency
    components = set()
    for (i, thisrect) in enumerate(rrects):
        thisenlarged = enlarge_rrect(thisrect, fx=fx, fy=fy)

        component = {i}
        for (j, thatrect) in enumerate(rrects):
            (rv, intersection) = cv.rotatedRectangleIntersection(thisenlarged, thatrect)
            if rv != cv.INTERSECT_NONE: # i.e. INTERSECT_PARTIAL, INTERSECT_FULL
                component.add(j)

        components.add(frozenset(component))
    
    # merge intersecting components (transitivitiy)
    components = merge_intersecting_sets(components)

    return components


components = associate_rrects([rrect for rrect, code in found_codes], fy=5)
print(components)
{frozenset({1, 4}), frozenset({2, 7}), frozenset({0, 6}), frozenset({3, 5})}

Now you can pick from found_codes using those indices.现在您可以使用这些索引从found_codes中进行选择。

Drawing the groups, using convex hull:使用凸包绘制组:

canvas = im.copy()
for component in components:
    component_codes = [found_codes[i] for i in component]

    component_corners = np.concatenate([
        cv.boxPoints(rrect)
        for (rrect, code) in component_codes
    ])
    hull = cv.convexHull(component_corners)
    cv.polylines(img=canvas, pts=[hull.astype(np.int32)], isClosed=True, color=(255, 255, 0), thickness=2)

    for (rrect, code) in component_codes:
        #print(rrect, code)
        cv.polylines(img=canvas, pts=[cv.boxPoints(rrect).astype(int)], isClosed=True, color=(255, 0, 0), thickness=2)
        cv.putText(canvas, text=str(code.data),
            org=np.int0(rrect[0]), fontFace=cv.FONT_HERSHEY_SIMPLEX,
            fontScale=0.7, color=(0,0,0), thickness=8)
        cv.putText(canvas, text=str(code.data),
            org=np.int0(rrect[0]), fontFace=cv.FONT_HERSHEY_SIMPLEX,
            fontScale=0.7, color=(0,255,255), thickness=2)

帆布

Entire thing: https://gist.github.com/crackwitz/3a7e7e5d698274198393737415ef409a整件事: https://gist.github.com/crackwitz/3a7e7e5d698274198393737415ef409a

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在图像中找到多个条形码 - How to find the multiple barcodes in an image 使用python将多个图像连接到单个图像 - Concatenate multiple pieces of an image to a single image using python 如何使用 Python 和 pdf417 同时创建多个条形码 - How can I create multiple barcodes at the same time using Python and pdf417 python将多个子列表组合到单个列表中 - python combine multiple sublist in to single list by group python PIL从单个图像文件访问多个图像 - python PIL acces multiple images from a single image file 在Python中从单个大图像创建多个缩略图的最快方法 - Fastest way to create multiple thumbnails from a single large image in Python 如何从字典生成器中添加多个字典以创建单个整理的python字典 - How to add multiple dictionaries together from a dictionary generator to create single collated python dictionary 将多列中的匹配对组合在一起 Python - Group together matched pairs across multiple columns Python 如何<span>使用BS4</span>从多个<span>标签中</span>提取<span>数据并将数据分组在一起?</span> - How to extract from multiple <span> tags and group the data together using BS4? 使用python读取条形码 - Reading barcodes using python
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM