mylogo

退役程序员的茶室 RetiredCoder.cn

← 返回上一页

巧用Python:批量“扫码”

2025-05-26 04:08:31

这一讲我们来看看如何通过Python程序进行批量“扫码”。

改变方案

尝试用摄像头“扫码”时遇到挫折后,我决定采用扫描图片文件的方式获得书籍的条形码,之前的准备工作就是将每本书的ISBN码拍照,这时我才注意到有些上了年纪的书根本没有ISBN码,这样的书将来只能人工录入了。

获得文件夹中的所有文件名

批量扫码时,首先要先将这些包含条形码信息的图片存在同一个文件夹中,方便起见,在实现时我将这个文件夹BarImages放在了和python源码同级的目录。

在对文件系统进行操作时,我们一般会用到os库,所以要先引入:

import os

在程序中,对文件夹中的文件进行处理之前,要先判断这个文件夹是否存在,并且还要判断文件夹中是否有文件。

if not os.path.exists('BarImages'):    
    print('There are no image folder')
else:    
    imageslist = os.listdir('BarImages')    
    if len(imageslist)>0:        
        print(str(len(imageslist)))    
    else:        
        print('There are no images')

这里首先判断BarImages是否存在,如果不存在,打印提示,如果存在,则调用os里的listdir方法,得到对应文件夹里的文件名称列表imageslist。需要注意一点的是,如果文件夹中包含了非图片格式的文件时,可能会报错,我在macOS系统上在文件夹里做了删除文件的操作后会出现一个隐藏文件,如果遇到这种情况,可以用条件语句判断一下来跳过这种特殊情况。

在视频教程中,当判断imageslist的长度大于0时就可以开始遍历文件夹中的每一项来进行解码了:

if not os.path.exists('BarImages'):    
    print('There are no image folder')
else:    
    imageslist = os.listdir('BarImages')    
    if len(imageslist)>0:        
        print(str(len(imageslist)))        
        for image in imageslist:            
            readData = decode(Image.open(f'BarImages/{image}'))            
            #print(str(readData))            
            flag = False            
            for idata in readData:                
                if len(idata.data.decode('utf-8'))==13:                    
                    flag = True                    
                    isbn = idata.data.decode('utf-8')                    
                    if isbn not in scannedISBNList:  
                        scannedISBNList.append(isbn)                    
                    else:                        
                        print(f'The ISBN {isbn} is existed!')            
            if not flag:                
                print(f'Failed:{image}')
                failedlist.append(image)        
        print(f'Scanned ISBN:{str(scannedISBNList)} \
        length:{len(scannedISBNList)}')        
        print(f'Failed Images:{str(failedlist)} \
        length:{len(failedlist)}') 
    else:        
        print('There are no images')

这里外部的for循环负责遍历图片名称列表imageslist中的每一项,组合出图片所在路径后打开对应文件,并解码得出readData值。

由于readData也是一个列表,所以还要遍历这个列表中的每一项idata,这个临时变量保存的是一个已命名tuple类型的数据,包含着data、type、rect、 polygon属性,这里我们只需要取data属性的值,用点“.”操作符来获取,由于取到的值是字节型数据,还需要对它解码为utf-8编码类型的字符串。

取得代表ISBN的字符串之后,就可以将其加入到存放已经获得的ISBN列表scannedISBNList之中。这里用了一个布尔类型的标记来记录是否成功取到了ISBN值,在一幅图扫码结束后来通过这个标记的值来判断,如果没有成功取到,将该图片的名称加入存放失败图片的列表failedlist。

存储数据至json文件

得到了包含有ISBN的列表scannedISBNList之后,如果不将其保存至文件,程序结束后这些数据也将灰飞烟灭,因此这里用到了json库来协助将这些数据保存至json文件:

import json

最初的程序,在开始时scannedISBNList是空的列表,假设我们已经有了存放已有ISBN的json文件ScannedISBNList.json,我们在程序开头时就要将这个文件里的内容变成Python程序中的数据格式,传递给scannedISBNList列表,以免同一本书被多次记录:

if os.path.exists('ScannedISBNList.json'):    
    with open('ScannedISBNList.json') as f:        
        scannedISBNList = json.loads(f.read())        
        print(f'The list from json file:{str(scannedISBNList)} \
        length:{str(len(scannedISBNList))}')

json库中的loads方法将从文件ScannedISBNList.json中读到的内容转为了Python程序可识别的数据格式。接下来会看到,保存scannedISBNList中的数据到json文件时,保存的是列表格式,所以在程序开头的这段接收json文件中数据的代码,也是用列表变量来接收的。

在文件夹中的文件都被遍历解码之后,就可以将含有ISBN码的列表导入到json文件中了:

with open('ScannedISBNList.json','w') as fout:    
    json.dump(scannedISBNList, fout) 

这里用json的dump方法,将scannedISBNList中的数据写入了文件对象fout对应的json文件ScannedISBNList.json。

**** 未来扩展

成功存储到这些ISBN码只是图书管理系统的第一步,这些ISBN码在将来可以作为搜索的关键字来从网上获得对应书籍的信息,进一步完善图书信息,批量获得这些信息可以考虑用爬虫抓取,或者找到一些网站提供的api接口来获得包含有图书详细信息的json数据,再从中挑取需要的信息。

本节课完整代码

from pyzbar.pyzbar import decode 
from PIL import Image 
import os 
import json 

imageslist = [] 
scannedISBNList = [] 
failedlist = [] 

if os.path.exists('ScannedISBNList.json'):    
    with open('ScannedISBNList.json') as f:        
        scannedISBNList = json.loads(f.read())        
        print(f'The list from json file:{str(scannedISBNList)} \
        length:{str(len(scannedISBNList))}')
        
if not os.path.exists('BarImages'):   
    print('There are no image folder')
else:    
    imageslist = os.listdir('BarImages')    
    if len(imageslist)>0:        
        print(str(len(imageslist)))
        for image in imageslist:            
            readData = decode(Image.open(f'BarImages/{image}'))            
            #print(str(readData))
            flag = False            
            for idata in readData:
                if len(idata.data.decode('utf-8'))==13:            
                    flag = True
                    isbn = idata.data.decode('utf-8')
                    if isbn not in scannedISBNList:               
                        scannedISBNList.append(isbn)
                    else:                     
                        print(f'The ISBN {isbn} is existed!')            
            if not flag:                
                print(f'Failed:{image}')                
                failedlist.append(image)        
        print(f'Scanned ISBN:{str(scannedISBNList)} \
        length:{len(scannedISBNList)}')        
        print(f'Failed Images:{str(failedlist)} \
        length:{len(failedlist)}')    
    else:        
        print('There are no images')
                    
with open('ScannedISBNList.json','w') as fout:    
    json.dump(scannedISBNList, fout) 
                        
#readData = decode(Image.open('bar8.png'))
#print(readData[0].data.decode('utf-8'))