用 Python 和 OpenCV 检测图片上的条形码

这篇博文的目的是应用计算机视觉和图像处理技术,展示一个条形码检测的基本实现。我所实现的算法本质上基于StackOverflow 上的这个问题,浏览代码之后,我提供了一些对原始算法的更新和改进。

首先需要留意的是,这个算法并不是对所有条形码有效,但会给你基本的关于应用什么类型的技术的直觉。

假设我们要检测下图中的条形码:

用 Python 和 OpenCV 检测图片上的条形码

图1:包含条形码的示例图片

现在让我们开始写点代码,新建一个文件,命名为detect_barcode.py,打开并编码:

1 # import the necessary packages

2 import numpy as np

3 import argparse

4 import cv2

5

6 # construct the argument parse and parse the arguments

7 ap = argparse.ArgumentParser()

8 ap.add_argument("-i", "--image", required = True, help = "path to the image file")

9 args = vars(ap.parse_args())

 

我们首先做的是导入所需的软件包,我们将使用NumPy做数值计算,argparse用来解析命令行参数,cv2是OpenCV的绑定。

然后我们设置命令行参数,我们这里需要一个简单的选择,–image是指包含条形码的待检测图像文件的路径。

现在开始真正的图像处理:

11 # load the image and convert it to grayscale

12 image = cv2.imread(args["image"])

13 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

14

15 # compute the Scharr gradient magnitude representation of the images

16 # in both the x and y direction

17 gradX = cv2.Sobel(gray, ddepth = cv2.cv.CV_32F, dx = 1, dy = 0, ksize = -1)

18 gradY = cv2.Sobel(gray, ddepth = cv2.cv.CV_32F, dx = 0, dy = 1, ksize = -1)

19

20 # subtract the y-gradient from the x-gradient

21 gradient = cv2.subtract(gradX, gradY)

22 gradient = cv2.convertScaleAbs(gradient)

 

12~13行:从磁盘载入图像并转换为灰度图。

17~18行:使用Scharr操作(指定使用ksize = -1)构造灰度图在水平和竖直方向上的梯度幅值表示。

21~22行:Scharr操作之后,我们从x-gradient中减去y-gradient,通过这一步减法操作,最终得到包含高水平梯度和低竖直梯度的图像区域。

上面的gradient表示的原始图像看起来是这样的:

用 Python 和 OpenCV 检测图片上的条形码

图:2:条形码图像的梯度表示

注意条形码区域是怎样通过梯度操作检测出来的。下一步将通过去噪仅关注条形码区域。

?

24 # blur and threshold the image

25 blurred = cv2.blur(gradient, (9, 9))

26 (_, thresh) = cv2.threshold(blurred, 225, 255, cv2.THRESH_BINARY)

 

25行:我们要做的第一件事是使用9*9的内核对梯度图进行平均模糊,这将有助于平滑梯度表征的图形中的高频噪声。

26行:然后我们将模糊化后的图形进行二值化,梯度图中任何小于等于255的像素设为0(黑色),其余设为255(白色)。

模糊并二值化后的输出看起来是这个样子:

用 Python 和 OpenCV 检测图片上的条形码

图3:二值化梯度图以此获得长方形条形码区域的粗略近似

然而,如你所见,在上面的二值化图像中,条形码的竖杠之间存在缝隙,为了消除这些缝隙,并使我们的算法更容易检测到条形码中的“斑点”状区域,我们需要进行一些基本的形态学操作:

28 # construct a closing kernel and apply it to the thresholded image

29 kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (21, 7))

30 closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)

 

29行:我们首先使用cv2.getStructuringElement构造一个长方形内核。这个内核的宽度大于长度,因此我们可以消除条形码中垂直条之间的缝隙。

30行:这里进行形态学操作,将上一步得到的内核应用到我们的二值图中,以此来消除竖杠间的缝隙。

现在,你可以看到这些缝隙相比上面的二值化图像基本已经消除:

用 Python 和 OpenCV 检测图片上的条形码

图4:使用形态学中的闭运算消除条形码竖条之间的缝隙

当然,现在图像中还有一些小斑点,不属于真正条形码的一部分,但是可能影响我们的轮廓检测。

让我们来消除这些小斑点:

32 # perform a series of erosions and dilations

33 closed = cv2.erode(closed, None, iterations = 4)

34 closed = cv2.dilate(closed, None, iterations = 4)

 

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/8bf7e6d053879848d2e230522fdff393.html