一、场景需求简介

  在现实场景中,我们经常会遇到条形码,它的主要作用是用来标识物体,使得每一个物体都有唯一的一个编号,这样可以极大的提高查找效率等,比较常用的应用场景是超市和图书馆等,售货员通过扫描这些条形码就可以很快的知道这个商品的价格,极大的提高了用户的购物体验。具体的示例如下所示:
在这里插入图片描述

二、算法实现步骤

  • 步骤1-读取图片并将其转化为灰度图片,便于后面的处理;
  • 步骤2-计算图像中x和y方向的Scharr梯度幅值,并利用x方向的梯度值减去y方向的梯度值,对结果取绝对值操作;
  • 步骤3-执行高斯模糊并将图片转化为二值图片,这样可以抑制掉大量的噪声,初略检测出条形码所在的区域;
  • 步骤4-构建一个掩码并将其应用在二值图片中,这样可以使得二维码区域更加完整;
  • 步骤5-执行多次膨胀和腐蚀操作,这样可以去除一些空洞和噪声;
  • 步骤6-检测出图片中的最大轮廓,这个轮廓其实就是我们的目标;
  • 步骤7-计算该轮廓的最小外界矩形作为最终的输出结果,由于该轮廓是一个不规则图像,因而需要执行该操作。

三、图片中条形码检测代码实现

# coding=utf-8
# 导入python包
import numpy as np
import argparse
import imutils
import cv2

# 构建并解析参数
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required = True, help = "path to the image file")
args = vars(ap.parse_args())

# 读取图片并将其转化为灰度图片
image = cv2.imread(args["image"])
image1 = image.copy()
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方向的梯度
gradient = cv2.subtract(gradX, gradY)
# 获取处理后的绝对值
gradient = cv2.convertScaleAbs(gradient)
cv2.imwrite("gradient.png", gradient)

# 对处理后的结果进行模糊操作
blurred = cv2.blur(gradient, (9, 9))
# 将其转化为二值图片
(_, thresh) = cv2.threshold(blurred, 225, 255, cv2.THRESH_BINARY)
cv2.imwrite("thresh.png", thresh)

# 构建一个掩码并将其应用在二值图片中
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (21, 7))
closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
cv2.imwrite("closed1.png", closed)

# 执行多次膨胀和腐蚀操作
closed = cv2.erode(closed, None, iterations = 4)
closed = cv2.dilate(closed, None, iterations = 4)
cv2.imwrite("closed2.png", closed)

# 在二值图像中寻找轮廓, 然后根据他们的区域大小对该轮廓进行排序,保留最大的一个轮廓
cnts = cv2.findContours(closed.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
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)

# 绘制并显示结果
cv2.drawContours(image1, [box], -1, (0, 255, 0), 3)
result = np.hstack([image, image1])
cv2.imwrite("detect1.png", result)
cv2.imshow("Image", result)
cv2.waitKey(0)

四、图片中条形码检测效果展示与分析

在这里插入图片描述
  上图展示了该算法在图片中的检测结果和中间结果。第1行第1列表示原始的输入图片,对应到代码中的image,该图片中含有一个条形码;第1行第2列表示梯度差的绝对值的效果,对应于代码中的gradient,通过这个操作,我们可以发现含有条形码的区域比较明显;第2行第1列表示的是二值化后的结果,对应于代码中的thresh,通过二值化操作可以去除大量的噪声,将二维码的大致位置检测出来;第2行第2列表示的是应用掩模之后的效果图,对应于代码中的closed,我们可以发现应用了掩模之后的结果中调补了条形码中的大部分空洞;第3行第1列表示的是经过膨胀和腐蚀操作之后的结果,对应于代码中的closed,我们可以发现多次膨胀和腐蚀操作可以去除掉大量的空洞和噪声;第3行第2列表示算法最终的检测结果,对应于代码中的image1,我们可以发现该算法准确的检测出图片中的条形码。

五、视频中条形码检测代码实现

# coding=utf-8
# 导入python包
from imutils.video import VideoStream
import numpy as np
import imutils
import argparse
import time
import cv2

# 定义简单的图片检测函数,这个思路就是单张图片的检测思路
def detect(image):
	gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

	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)

	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)

	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

# 构建并解析参数
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:
	# 获取当前帧
	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()

六、视频中条形码检测效果展示与分析

在这里插入图片描述
  上图中展示了该算法在视频上面的检测结果,该算法在图片中的条形码检测的基础上,增加了一些视频控制的代码,整个代码默认会读取视频,当用户没有设置读取的视频时,该代码会调用电脑上面的摄像头进行视频捕获,并实时的检测当前帧中是否包含条形码,如果包含则会显示该条形码,否则不显示任何东西,整个捕获过程会一直执行下去,直到用户按下键盘上面的q键为止。通过观察上图我们可以发现该代码可以实时、准确的检测到视频中包含的二维码。

七、思维扩展

  本文仅仅实现了一个简单的图像条形码检测算法,如果你想实现一个更强大的条形码检测算法,你需要考虑到图像的方向,尝试着去应用机器学习技术,如haar级联或hog+linear svm来“扫描”图像的条形码区域。
  本文实现的基于视频的条形码检测算法是在两个假设的基础上设计的。第一个假设是我们有一个静态摄像头,它以90度角“向下看”条形码。这将确保条码图像的梯度区域被我们设计的简单条码探测器探测到。第二个假设是,我们的视频有条形码的“特写”,这意味着我们将智能手机直接放在条形码的上方,而不是将条形码放在远离镜头的地方。我们把条形码移离相机越远,我们简单的条形码检测器就越不成功。如果你想要设计一个更好的条形码检测器,可以去参考博客1博客2的实现细节。

参考资料

[1] 参考链接

注意事项

[1] 如果您对AI、自动驾驶、AR、ChatGPT等技术感兴趣,欢迎关注我的微信公众号“AI产品汇”,有问题可以在公众号中私聊我!
[2] 该博客是本人原创博客,如果您对该博客感兴趣,想要转载该博客,请与我联系(qq邮箱:1575262785@qq.com),我会在第一时间回复大家,谢谢大家的关注.
[3] 由于个人能力有限,该博客可能存在很多的问题,希望大家能够提出改进意见。
[4] 如果您在阅读本博客时遇到不理解的地方,希望您可以联系我,我会及时的回复您,和您交流想法和意见,谢谢。
[5] 本文测试的图片可以通过关注公众号AI产品汇之后找我索取!
[6] 本人业余时间承接各种本科毕设设计和各种小项目,包括图像处理(数据挖掘、机器学习、深度学习等)、matlab仿真、python算法及仿真等,有需要的请加QQ:1575262785详聊!!!

GitHub 加速计划 / opencv31 / opencv
142
15
下载
OpenCV: 开源计算机视觉库
最近提交(Master分支:3 个月前 )
d9a139f9 Animated WebP Support #25608 related issues #24855 #22569 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake 1 天前
09030615 V4l default image size #25500 Added ability to set default image width and height for V4L capture. This is required for cameras that does not support 640x480 resolution because otherwise V4L capture cannot be opened and failed with "Pixel format of incoming image is unsupported by OpenCV" and then with "can't open camera by index" message. Because of the videoio architecture it is not possible to insert actions between CvCaptureCAM_V4L::CvCaptureCAM_V4L and CvCaptureCAM_V4L::open so the only way I found is to use environment variables to preselect the resolution. Related bug report is [#25499](https://github.com/opencv/opencv/issues/25499) Maybe (but not confirmed) this is also related to [#24551](https://github.com/opencv/opencv/issues/24551) This fix was made and verified in my local environment: capture board AVMATRIX VC42, Ubuntu 20, NVidia Jetson Orin. ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [X] I agree to contribute to the project under Apache 2 License. - [X] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [X] The PR is proposed to the proper branch - [X] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake 1 天前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