使用Python,OpenCV在视频中进行实时条形码检测

使用Python,OpenCV在视频中进行实时条形码检测

上一篇博客介绍了如何检测和查找图像中的条形码。这篇博客将进行一些优化以检测实时视频中的条形码。

1. 步骤

  • 图像/视频帧中进行条形码检测
  • 驱动程序以访问视频流,并调用条形码检测程序

计算x梯度与y梯度的差值(由于是x减y的,故只能检测横向的条形码,算法并不通用)

2. 适用场景及优化

适用场景:

  1. 静态摄像头,以90度角“向下”看条形码。这将确保程序可以找到条形码图像的梯度区域。
  2. 视频带有条形码的正视角的特写,即智能手机直接放在条形码的上方,而不是将条形码远离镜头。将条形码移离相机的距离越远,简单条形码检测器的成功就越少。

该方法并不适用于所有的条形码检测,仅在最佳条件下有效,并且是横向条形码;
当条形码离相机太远,图像中有太多的“干扰”和“噪音”时,都不会奏效。

优化:
如果要实施更强大的条形码检测算法,则需要考虑图像的方向,或者更好的方法是应用机器学习技术(例如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()

参考

猜你喜欢

转载自blog.csdn.net/qq_40985985/article/details/110817080