使用Python,OpenCV在视频中进行实时条形码检测
上一篇博客介绍了如何检测和查找图像中的条形码。这篇博客将进行一些优化以检测实时视频中的条形码。
1. 步骤
- 图像/视频帧中进行条形码检测
- 驱动程序以访问视频流,并调用条形码检测程序
计算x梯度与y梯度的差值(由于是x减y的,故只能检测横向的条形码,算法并不通用)
2. 适用场景及优化
适用场景:
- 静态摄像头,以90度角“向下”看条形码。这将确保程序可以找到条形码图像的梯度区域。
- 视频带有条形码的正视角的特写,即智能手机直接放在条形码的上方,而不是将条形码远离镜头。将条形码移离相机的距离越远,简单条形码检测器的成功就越少。
该方法并不适用于所有的条形码检测,仅在最佳条件下有效,并且是横向条形码;
当条形码离相机太远,图像中有太多的“干扰”和“噪音”时,都不会奏效。
优化:
如果要实施更强大的条形码检测算法,则需要考虑图像的方向,或者更好的方法是应用机器学习技术(例如Haar级联或HOG +线性SVM)“扫描”图像以进行条形码区域扫描。
3. 总结
这篇博客拓展了上一篇检测图像中的条形码,主要分为俩部分:
- 用于检测视频各个帧中的条形码的方法;
- “驱动程序”,用于访问摄像机或视频文件的方法;
当将其应用于检测视频中的条形码时,并不通用。仅在:
- 有一个静态相机视图,它以90度角在条形码上“向下看”。
- 拥有较清晰的,近的条形码的“特写”视图,并且框架视图中没有其他干扰对象或噪声。
下效果比较好。
优化可以通过使用机器学习来训练更强壮的条形码检测器。
4. 源码
# USAGE
# python detect_real_barcode.py --video video/video_games.mov
# python detect_real_barcode.py
# 导入必要的包
import argparse
import time
import cv2
import imutils
import numpy as np
from imutils.video import VideoStream
# 检测图像中的条形码
def detect(image):
# 转换图像为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 计算图像x,y方向上的Scharr梯度幅度表示
ddepth = cv2.cv.CV_32F if imutils.is_cv2() else cv2.CV_32F
gradX = cv2.Sobel(gray, ddepth=ddepth, dx=1, dy=0, ksize=-1)
gradY = cv2.Sobel(gray, ddepth=ddepth, dx=0, dy=1, ksize=-1)
# 计算x梯度与y梯度的差值(由于是x减y的,故只能检测横向的条形码,算法并不通用),获取到条形码的大致范围
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)
cnts = imutils.grab_contours(cnts)
# 如果没有轮廓,返回None,认为图像中不存在条形码
if len(cnts) == 0:
return None
# 否则,根据外接圆面积排序,留下最大的轮廓认为是条形码区域
c = sorted(cnts, key=cv2.contourArea, reverse=True)[0]
rect = cv2.minAreaRect(c)
box = cv2.cv.BoxPoints(rect) if imutils.is_cv2() else cv2.boxPoints(rect)
box = np.int0(box)
# 返回条形码区域的边界框
return box
# 构建命令行参数及解析
# --video,视频文件的路径
ap = argparse.ArgumentParser()
ap.add_argument("-v", "--video", help="path to the (optional) video file")
args = vars(ap.parse_args())
# 如果提供了视频文件,则读取视频文件
if not args.get("video", False):
vs = VideoStream(src=0).start()
time.sleep(2.0)
# 否则,加载电脑自带的摄像头
else:
vs = cv2.VideoCapture(args["video"])
# 循环遍历帧
while True:
# 获取当前帧
# 如果是从视频中VideoStram获取则直接获取当前帧,否则,当前视频流VideoCapture中捕获下一帧
frame = vs.read()
frame = frame[1] if args.get("video", False) else frame
# 检测是否到了视频尾部
if frame is None:
break
# 检测视频帧的条形码
box = detect(frame)
# 如果检测到了条形码,在其上绘制绿色框
if box is not None:
cv2.drawContours(frame, [box], -1, (0, 255, 0), 2)
# 展示帧,并记录用户是否有按键
cv2.imshow("Frame", frame)
key = cv2.waitKey(1) & 0xFF
# 按下‘q’键,结束循环
if key == ord("q"):
break
# 清除视频流对象的指针
# 如果未使用视频文件,停止视频文件流
if not args.get("video", False):
vs.stop()
# 否则,释放摄像头指针
else:
vs.release()
# 关闭所有展示的窗口
cv2.destroyAllWindows()