集智专栏
资源加载中,请稍后...
集智专栏

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

集智小编

参考资料:www.pyimagesearch.com


导读:计算机视觉专家Adrian Rosebrock近日分享了如何借助OpenCV和Zbar,编写出能够实时识别二维码和条形码的扫描程序,最后部署在树莓派上,成功制作一款实用的条形码&二维码扫描设备。

最近有朋友问我(作者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

$ sudo apt-get install libzbar0

在MacOS系统中安装Zbar

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

$ brew install zbar

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

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

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

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

$ mkvirtualenv barcode -p python3

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

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

$ workon barcode
$ pip install pyzbar

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

$ pip install pyzbar

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

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

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

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

# 导入所需工具包
from pyzbar import pyzbar
import argparse
import cv2

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

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

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

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

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

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

# 加载输入图像
image = cv2.imread(args["image"])

# 找到图像中的条形码并进行解码
barcodes = pyzbar.decode(image)

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

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

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

# 循环检测到的条形码
for barcode in barcodes:
 # 提取条形码的边界框的位置
 # 画出图像中条形码的边界框
 (x, y, w, h) = barcode.rect
 cv2.rectangle(image, (x, y), (x + w, y + h), (0, 0, 255), 2)

 # 条形码数据为字节对象,所以如果我们想在输出图像上
 # 画出来,就需要先将它转换成字符串
 barcodeData = barcode.data.decode("utf-8")
 barcodeType = barcode.type

 # 绘出图像上条形码的数据和条形码类型
 text = "{} ({})".format(barcodeData, barcodeType)
 cv2.putText(image, text, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX,
  0.5, (0, 0, 255), 2)

 # 向终端打印条形码数据和条形码类型
 print("[INFO] Found {} barcode: {}".format(barcodeType, barcodeData))

# 展示输出图像
cv2.imshow("Image", image)
cv2.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条形码扫描仪。 从这里,打开你的终端,执行如下命令:

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

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

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

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

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

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

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

# 导入所需工具包
from imutils.video import VideoStream
from pyzbar import pyzbar
import argparse
import datetime
import imutils
import time
import cv2

# 创建参数解析器,解析参数
ap = argparse.ArgumentParser()
ap.add_argument("-o", "--output", type=str, default="barcodes.csv",
 help="path to output CSV file containing barcodes")
args = vars(ap.parse_args())

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

$ pip install imutils

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

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

# 初始化视频流,让摄像头热热身
print("[INFO] starting video stream...")
# vs = VideoStream(src=0).start()
vs = VideoStream(usePiCamera=True).start()
time.sleep(2.0)

# 打开输出CSV文件,用来写入和初始化迄今发现的所有条形码
csv = open(args["output"], "w")
found = set()

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

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

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

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

  • 将其保存在SQL数据库中
  • 将其发送到服务器
  • 将其上传至云端
  • 发送邮件或文本信息

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

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

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

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

# 循环来自视频流的帧
while True:
 # 抓取来自单线程视频流的帧, 
 # 将大小重新调整为最大宽度400像素
 frame = vs.read()
 frame = imutils.resize(frame, width=400)

 # 找到视频中的条形码,并解析所有条形码
 barcodes = pyzbar.decode(frame)

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

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

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

 # 循环检测到的条形码
 for barcode in barcodes:
  # 提取条形码的边界框位置
  # 绘出围绕图像上条形码的边界框
  (x, y, w, h) = barcode.rect
  cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 2)

  # 条形码数据为字节对象,所以如果我们想把它画出来
  # 需要先把它转换成字符串
  barcodeData = barcode.data.decode("utf-8")
  barcodeType = barcode.type

  # 绘出图像上的条形码数据和类型
  text = "{} ({})".format(barcodeData, barcodeType)
  cv2.putText(frame, text, (x, y - 10),
   cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)

  # 如果条形码文本目前不在CSV文件中, write
  # 就将时间戳+条形码 to disk and update the set
  if barcodeData not in found:
   csv.write("{},{}\n".format(datetime.datetime.now(),
    barcodeData))
   csv.flush()
   found.add(barcodeData)

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

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

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

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

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

 # 展示输出帧
 cv2.imshow("Barcode Scanner", frame)
 key = cv2.waitKey(1) & 0xFF

 # 如果按下”q”键就停止循环
 if key == ord("q"):
  break

# 关闭输出CSV文件进行清除
print("[INFO] cleaning up...")
csv.close()
cv2.destroyAllWindows()
vs.stop()

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

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

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

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

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

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

  • 树莓派3(你也可以用最新的 3 B+)
  • 树莓派摄像头模块
  • Pi Foundation 7英寸触摸屏
  • RAVPower 22000mAh充电宝

很容易就能组装好。

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

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

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

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

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

成功了!而且能多角度扫描识别二维码。

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

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

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

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

结语

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

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

  • 第一个用于扫描单张图像上的条形码和二维码。
  • 第二个用于实时读取条形码和二维码的信息。

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

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

可以自己试着去做一条这样的条形码&二维码扫描仪,项目源代码下载


限时折扣中:0806期《人工智能-从零开始到精通》(前25位报名同学可领取¥200优惠券)

您也许喜欢这些文章

集智专栏

Patchouli的机器学习系列教程一:概率论初步与其他基础数学知识——『器』

发表至系列教程
将第一章中所学的数学知识运用到机器学习实践中去,学以致用,文武兼备。
集智专栏

[2018.04.03直播] Python之文件

发表至系列教程
2018年4月1日直播配套专栏,讲解如何利用Python对本地文件进行读写等操作,并初步地完成一个数据科学实例。
集智专栏

[Pandas] 表格整合三大神技之CONCATENATE

发表至数据科学
Pandas相比于传统的经济学计算工具(Stata、SAS、R、MATLAB等)的优点有很多: 1. 相比于R等统计软件,Pandas借鉴了R的数据结构,因此拥有了R的很多方便的数据操作特性;在语法设计上,Pandas相比于R和Stata更严谨并且更简洁易用;基于Python的自动管理内存的能力,以及在很多细节上的优化,Pandas拥有了更好的管理和计算大数据的能力。 2. Pandas的底层基于Numpy搭建,因此Pandas拥有了Numpy的全部优点。Numpy原生的C接口,也给扩展Pandas的计算性能带来了很大的方便。

文章评论(0)