python - 如何在大图中定位二维码以提高解码性能?

标签 python image-processing computer-vision qr-code

背景
我需要在 Raspberry Pi 上的大图像 (2500x2000) 中检测和解码相对较小的二维码 (110x110 像素)。二维码可以在框架中的任何位置,但方向应该是正常的,即充值。我们使用高质量的工业相机和镜头,因此图像通常质量良好且清晰。
目前,我能够使用 pyzbar 可靠地检测和解码图像当我使用 aprox 600x500 的窗口裁剪 QR 码周围的图像时。如果我尝试解码完整图像,则不会检测/解码该符号。
我试过的
我编写了一个循环,在图像上滑动一个裁剪窗口,并尝试分别解码每个裁剪的帧。每次迭代我都会将窗口移动 50%,以确保不会错过窗口边缘的任何符号。
我也尝试过使用 OpenCV 进行检测/解码,但性能并不比 pyzbar 好。
我的解决方案有问题
影响我当前项目的问题:
滑动窗口方法难以调优,效率低且 b/c 慢:

  • 它使整个区域被分析了近 4 次;将窗口移动 50% 的副作用,
  • 最可靠的窗口尺寸往往很小并且需要多次迭代,
  • 符号大小可能会因距离相机更近/更远而有所不同。

  • 可能影响我将使用此方法的其他项目的问题:
  • 滑动窗口可能会不止一次地捕捉到一个符号,因此很难确定该符号是否出现了不止一次。

  • 问题
    如何找到 QR 码的大致位置,以便相应地裁剪图像?
    我对任何提高检测/解码性能的解决方案感兴趣,但更喜欢那些(a)使用机器学习技术(我是 ML 新手但愿意学习),(b)使用 OpenCV 图像预处理或(c ) 改进我的基本裁剪算法。
    示例图像
    这是我用于测试的示例图像之一。故意降低照明质量来近似最坏的情况,但是在裁剪时各个代码仍然可以正确检测和解码。
    QR Code Test Image 001

    最佳答案

    我想我找到了一种简单而可靠的方法,可以检测二维码的角落。但是,我的方法假设 QR 与其周围区域之间存在一些对比(越多越好)。另外,我们必须记住,pyzbar也不是 opencv.QRCodeDetector是 100% 可靠的。
    所以,这是我的方法:

  • 调整图像大小。 经过一些实验,我得出的结论是 pyzbar不是完全尺度不变的。虽然我没有可以支持这一说法的引用资料,但我仍然使用中小型图像进行条码检测作为经验法则。您可以跳过此步骤,因为它看起来完全是任意的。

  • image = cv2.imread("image.jpg")
    scale = 0.3
    width = int(image.shape[1] * scale)
    height = int(image.shape[0] * scale)
    image = cv2.resize(image, (width, height))
    
  • 阈值。 我们可以利用条形码在白色表面上通常是黑色的这一事实。对比度越高越好。

  • gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    _, thresh = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
    
    image after masking
    3. 扩张+轮廓。 这一步有点棘手,如果我的英语在这里不完全清楚,我深表歉意。从上图中我们可以看到,二维码内部的白色之间有黑色空间。如果我们只是找到轮廓,那么 opencv 将假设这些空间是独立的实体,而不是整体的一部分。如果我们想要转换 QR 码并使其看起来只是一个白色方 block ,我们必须做一些 morphological operations .也就是说,我们必须扩大图像。
    # The bigger the kernel, the more the white region increases.
    # If the resizing step was ignored, then the kernel will have to be bigger
    # than the one given here.
    kernel = np.ones((3, 3), np.uint8)
    thresh = cv2.dilate(thresh, kernel, iterations=1)
    contours, _ = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
    
    threshold after dilation
    4. 过滤和获取边界框。 大多数找到的轮廓都太小而无法包含条形码,因此我们必须过滤它们以使我们的搜索空间更小。过滤掉弱候选后,我们可以获取强候选的边界框。
    编辑:在这种情况下,我们按区域过滤(小区域 = 弱候选),但我们也可以按 extent 过滤。的检测。基本上,范围测量的是对象的矩形,我们可以使用该信息,因为我们知道 QR 码是正方形。我选择了大于 pi/4 的范围,因为这是一个完美圆的范围,这意味着我们也在过滤掉圆形对象。
    bboxes = []
    for cnt in contours:
      area = cv2.contourArea(cnt)
      xmin, ymin, width, height = cv2.boundingRect(cnt)
      extent = area / (width * height)
      
      # filter non-rectangular objects and small objects
      if (extent > np.pi / 4) and (area > 100):
        bboxes.append((xmin, ymin, xmin + width, ymin + height))
    
    Search space
    5. 检测条形码。 我们已将搜索空间缩小到只有实际的二维码!现在我们终于可以使用pyzbar了无需过多担心进行条码检测需要很长时间。
    qrs = []
    info = set()
    for xmin, ymin, xmax, ymax in bboxes:
      roi = image[ymin:ymax, xmin:xmax]
      detections = pyzbar.decode(roi, symbols=[pyzbar.ZBarSymbol.QRCODE])
      for barcode in detections:
         info.add(barcode.data)
         # bounding box coordinates
         x, y, w, h = barcode.rect
         qrs.append((xmin + x, ymin + y, xmin + x + w, ymin + y + height))
    
    不幸的是,pyzbar只能解码最大的二维码(b'3280406-001')的信息,即使两个条形码都在搜索空间中。关于知道检测到特定代码的次数,您可以使用 Counter来自 collections 的对象标准模块。如果您不介意拥有这些信息,那么您可以像我在这里所做的那样使用一组。
    希望这会有所帮助:)。

    关于python - 如何在大图中定位二维码以提高解码性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63195577/

    相关文章:

    python - 如何在c内部调用静态pyobject类型的c函数

    algorithm - 从 3D 坐标列表中找到最适合的平面

    ios - 查找立体图像中匹配关键点的距离

    Python:查找视频中的笔迹数量

    python - Gtk3:设置固定的窗口大小(小于子部件请求的大小)

    python - 猜测 1 到 100 之间的数字

    algorithm - 可以使用什么算法来识别图像是否为 "same"或类似图像,无论大小如何?

    java - 加载太多 JPEG 文件(100 秒)导致内存不足错误

    Python SSL错误解密失败或坏记录mac

    使用由不规则多边形界定/轮廓的图像进行 OpenCV 级联训练?