用OpenCV和Python识别二维码与条形码

Posted 景略集智

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用OpenCV和Python识别二维码与条形码相关的知识,希望对你有一定的参考价值。

用OpenCV和Python识别二维码与条形码

最近有朋友问我(作者Adrian Rosebrock——译者注)OpenCV里有没有什么模块能直接识别条形码和二维码,很遗憾,答案是没有。但是OpenCV能够加快读取条形码和二维码的过程,包括从硬盘加载图像,从视频流中抓取新的帧,并进行处理。 等我们获取图像或视频帧后,就可以将其传入Python中专用的条形码解码库,比如Zbar。 然后Zbar会对条形码或二维码进行解码。OpenCV可以接着执行进一步的图像处理工作以及展示结果。 听起来有些复杂,其实整个处理过程相当简单明了。程序库Zbar也衍生了很多变体,其中pyzbar是我的最爱。

在本文,我会教你怎样用OpenCV和Zbar读取条形码和二维码。而且,我还会展示怎样将我们制作的这个条形码&二维码扫描仪部署到树莓派上!!

使用OpenCV和ZBar打造一款条形码及二维码扫描仪

本文主要分为四部分。

  • 在第一部分,我会教你如何安装Zbar库(Python绑定)。

  • Zbar库会连同OpenCV一起用于扫描条形码和二维码。

  • 等正确配置好Zbar和OpenCV以后,我会展示如何用它们扫描一张图像上的条形码和二维码。

  • 先识别一张图像上的条形码和二维码练练手后,我们就进入下一阶段:用OpenCV和Zbar实时读取二维码和条形码。

  • 最后,我会展示如何将制作好的实时二维码&条形码扫描仪部署到树莓派上。

安装Zbar(带Python绑定)用于解码条形码&二维码

前段时间Staya Mallick在LearnOpenCV博客上发表了一篇实用教程,讲解如何用Zbar扫描条形码。

本文关于Zbar安装部分基本上是根据这篇博文的指导,但是做了一点改进,主要是围绕安装Python Zbar绑定部分,目的是确保我们能:

使用Python3(官方Zbar Python绑定只支持Python 2.7) 准确地检测和定位图像中二维码及条形码

安装所需的软件,只需简单三步。

第一步:从apt或brew库中安装Zbar

在Ubuntu或树莓派上安装Zbar

1$ sudo apt-get install libzbar0

在MacOS系统中安装Zbar

使用brew在macOS系统中安装Zbar也很容易(假定你已经安装了Homebrew):

1$ brew install zbar

第二步:创建一个虚拟环境,安装OpenCV。

这里你有俩个选择: 使用现成的已经安装好了OpenCV的虚拟环境(跳过这一步,看第三步)。 或者创建一个新的独立的虚拟环境,安装OpenCV。

虚拟环境对于Python开发来说是非常实用的做法,我非常鼓励使用虚拟环境。

我选择创建一个新的独立的Python 3 虚拟环境,然后安装了OpenCV,并将环境命名为barcode:

1$ mkvirtualenv barcode -p python3

注:如果你已经安装好了OpenCV,就可以跳过OpenCV编译过程,只需将你的cv2.so绑定符号链接(sym-link)入你的新Python虚拟环境中的site-pakages目录。

第三步:安装Pyzbar 现在我已经安装了Python 3 虚拟环境,命名为barcode,然后激活了barcode环境,安装pyzbar:

1$ workon barcode
2$ pip install pyzbar

如你不是用的Python 虚拟环境,只需:

1$ pip install pyzbar

如果想将pyzbar安装到Python版系统中,确保你也使用sudo命令。

用OpenCV解码单张图像上的条形码和二维码

在我们实现能实时读取条形码和二维码之前,我们首先创建一个单张图像扫描仪练练手。

打开一个新文件,命名为barcode_scanner_image.py,插入如下代码:

 1# 导入所需工具包
2from pyzbar import pyzbar
3import argparse
4import cv2
5
6# 构建参数解析器并解析参数
7ap = argparse.ArgumentParser()
8ap.add_argument("-i""--image", required=True,
9 help="path to input image")
10args = vars(ap.parse_args())

在第2-4行代码,我们导入了所需的工具包。

需要按照上一部分的指令安装pyzbar和cv2(OpenCV)。不过,在Python安装中包含了argparse,负责解析命令行参数。

对于该脚本我们有一个必需的命令行参数(--image),在7-10行进行解析。

你会在这部分结尾处看到在传入包含输入图像路径的命令行参数时,如何运行这里的脚本。

现在,我们获取输入图像,运行pyzbar:

1# 加载输入图像
2image = cv2.imread(args["image"])
3
4# 找到图像中的条形码并进行解码
5barcodes = pyzbar.decode(image)

在第13行,我们通过图像的路径(包含在我们很方便的args目录中)加载图像。

从这里,我们调取pyzbar.decode来发现和解码图像中的条形码(第16行)。

我们还没完成——现在我们需要解析包含在barcode变量中的信息:

 1# 循环检测到的条形码
2for barcode in barcodes:
3 # 提取条形码的边界框的位置
4 # 画出图像中条形码的边界框
5 (x, y, w, h) = barcode.rect
6 cv2.rectangle(image, (x, y), (x + w, y + h), (00255), 2)
7
8 # 条形码数据为字节对象,所以如果我们想在输出图像上
9 # 画出来,就需要先将它转换成字符串
10 barcodeData = barcode.data.decode("utf-8")
11 barcodeType = barcode.type
12
13 # 绘出图像上条形码的数据和条形码类型
14 text = "{} ({})".format(barcodeData, barcodeType)
15 cv2.putText(image, text, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX,
16  0.5, (00255), 2)
17
18 # 向终端打印条形码数据和条形码类型
19 print("[INFO] Found {} barcode: {}".format(barcodeType, barcodeData))
20
21# 展示输出图像
22cv2.imshow("Image", image)
23cv2.waitKey(0)

从19行开始,我们循环检测到的barcodes。 在这里的循环中,我们继续:

从barcode.rect对象(22行)提取边界框(x,y)坐标,这样能让我们定位和确定当前条形码在输入图像的位置。

围绕着检测到的barcode(第23行),在图像上画出边界框。

将barcode解码为“utf-8”字符串,提取barcode的类型(第27行和28行)。调取.decode(“utf-8”)函数将对象从字节数组转换为字符串,非常关键。你可以通过删除或添加注释,试验一下结果。

在图像上格式化和绘制barcodeData和barcodeType(第31-33行)。

最后,输出同样的数据,朝终端输入信息以进行调试(第36行)。

我们测试一下搭建的OpenCV条形码扫描仪。 从这里,打开你的终端,执行如下命令:

1$ python barcode_scanner_image.py --image barcode_example.png
2[INFO] Found QRCODE barcode: {"author""Adrian""site""PyImageSearch"}
3[INFO] Found QRCODE barcode: https://www.pyimagesearch.com/
4[INFO] Found QRCODE barcode: PyImageSearch
5[INFO] Found CODE128 barcode: AdrianRosebrock

可以在终端中看到,全部4个条形码均被正确的发现和解码!

用OpenCV和Python识别二维码与条形码

如图所示,识别出了图像中的条形码和二维码,以红框标出,并显示出了它们包含的信息。

用OpenCV实时读取条形码和二维码

在前面部分中,我们学习了如何为单张图像创建一个Python+OpenCV条形码扫描仪。

我们的条形码和二维码扫描仪效果很好——但是问题来了,我们能实时检测和解码条形码+二维码吗?

我们试试看。打开一个新文件,命名为barcode_scanner_video.py,插入如下代码:

 1# 导入所需工具包
2from imutils.video import VideoStream
3from pyzbar import pyzbar
4import argparse
5import datetime
6import imutils
7import time
8import cv2
9
10# 创建参数解析器,解析参数
11ap = argparse.ArgumentParser()
12ap.add_argument("-o""--output", type=str, default="barcodes.csv",
13 help="path to output CSV file containing barcodes")
14args = vars(ap.parse_args())

在第2-8行,我们导入了所需的工具包。 这里回想一下上面的解释,你应该识别pyzbar,argparse和cv2. 我们会使用VideoStream以高效和单线程的方式处理获取的视频帧。如果你的系统中没有安装imutils,只需使用如下命令:

1$ pip install imutils

我们接着解析一个可选的命令行参数--output,其包含了指向输出结果CSV文件的路径。该文件会包含从视频流中检测到和解析出的条形码的时间戳及载荷。如果该参数没有指定,那么CSV文件就会被我们当前名为“barcodes.csv”的工作目录所替换(第11-14行)。

在这里,我们初始化视频流,打开CSV文件:

1# 初始化视频流,让摄像头热热身
2print("[INFO] starting video stream...")
3# vs = VideoStream(src=0).start()
4vs = VideoStream(usePiCamera=True).start()
5time.sleep(2.0)
6
7# 打开输出CSV文件,用来写入和初始化迄今发现的所有条形码
8csv = open(args["output"], "w")
9found = set()

在第18-19行,我们初始化和启动了视频流,你可以: 使用自己的USB网络摄像头(无注释行第18行及注释行第19行) 或者如果你是使用树莓派的话(和我一样),可以用PiCamera(无注释行第19行及注释行第18行)。

我选择使用我的树莓派 PiCamera,下部分会说到。

然后我们等上几秒钟,让摄像头热热身(第20行)。

我们会将发现的所有条形码和二维码以CSV文件写入硬盘(确保不要写重复)。这里只是一种记录条形码的例子,当然你可以按照自己的喜好来,比如检测到条形码后,将它们读取为:

  • 将其保存在SQL数据库中

  • 将其发送到服务器

  • 将其上传至云端

  • 发送邮件或文本信息

实际操作随意,我们只是用CSV文件作为示例。

我们在第24行代码打开CSV文件以写入硬盘。如果你修改了代码以添加到文件,你可以只需将第二个参数从“w”改为“a”(但是你后面只能换种方式搜索重复文件)。

我们也初始化一个set用于found条形码。这个set会包含独一无二的条形码,防止出现重复。

我们开始获取和处理视频帧:

1# 循环来自视频流的帧
2while True:
3 # 抓取来自单线程视频流的帧, 
4 # 将大小重新调整为最大宽度400像素
5 frame = vs.read()
6 frame = imutils.resize(frame, width=400)
7
8 # 找到视频中的条形码,并解析所有条形码
9 barcodes = pyzbar.decode(frame)

在第28行,我们开始循环,继续抓取来自视频流中的frame,并调整大小(第31和32行)。

在这里,我们调取pyzbar.decode以检测和解码frame中的全部条形码和二维码。

我们接着循环检测到的barcodes:

 1# 循环检测到的条形码
2 for barcode in barcodes:
3  # 提取条形码的边界框位置
4  # 绘出围绕图像上条形码的边界框
5  (x, y, w, h) = barcode.rect
6  cv2.rectangle(frame, (x, y), (x + w, y + h), (00255), 2)
7
8  # 条形码数据为字节对象,所以如果我们想把它画出来
9  # 需要先把它转换成字符串
10  barcodeData = barcode.data.decode("utf-8")
11  barcodeType = barcode.type
12
13  # 绘出图像上的条形码数据和类型
14  text = "{} ({})".format(barcodeData, barcodeType)
15  cv2.putText(frame, text, (x, y - 10),
16   cv2.FONT_HERSHEY_SIMPLEX, 0.5, (00255), 2)
17
18  # 如果条形码文本目前不在CSV文件中, write
19  # 就将时间戳+条形码 to disk and update the set
20  if barcodeData not in found:
21   csv.write("{},{}\n".format(datetime.datetime.now(),
22    barcodeData))
23   csv.flush()
24   found.add(barcodeData)

