如何可靠地检测条码的 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 或其他库?)
备注:
它 是 可能的,这是一个很好的例子(但遗憾的是不是评论中提到的开源):
Object detection, very fast and robust blurry 1D barcode detection for real-time applications
角点检测似乎非常好而且非常快,即使条形码只是整个图像的一小部分(这对我很重要)。
有趣的解决方案:Real-time barcode detection in video with Python and OpenCV但该方法存在局限性(参见文章中:条形码应关闭等)限制了潜在用途。此外,我更想为此寻找一个现成的库。
有趣的解决方案2:Detecting Barcodes in Images with Python and OpenCV but again, it does not seem like a production-ready solution, but more a research in progress. Indeed, I tried their code on this image but the detection does not yield successful result. It has to be noted that it doesn't take any spec of the barcode in consideration for the detection (the fact there's a start/stop symbol等)
import numpy as np
import cv2
image = cv2.imread("000.jpg")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gradX = cv2.Sobel(gray, ddepth = cv2.CV_32F, dx = 1, dy = 0, ksize = -1)
gradY = cv2.Sobel(gray, ddepth = cv2.CV_32F, dx = 0, dy = 1, ksize = -1)
gradient = cv2.subtract(gradX, gradY)
gradient = cv2.convertScaleAbs(gradient)
blurred = cv2.blur(gradient, (9, 9))
(_, thresh) = cv2.threshold(blurred, 225, 255, cv2.THRESH_BINARY)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (21, 7))
closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
closed = cv2.erode(closed, None, iterations = 4)
closed = cv2.dilate(closed, None, iterations = 4)
(_, 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)
cv2.imshow("Image", image)
cv2.waitKey(0)
方案二很好。使它在图像上失败的关键因素是阈值。如果将参数 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)
结果:
检测到的条码:
检测条码及信息:
我正在尝试使用 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 或其他库?)
备注:
它 是 可能的,这是一个很好的例子(但遗憾的是不是评论中提到的开源):
Object detection, very fast and robust blurry 1D barcode detection for real-time applications
角点检测似乎非常好而且非常快,即使条形码只是整个图像的一小部分(这对我很重要)。
有趣的解决方案:Real-time barcode detection in video with Python and OpenCV但该方法存在局限性(参见文章中:条形码应关闭等)限制了潜在用途。此外,我更想为此寻找一个现成的库。
有趣的解决方案2:Detecting Barcodes in Images with Python and OpenCV but again, it does not seem like a production-ready solution, but more a research in progress. Indeed, I tried their code on this image but the detection does not yield successful result. It has to be noted that it doesn't take any spec of the barcode in consideration for the detection (the fact there's a start/stop symbol等)
import numpy as np import cv2 image = cv2.imread("000.jpg") gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) gradX = cv2.Sobel(gray, ddepth = cv2.CV_32F, dx = 1, dy = 0, ksize = -1) gradY = cv2.Sobel(gray, ddepth = cv2.CV_32F, dx = 0, dy = 1, ksize = -1) gradient = cv2.subtract(gradX, gradY) gradient = cv2.convertScaleAbs(gradient) blurred = cv2.blur(gradient, (9, 9)) (_, thresh) = cv2.threshold(blurred, 225, 255, cv2.THRESH_BINARY) kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (21, 7)) closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel) closed = cv2.erode(closed, None, iterations = 4) closed = cv2.dilate(closed, None, iterations = 4) (_, 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) cv2.imshow("Image", image) cv2.waitKey(0)
方案二很好。使它在图像上失败的关键因素是阈值。如果将参数 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)
结果:
检测到的条码:
检测条码及信息: