如何可靠地检测条码的 4 个角?

How to reliably detect a barcode's 4 corners?

我正在尝试使用 Python + zbar 模块检测此 Code128 条形码:

(图片下载linkhere).

这个有效:

import cv2, numpy
import zbar
from PIL import Image 
import matplotlib.pyplot as plt

scanner = zbar.ImageScanner()
pil = Image.open("000.jpg").convert('L')
width, height = pil.size    
plt.imshow(pil); plt.show()
image = zbar.Image(width, height, 'Y800', pil.tobytes())
result = scanner.scan(image)

for symbol in image:
    print symbol.data, symbol.type, symbol.quality, symbol.location, symbol.count, symbol.orientation

但只检测到一个点:(596, 210).

如果我应用黑白阈值:

pil = Image.open("000.jpg").convert('L')
pil = pil .point(lambda x: 0 if x<100 else 255, '1').convert('L')    

比较好,我们有3个点:(596, 210), (482, 211), (596, 212)。但这又增加了一个困难(为每个新图像自动找到最佳阈值 - 这里是 100)。

仍然,我们没有条形码的 4 个角。

问题:如何使用 Python 在图像上可靠地找到条形码的 4 个角?(也许还有 OpenCV 或其他库?)

备注:

方案二很好。使它在图像上失败的关键因素是阈值。如果将参数 225 降低到 55,您将获得更好的结果。

我重新编写了代码,并进行了一些调整。如果您愿意,原始代码很好。 documentation for OpenCV is quite good, and there are very good Python tutorials.

import numpy as np
import cv2

image = cv2.imread("barcode.jpg")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# equalize lighting
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
gray = clahe.apply(gray)

# edge enhancement
edge_enh = cv2.Laplacian(gray, ddepth = cv2.CV_8U, 
                         ksize = 3, scale = 1, delta = 0)
cv2.imshow("Edges", edge_enh)
cv2.waitKey(0)
retval = cv2.imwrite("edge_enh.jpg", edge_enh)

# bilateral blur, which keeps edges
blurred = cv2.bilateralFilter(edge_enh, 13, 50, 50)

# use simple thresholding. adaptive thresholding might be more robust
(_, thresh) = cv2.threshold(blurred, 55, 255, cv2.THRESH_BINARY)
cv2.imshow("Thresholded", thresh)
cv2.waitKey(0)
retval = cv2.imwrite("thresh.jpg", thresh)

# do some morphology to isolate just the barcode blob
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 9))
closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
closed = cv2.erode(closed, None, iterations = 4)
closed = cv2.dilate(closed, None, iterations = 4)
cv2.imshow("After morphology", closed)
cv2.waitKey(0)
retval = cv2.imwrite("closed.jpg", closed)

# find contours left in the image
(_, cnts, _) = cv2.findContours(closed.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
c = sorted(cnts, key = cv2.contourArea, reverse = True)[0]
rect = cv2.minAreaRect(c)
box = np.int0(cv2.boxPoints(rect))
cv2.drawContours(image, [box], -1, (0, 255, 0), 3)
print(box)
cv2.imshow("found barcode", image)
cv2.waitKey(0)
retval = cv2.imwrite("found.jpg", image)

edge.jpg

thresh.jpg

closed.jpg

found.jpg

控制台输出:

[[596 249]
 [470 213]
 [482 172]
 [608 209]]

更高版本的 OpenCV 4.x 有一个 separate class for detecting barcodes

cv2.barcode_BarcodeDetector() 配备了 3 个 in-built 功能:

  • decode(): returns 解码信息和类型
  • detect(): returns 包围每个检测到的条形码的 4 个角点
  • detectAndDecode(): returns 以上全部

使用的示例图像是 from pyimagesearch blog:

points 中捕获了 4 个角。

代码:

img = cv2.imread('barcode.jpg')

barcode_detector = cv2.barcode_BarcodeDetector()

# 'retval' is boolean mentioning whether barcode has been detected or not
retval, decoded_info, decoded_type, points = barcode_detector.detectAndDecode(img)

# copy of original image
img2 = img.copy()

# proceed further only if at least one barcode is detected:
if retval:
    points = points.astype(np.int)
    for i, point in enumerate(points):
        img2 = cv2.drawContours(img2,[point],0,(0, 255, 0),2)

        # uncomment the following to print decoded information
        #x1, y1 = point[1]
        #y1 = y1 - 10
        #cv2.putText(img2, decoded_info[i], (x1, y1), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 0), 3, 2)

结果:

检测到的条码:

检测条码及信息: