使用Python和OpenCV在视频中实时监测条形码
https://www.youtube.com/watch?v=oooDn5SBUAg
今天的博客是上篇博客的继续,上篇博客是使用Python和OpenCV检测图像中的条形码。
今天我们将重构代码去检测视频中的条形码。
举个例子,下图是我实验的一个截图,我手持Modern Warfare 3,我们的程序能够顺利检测到其背面的条形码。
图1:使用Python和OpenCV检测视频流中的条形码
注意:非常感谢Jason的建议,他在上次的博客中留言道:要是能检测视频中的条形码将是很酷的事情。你说的很对,这确实很酷。
举个例子,假设我们12月26日这天正在电玩店工作。店外面排起了足有十个街区长的队伍,这些孩子正等着退货或是换货(显而易见,他们的父母或亲戚买错东西了)。
为了加快换货速度,我们就到队伍中间去一个一个去扫描他们的条形码。但是问题来了,激光扫描枪的线是连接到柜台上的电脑上的,队伍可有10个街区长,怎么都够不到。
既然这样,我有个计划:那我们可以用智能手机代替。
使用我们的iPhone(或者Android),带上摄像头,切换到录像模式,向着漫漫长队开工吧。
只要手机摄像头扫描到商品上的条形码,我们的APP就会检测到它的信息并及时传回到柜台。
听起来是不是太美好了,简直难以置信。
也许吧。毕竟,你也可以用激光扫描枪和无线连接来完成这项工作。况且稍后也会看到我们方法的局限性。
但是,我还是觉得这是一个关于使用OpenCV和Python检测条形码的好例子,该例展示了利用OpenCV函数来构建一个真实的应用。
不管怎样,继续往下看,看我们是怎么使用OpenCV和Python来检测视频中的条形码的。
使用Python和OpenCV在视频中实时监测条形码
这就是我们的方案,我们的视频条形码检测系统分成两个部分:
- 部件1:该模块检测图像中的条形码(本例中,既是视频中的帧图像)。上篇博客中我们已经实现了该功能,现在我们只需要简单修改一下代码即可
- 部件2:该模块的功能是读取视频源,然后调用条形码检测模块。
我们先讲解第一部分,单帧图像的条形码检测。
部件1:视频帧中的条形码检测
由于上篇博客中已经讲到图像中条形码的检测,这里我就不再熬述。
尽管这样,基于完整性考虑,我快速过一下这部分内容。打开一个新文件,取名simple_barcode_detection.py,开始编码:
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
# import the necessary packages
import numpy as np
import cv2
def detect(image):
# convert the image to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# compute the Scharr gradient magnitude representation of the images
# in both the x and y direction
gradX = cv2.Sobel(gray, ddepth = cv2.cv.CV_32F, dx = 1, dy = 0, ksize = –1)
gradY = cv2.Sobel(gray, ddepth = cv2.cv.CV_32F, dx = 0, dy = 1, ksize = –1)
# subtract the y-gradient from the x-gradient
gradient = cv2.subtract(gradX, gradY)
gradient = cv2.convertScaleAbs(gradient)
# blur and threshold the image
blurred = cv2.blur(gradient, (9, 9))
(_, thresh) = cv2.threshold(blurred, 225, 255, cv2.THRESH_BINARY)
# construct a closing kernel and apply it to the thresholded image
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (21, 7))
closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
# perform a series of erosions and dilations
closed = cv2.erode(closed, None, iterations = 4)
closed = cv2.dilate(closed, None, iterations = 4)
# find the contours in the thresholded image
(cnts, _) = cv2.findContours(closed.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
# if no contours were found, return None
if len(cnts) == 0:
return None
# otherwise, sort the contours by area and compute the rotated
# bounding box of the largest contour
c = sorted(cnts, key = cv2.contourArea, reverse = True)[0]
rect = cv2.minAreaRect(c)
box = np.int0(cv2.cv.BoxPoints(rect))
# return the bounding box of the barcode
return box
|
如果你看过之前的博客,那么这段关于在图像中检测条形码的代码将非常熟悉。
第一件事就是导入NumPy库来做数值计算,cv2来调用OpenCV。
第5行定义的函数带1个参数,就是需要检测条形码的图像。
第7行将图像转换为灰度图像,9-16行检测图像中具有水平梯度高和垂直梯度低的区域(想了解更多,请参看前面条形码检测的博客)。
19和20行分别模糊图像和二值化图像,方便后续23-28行的形态学处理。形态学操作用来检出条形码的矩形区域,忽略其他区域。
现在我们已经有了条形码的矩形区域,接下来在30-31行就是找出其轮廓。
如果没有找到轮廓,那么就认为没有条形码(35和36行)。
如果找到了轮廓,在40行我们就对轮廓进行排序(轮廓按面积大小降序排列)。这里,我们同样假设面积最大的轮廓就是条形码的位置。
最后,我们拿到这个轮廓,然后计算该轮廓的矩形包络(41和42行)。然后45行就返回该包络的x,y坐标,意即条形码的位置。
现在,我们这个简单的条形码检测就告完成,步入第二部分,图像获取,然后整合第一部分。
部件2:访问摄像头,检测视频中的条形码
我们开始构建一个驱动接口以便后面检测视频中的条形码。打开并新建一个文件,取名detect_barcode.py,开始构建第二个部件:
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
# import the necessary packages
from pyimagesearch import simple_barcode_detection
import argparse
import cv2
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument(“-v”, “–video”, help = “path to the (optional) video file”)
args = vars(ap.parse_args())
# if the video path was not supplied, grab the reference to the
# camera
if not args.get(“video”, False):
camera = cv2.VideoCapture(0)
# otherwise, load the video
else:
camera = cv2.VideoCapture(args[“video”])
|
我们开始也是导入我们需要的库。出于方便管理的目的,我们将simple_barcode_detection函数放到pyimagesearch模块中,并在此导入。然后我们导入argparse来解析命令,导入cv2来调用OpenCV的函数。
6-9行解析我们的命令行参数。我们设置了一个可选项,–video,该选项指定需要检测条形码的视频文件路径。
注意:这个切换选项对于运行源代码提供的例子视频还是很有用的。忽略这个选项默认就是用笔记本或是台式机上的摄像头。
13-18行获取一个camera引用,如果没有–video选项,那么就获取一个webcam;反之就活去一个视频文件句柄(17-18行)。
设置完成后,我们就可以开始加载我们的条形码检测模块了。
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
# keep looping over the frames
while True:
# grab the current frame
(grabbed, frame) = camera.read()
# check to see if we have reached the end of the
# video
if not grabbed:
break
# detect the barcode in the image
box = simple_barcode_detection.detect(frame)
# if a barcode was found, draw a bounding box on the frame
cv2.drawContours(frame, [box], –1, (0, 255, 0), 2)
# show the frame and record if the user presses a key
cv2.imshow(“Frame”, frame)
key = cv2.waitKey(1) & 0xFF
# if the ‘q’ key is pressed, stop the loop
if key == ord(“q”):
break
# cleanup the camera and close any open windows
camera.release()
cv2.destroyAllWindows()
|
第2行我们开始循环抓取视频中的图像帧,直到抓取完视频中的图像或者说我们按下q键然后退出循环。
第5行中,我们访问camera。返回值是2-tuple.其一是boolean值,表示是否抓取成功(从摄像头或者视频文件)。frame这是实际抓取到的图像数据。
如果抓取失败(例如到了视频文件尾部),11和12行就退出循环。
一旦抓取到图像帧,在16行我们就利用条形码检测模块检测图像中的条形码,返回外接矩形范围。
我们将最终的包络区域绘制在图像中(20行),最后显示到屏幕上(23和24行).
最后,29和30行则检测q键是否按下,按下就退出循环。32和33行就进行camera对象的清理操作。
如你所见,我们的脚本还是比较简洁的。开始运行我们的代码,看看结果如何。
成功检测视频中的条形码
我们试一些例子,打开终端,输入如下指令:
Python
1
|
$ python detect_barcode.py —video video/video_games.mov
|
本博客的开始部分就展示了我们脚本的输出结果。下面则是三个成功的条形码检测视频截图。
图2:在Xbox视频游戏流中,成功检测条形码
我们看一个在衣服优惠券上检测的例子:
Python
1
|
$ python detect_barcode.py —video video/coupon.mov
|
如下是一个视频流截图:
图3:使用Python和OpenCV成功检测条形码的示例
完整视频输出:
https://www.youtube.com/watch?v=YlF8hI5BGi8
当然,如我之前所说,我们的方法只在很好的条件下才有效(稍后会详述该方法的缺点和限制)
这就是一个条形码检测失败的例子。
图4:一个失败的例子。条形码离摄像头太远了
该种情形下,条形码离摄像头太远,图像中也有太多的干扰和噪声,例如视频游戏盒子上有很多的大块的文本区。
这个也是个失败案例,我只是想开个玩笑而已。
图5:我的耳朵当然不是条形码
再一次说,我们这只是一个很简单的解决办法,并不适用于所有情况。虽说不是一个健壮的解决方案,但如果能满足下述条件的情况下,简单的图像处理还是能提供让人惊讶的够好的结果。
限制条件和缺点
就如博文中见到的,我们的方法在一定假设条件下还是能顺利检测到视频中的条形码。
第一个假设:我们的相机以90度的静态视角来观察这个条形码。以使条形码区域的图像梯度能够被条形码检测器检测到。
第二个假设:我们的视频或者摄像头是近距离查看条形码,也就是我们拿着智能手机直接对着条形码的上面,而不是远离摄像头。条形码离相机越远,成功检测的概率就越小。
那么怎样改进我们的条形码检测器呢?
好问题
Christoph Oberhofer 提供了一篇很好的综述,关于在QuaggaJS中如何实现条形码的鲁棒检测。 Tomasz Malisiewicz也写了一篇很好的文章,关于如何利用他的VMX中的机器学习算法来训练出一个条形码检测器。如果你想更近一步了解这些内容,请一定要看这些文章。
总结
基于之前在图像中检测条形码的程序,本文中我们将代码分为两部分:
- 一个部件就是检测视频帧的条形码
- 另一个则是访问视频源(摄像头或者视频文件)
然后使用条形码检测模块检测视频中的条形码。
我们的方法有如下假设:
- 第一个假设就是我们摄像头的向下看的静态视角为90度。
- 第二个假设是我们会拉近看图像中的条形码,且图像中没有其他干扰物或噪声。
事实上,能否满足这些假设全取决于你的实际应用场景。
至少,我想通过这篇博文给你说明的是一些基本的图像处理技术,以及如何采用Python和OpenCV,实现这些图像处理技术来构成一个简单的条形码检测器。
转载自演道,想查看更及时的互联网产品技术热点文章请点击http://go2live.cn