mylogo

退役程序员的茶室 RetiredCoder.cn

← 返回上一页

巧用Python:用摄像头“扫码”

2025-05-10 14:57:16

这一讲我们来看看如何实现用摄像头“扫码”。

最初设想

在制作完生成二维码和条形码的课程之后,我的终极目标是希望为我家越积越多的书籍们做一个图书管理系统,而人工录入的工作实在太繁琐。因此我的设想是就像超市结账时的扫码那样,用摄像头将书籍后面的条形码统统扫一遍,记录下这些书籍的ISBN码。

然而!很傻很天真的我在实践这“宏图伟业”时遇到了麻烦!

摄像头 ≠ 扫码枪

通过平时观察,不难推测扫码枪是有“视力”的,但我忽略了它看东西的远近这一要素,还有它偷偷背地里打光这一事实,回想超市里人家扫码枪都是贴近货物的条形码在扫,还有红光闪现,而连接摄像头后,才意识到我这款摄像头是无法贴近物体扫码的,因为一靠近它的“视线”就模糊了,还有离远一些图像清晰,但采集细密的条形码影像效果仍然不好,直接影响了对实体书扫码的成功率。

在视频教程中,我直接扫描的是在电脑上打开的条形码图片,这种方式摄像头是可以正常扫码的,而对实体书条形码的扫码操作,我将会改为批量扫描包含条形码图片的方式,在之后的视频教程和公众号上,我会陆续公布相应的课程。

感慨万千之后,开始进入正题,来讲讲这节课遇到的知识点。

扫码所需要的库 pyzbar

无论是通过摄像头对采集到的影像实时来进行扫码,还是对已有的图片进行扫码,都需要pyzbar这个库来进行解码。

安装这个库时Windows环境只需:

pip install pyzbar

macOS环境需要用brew来安装:

brew install zbar

如果使用brew命令,不要忘记先运行brew upgrade和brew update,否则安装会遇到问题。

程序开头需要引入程序中会用到的decode方法

from pyzbar.pyzbar import decode

引入OpenCV库cv2

因为我们要用OpenCV的模块来捕获摄像头拍到的信息,所以程序开头要引入cv2这个库:

import cv2

如何安装OpenCV,请参看我的公众号里“OpenCV在macOS上的安装”这篇文章。

用来“捕获”图像的类VideoCapture

为了开启摄像头,我们要用到cv2里面的VideoCapture类

myVC = cv2.VideoCapture(0)
myVC.set(3,160)
myVC.set(4,120)

通过调用对象myVC的set方法,可以改变这个对象中的属性值,这里常量3代表采集图像的宽,4代表高。

当我们接触到一个陌生的类时,要了解它的构造和里面提供的方法、属性时,首要想到的应该是查询这个类的API文档,例如我们可能对OpenCV里面的类和方法并不了解,但可以去查询它的文档来学习:

https://docs.opencv.org/4.5.3/

获得连续影像

通过While无限循环,来不停抓取图像,并通过imshow方法显示在打开窗口中,这里用到了类VideoCapture对象myVC的read()方法,它返回两个值(能返回那么多值,Python真牛),第一个值如果是False,说明它没抓取到图像(我是怎么知道的,也是看文档),第二个值返回的就是抓取到的对象。

while True:
    check, frame = myVC.read()    
    cv2.imshow('My Camera', frame)    
    for barcode in decode(frame):        
        print(barcode.data.decode('utf-8'))    
    if(cv2.waitKey(1)==ord('q')):        
        break

图像中可能不只有一个条形码或二维码,decode方法返回的是一个包含这些数据的列表,因此这里用了for循环来遍历列表中的每一项,这里的每一项,都是一个已命名的元组,要取某一项的具体值,只需在点“.”操作符后面跟具体的参数名称,这里我需要的ISBN值放在了参数data这里,由于它保存的是个字节类型的数据,还需要进一步调用字节对象的decode方法,将它的编码设为“utf-8”。

在无限循环当中,万万不能忘记的是一个可达的退出条件,这里设定的是如果按了q键就跳出循环。

释放占用的摄像头资源

在编程世界里也是要讲规矩的,用完了,就别占着了,所以不要忘记退出前释放摄像头资源并关闭窗口。

myVC.release()
cv2.destroyAllWindows()

本节课完整代码

import cv2
from pyzbar.pyzbar import decode
myVC = cv2.VideoCapture(0)
myVC.set(3,160)
myVC.set(4,120)
while True:    
    check, frame = myVC.read()    
    cv2.imshow('My Camera', frame)    
    for barcode in decode(frame):        
        print(barcode.data.decode('utf-8'))    
    if(cv2.waitKey(1)==ord('q')):        
        break
myVC.release()
cv2.destroyAllWindows()