如果看看前面的循环部分,你会发现这里循环和之前的很像。

实际上,第38-52行和前面识别单张图像的脚本是一样的。这部分代码详情解释,参见单张图像条形码检测和扫描部分。

第56-60行代码比较新。在这些行代码中,我们是检查是否发现了独有的(此前没有发现)条形码(第56行)。

如果是这种情况,我们将时间戳和数据写成CSV文件(第57-59行)。此外还可以将barcodeData添加到found集,作为一种处理重复文件的简单方法。

在实时条形码扫描脚本的剩余代码行中,我们展示视频帧,检查是否按退出键,并进行清除:

 1# 展示输出帧
2 cv2.imshow("Barcode Scanner", frame)
3 key = cv2.waitKey(1) & 0xFF
4
5 # 如果按下”q”键就停止循环
6 if key == ord("q"):
7  break
8
9# 关闭输出CSV文件进行清除
10print("[INFO] cleaning up...")
11csv.close()
12cv2.destroyAllWindows()
13vs.stop()

在第63行,我们展示输出帧。

然后在第64-68行,我们检查是否按了“q”,执行主循环。

最后,我们在第72-74行执行清除。

在树莓派上部署条形码和二维码扫描仪

我决定使用树莓派、触摸屏和一个充电宝打造一款自己的实时条形码扫描仪。

下图显示的是我的组装成果。如果你也想自己做一个,以下是需要的部件:

用OpenCV和Python识别二维码与条形码

  • 树莓派3(你也可以用最新的 3 B+)

  • 树莓派摄像头模块

  • Pi Foundation 7英寸触摸屏

  • RAVPower 22000mAh充电宝

很容易就能组装好。

在这里,打开你树莓派上的终端,用如下命令启动应用(这一步需要一个键盘/鼠标,但是后面就用不着了):

1$ python barcode_scanner_video.py
2[INFO] starting video stream...

等一切准备就绪后,就可以将条形码展示给摄像头了,可以打开barcode.csv文件(或者如果你愿意,也可以在另一个终端上执行tail -f barcodes.csv,查看打开CSV文件时的数据)。

我首先向摄像头展示了一个黑色背景上的二维码,Zbar很轻松的检测到了它:

用OpenCV和Python识别二维码与条形码

然后又在我家厨房里用这套装置发现了另一个二维码:

用OpenCV和Python识别二维码与条形码

现在,我们试试一个包含了json-blob数据的二维码:

用OpenCV和Python识别二维码与条形码

最后,我试了试传统的1维条形码:

1维的条形码对于我们这套系统来说略微难些,因为摄像头不支持自动对焦。但是最后还是成功的检测和解码了条形码。

如果你用有自动对焦功能的USB网络摄像头的话,效果要好得多。

结语

在本文,我们讨论了怎样用OpenCV和Python库Zbar打造一款条形码和二维码扫描仪。

将Zbar和OpenCV安装后,我们创建了两个Python脚本:

  • 第一个用于扫描单张图像上的条形码和二维码。

  • 第二个用于实时读取条形码和二维码的信息。

在这两种情况中,我们都使用了OpenCV来加快进程。

最终,我们将创建好的程序部署到树莓派上,并且能成功实时识别条形码和二维码。

可以自己试着去做一条这样的条形码&二维码扫描仪,项目源代码下载请查看集智专栏原帖。



限时折扣中:0806期《人工智能-从零开始到精通》最低58折!↓↓(或点击阅读原文,前25位同学可以领取¥200的优惠喔)


以上是关于用OpenCV和Python识别二维码与条形码的主要内容,如果未能解决你的问题,请参考以下文章

在Ubantu系统中使用Python+OpenCV识别条形码和二维码

OpenCV和Zbar两个Python模块实现二维码和条形码识别

二维码解码器Zbar+VS2010开发环境配置(使用opencv库)

OPENCV条形码检测与识别

python怎么识别条形码

Python zxing 库解析(条形码二维码识别)