[Python]在Windows系统中使用ZXing模块实现二维码、条形码读码

  需要实现Python读取图片中二维码、条形码信息。前段时间研究使用zbarlight模块,费了很大功夫安装调试好,但是发现有些图片读取不正确,而且如果图片中二维码倾斜,就读取不了,不能满足要求。昨天琢磨着试一试ZXing,下载ZXing模块安装后,却一直报错。打开模块源码仔细分析,原来该模块是通过调用java程序,使用ZXing的java库来实现的,通过分析命令行输出得到解码结果。忙活了一天多,各种测试、查资料,终于解决了问题。调试过程非常艰辛,现将做法整理如下(Windows 10系统):

 一、 因为需要调用java程序,必须安装jdk。

  • 下载java安装程序,根据提示一步步安装。

  下载地址:http://www.oracle.com/technetwork/java/javase/downloads/index.html

  • 设置环境变量。

  添加JAVA_HOME环境变量,其值设置为jdk安装目录。将java运行程序所在目录(%JAVA_HOME%\bin)添加到PATH。

  • 在命令行输入java -version并执行,如果没有问题则说明安装成功。

 二、下载ZXing的jar库文件,以及所需java库文件。

  • 下载ZXing Core和ZXing Java SE Extensions库文件。

  登录网址http://www.mvnrepository.com,在搜索栏中输入"zxing"并查找,就能找到这两个库,下载库文件并将其分别改名为core.jar和javase.jar。

  • 下载 JCommander库文件。

  在测试中,安装了上面两个库以后,仍然报错,错误信息中提示JCommander模块找不到,后来下载了这个库文件,经过多番调试,终于成功。登录网址http://www.mvnrepository.com,在搜索栏中输入"jcommander"并查找,就能找到这个库,下载库文件并将其改名为jcommander.jar。

  • 创建一个目录,并将以上三个jar文件复制到其中。

  这个目录可以创建在java安装目录下。我是在java目录下建了一个名为jar的文件夹,将上面三个文件复制到里面。

  • 设置环境变量。

  添加ZXING_JAR_PATH环境变量,将其值设为上面创建的那个文件夹的绝对路径,我的路径是“d:\java\jar”。

 三、在ZXing源代码基础上进行修改,实现二维码、条形码解码功能;增加了两个函数,解决了不支持图片Windows格式绝对路径的问题。

#导入模块
import subprocess, re, os

class BarCodeReader():
    """
    解码器类,调用java执行ZXing库命令实现解码,通过分析输出获得结果。
    此模块在ZXing源码基础上修改,改动较大。
    """

    #ZXing库路径
    location = ""
    #ZXing库
    libs = ["javase.jar", "core.jar", "jcommander.jar"]
    #subprocess.Popen参数
    args = ["java", "-cp", "LIBS", "com.google.zxing.client.j2se.CommandLineRunner"]

    def __init__(self, loc=None):
        """
        初始化函数,参数loc设置ZXing库所在位置
        """
        
        #如果loc为空或者不是字符串对象,读取环境变量ZXING_JAR_PATH的值(没找到就设为空字符串)
        if not loc or not isinstance(loc,str):
            loc = os.environ.get("ZXING_JAR_PATH", "")
        
        #如果loc不为空,在末尾加上'\',否则设为默认值(即在当前目录的zxing子目录下)
        if loc:
            loc = loc.rstrip("\\") + "\\"
        else:
            loc = "zxing\\"
    
        #设置location
        self.location = loc

    def decode(self, files, try_harder = False, qr_only = False):
        """
        解码方法,参数files可以是一个文件名(字符串),也可以是一个文件名列表。
        """
        
        #根据try_harder和qr_only参数设置,添加相应选项
        if try_harder:
            self.args.append("--try_harder")
        if qr_only:
            self.args.append("--possibleFormats=QR_CODE")

        #为ZXing库加上路径
        libraries = [self.location + lib for lib in self.libs]
        
        #生成命令列表
        cmd = [ c if c != "LIBS" else os.pathsep.join(libraries) for c in self.args ]
        
        #单文件标志设为False
        SINGLE_FILE = False
        
        #如果files不是列表,将文件files添加到cmd列表,单文件标志设为True
        if not isinstance(files, list):
            cmd.append(files)
            SINGLE_FILE = True
        #否则,将文件列表files添加到cmd列表
        else:
            cmd += files
        
        #利用subprocess.Popen函数执行命令
        (stdout, stderr) = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True).communicate()
        
        #初始化编码列表
        codes = []
        
        #分析输出内容
        file_results = stdout.split("\nfile:")
        for result in file_results:
            lines = result.split("\n")
            if re.search("No barcode found", lines[0]):
                codes.append(None)
            else:
                codes.append(BarCode(result))

        #如果是单文件,返回第一个BarCode对象;否则,返回BarCode对象列表
        if SINGLE_FILE:
            return codes[0]
        else:
            return codes

class BarCode:
    """
    BarCode信息类,包含解码所得各项信息。
    此模块基本使用了ZXing源码,为方便使用,添加了几个属性。
    """
    format = ""
    points = []
    data = ""
    raw = ""

    def __init__(self, zxing_output):
        lines = zxing_output.split("\n")
        raw_block = False
        parsed_block = False
        point_block = False

        self.points = []
        for l in lines:
            m = re.search("format:\s([^,]+)", l)
            if not raw_block and not parsed_block and not point_block and m:
                self.format = m.group(1)
                continue

            if not raw_block and not parsed_block and not point_block and l == "Raw result:":
                raw_block = True
                continue

            if raw_block and l != "Parsed result:":
                self.raw += l + "\n"
                continue

            if raw_block and l == "Parsed result:":
                raw_block = False
                parsed_block = True
                continue

            if parsed_block and not re.match("Found\s\d\sresult\spoints", l):
                self.data += l + "\n"
                continue

            if parsed_block and re.match("Found\s\d\sresult\spoints", l):
                parsed_block = False
                point_block = True
                continue

            if point_block:
                m = re.search("Point\s(\d+):\s\(([\d\.]+),([\d\.]+)\)", l)
                if (m):
                    self.points.append((float(m.group(2)), float(m.group(3))))
        
        #去除多余换行符
        self.raw = self.raw.rstrip("\n")
        self.data = self.data.rstrip("\n")

    @property
    def Format(self):
        """
        返回编码格式
        """
        return self.format
        
    @property
    def Data(self):
        """
        返回编码数据
        """
        return self.data
        
    @property
    def Points(self):
        """
        返回坐标点
        """
        return self.points
        
    @property
    def RawData(self):
        """
        返回解码所得原始数据
        """
        return self.raw

def GetCodeString(filename):
    """
    解码函数,只处理单个文件,参数为文件名(含路径)。
    返回编码字符串,如果未能成功解码或未找到条码,返回空字符串。
    """
    
    #将'\'替换成'/'
    filename = filename.replace("\\", "/")

    #如果带有盘符(绝对路径),前面加上'file:/'
    if re.match('^[A-Za-z]:', filename):
        filename = "file:/" + filename

    #创建解码器对象
    zxcode = BarCodeReader()

    #解码,并将结果保存到barcode
    barcode = zxcode.decode(filename)

    #如果结果为None,返回空串,否则返回编码字符串
    if barcode is None:
        return ""
    else:
        return barcode.Data

def GetCodeObject(filename):
    """
    解码函数,只处理单个文件,参数为文件名(含路径)。
    返回编码对象,其属性如下:
        Data,编码信息;
        Format,编码类别;
        Points,坐标点;
        RawData,未处理的编码信息。
    如果未能成功解码或未找到条码,返回None
    """
    
    #将'\'替换成'/'
    filename = filename.replace("\\", "/")

    #如果带有盘符(绝对路径),前面加上'file:/'
    if re.match('^[A-Za-z]:', filename):
        filename = "file:/" + filename

    #创建解码器对象
    zxcode = BarCodeReader()

    #解码,并将结果保存到barcode
    barcode = zxcode.decode(filename)

    #返回结果
    return barcode

 四、测试。将上述代码保存到zxing.py文件中,编辑测试代码如下:

from zxing import GetCodeString,GetCodeObject
result = GetCodeString("d:\\barcode.png")
print(result)
result = GetCodeObject("d:\\qr.png")
if result:
    print("Format:", result.Format)
    print("Data:  ", result.Data)
    print("Points:")
    for point in result.Points:
        print(point)

 五、可以将以上面的代码安装到Python库(注册为zxing模块)。

  • 下载python-zxing模块。下载地址为https://github.com/oostendo/python-zxing

  • 解压缩到python-zxing文件夹。

  • 将python-zxing\zxing文件夹内__init__.py文件的内容替换成第三步的代码。

  • 在python-zxing文件夹下打开命令行,运行python setup.py install即可。

  • 同样可以使用第四步的代码进行测试。

posted @ 2018-08-20 21:43  乐松  阅读(1615)  评论(0编辑  收藏  举报