Python, ZBarでバーコード・QRコードを検出・読み取り

Posted: | Tags: Python, 画像処理, Pillow, OpenCV

PythonとZBar(pyzbar)でバーコードとQRコードを検出し、格納された文字列を読み取る方法について説明する。

  • ZBarとpyzbar
  • 画像からバーコード・QRコードを検出・読み取り
    • 基本的な使い方
    • 検出するタイプを指定
  • Pillowとpyzbarの組み合わせ
  • OpenCVとpyzbarの組み合わせ
  • カメラの動画からバーコード・QRコードを検出・読み取り

QRコードの作成については以下の記事を参照。

OpenCV単体でもバーコード・QRコードの検出・読み取りが可能。厳密には検証していないが、検出精度はZBar(pyzbar)のほうが良さそう。

本記事のサンプルコードで使用しているpyzbarのバージョンは0.1.9

import pyzbar

print(pyzbar.__version__)
# 0.1.9

※QRコードは株式会社デンソーウェーブの登録商標です。

ZBarとpyzbar

ZBarはバーコードを読み取るためのオープンソースライブラリ。現在はオリジナルからフォークされたものが開発されている。

pyzbarはPythonでZBarを利用するためのライブラリ。pipでインストールできる。

Windowsの場合、ZBarのDLLが同梱されているため追加のインストールは不要だが、macOSおよびLinuxでは別途ZBar本体のインストールが必要。詳細は上記GitHubのREADMEを参照。

画像からバーコード・QRコードを検出・読み取り

基本的な使い方

pyzbar.pyzbar.decode()に画像を指定する。ここではPillow(PIL)を用いているが、OpenCVで読み込んだnumpy.ndarrayもそのまま指定可能。OpenCVの例は後述。

例として以下の画像を用いる。

from PIL import Image
from pyzbar.pyzbar import decode, ZBarSymbol

img = Image.open('data/src/barcode_qrcode.jpg')

Barcode and QR code sample

decode()は、検出したバーコード・QRコードの情報を保持するpyzbar.pyzbar.Decoded型を要素とするリストを返す。一つも検出できなかった場合は空のリストとなる。

img = Image.open('data/src/barcode_qrcode.jpg')

decoded_list = decode(img)

print(type(decoded_list))
# <class 'list'>

print(len(decoded_list))
# 3

print(type(decoded_list[0]))
# <class 'pyzbar.pyzbar.Decoded'>

print(decoded_list[0])
# Decoded(data=b'QR Code Example', type='QRCODE', rect=Rect(left=8, top=6, width=159, height=160), polygon=[Point(x=8, y=66), Point(x=66, y=166), Point(x=167, y=109), Point(x=108, y=6)], quality=1, orientation='UP')

print(decoded_list[1])
# Decoded(data=b'1923055034006', type='EAN13', rect=Rect(left=161, top=217, width=175, height=32), polygon=[Point(x=161, y=229), Point(x=239, y=249), Point(x=330, y=248), Point(x=332, y=242), Point(x=335, y=226), Point(x=336, y=220), Point(x=336, y=218), Point(x=248, y=217), Point(x=165, y=217), Point(x=164, y=219), Point(x=162, y=225)], quality=37, orientation='UP')

print(decoded_list[2])
# Decoded(data=b'9784873117980', type='EAN13', rect=Rect(left=196, top=128, width=158, height=26), polygon=[Point(x=196, y=137), Point(x=267, y=151), Point(x=348, y=154), Point(x=349, y=152), Point(x=352, y=138), Point(x=354, y=128), Point(x=199, y=129), Point(x=197, y=133)], quality=25, orientation='UP')

dataはバーコード・QRコードに格納された内容。バイト列bytesなので、文字列strに変換したい場合はdecode()メソッドを使う。

print(decoded_list[0].data)
# b'QR Code Example'

print(type(decoded_list[0].data))
# <class 'bytes'>

print(decoded_list[0].data.decode())
# QR Code Example

typeは読み取ったバーコード・QRコードの種類。日本のバーコードで広く使われているJANコードはEANコードをベースに規格化されたものでEAN13として認識される。

print(decoded_list[0].type)
# QRCODE

print(decoded_list[1].type)
# EAN13

対応する種類は以下の通り。

for s in ZBarSymbol:
    print(s)
# ZBarSymbol.NONE
# ZBarSymbol.PARTIAL
# ZBarSymbol.EAN2
# ZBarSymbol.EAN5
# ZBarSymbol.EAN8
# ZBarSymbol.UPCE
# ZBarSymbol.ISBN10
# ZBarSymbol.UPCA
# ZBarSymbol.EAN13
# ZBarSymbol.ISBN13
# ZBarSymbol.COMPOSITE
# ZBarSymbol.I25
# ZBarSymbol.DATABAR
# ZBarSymbol.DATABAR_EXP
# ZBarSymbol.CODABAR
# ZBarSymbol.CODE39
# ZBarSymbol.PDF417
# ZBarSymbol.QRCODE
# ZBarSymbol.SQCODE
# ZBarSymbol.CODE93
# ZBarSymbol.CODE128

rectpolygonは検出したバーコード・QRコードの位置・形状を表す。rectはバウンディングボックス、polygonは検出したエリアそのものを表す多角形。それぞれの違いは後述の図形描画の結果を参照。

print(decoded_list[0].rect)
# Rect(left=8, top=6, width=159, height=160)

print(decoded_list[0].polygon)
# [Point(x=8, y=66), Point(x=66, y=166), Point(x=167, y=109), Point(x=108, y=6)]

検出するタイプを指定

上述のように、デフォルトではバーコードもQRコードもすべて検出する。

検出対象のタイプを限定したい場合はdecode()symbols引数にリストを指定する。一つだけでもリストで指定する必要があるので注意。

print(len(decode(img, symbols=[ZBarSymbol.QRCODE])))
# 1

print(len(decode(img, symbols=[ZBarSymbol.EAN13])))
# 2

print(len(decode(img, symbols=[ZBarSymbol.QRCODE, ZBarSymbol.EAN13])))
# 3

Pillowとpyzbarの組み合わせ

Pillowの図形描画機能を用いて、検出したバーコード・QRコードを枠で囲み、デコードした文字列を重畳して表示する。

from PIL import Image, ImageDraw, ImageFont
from pyzbar.pyzbar import decode

img = Image.open('data/src/barcode_qrcode.jpg')

draw = ImageDraw.Draw(img)
font = ImageFont.truetype('Arial.ttf', size=20)  # Set 'arial.ttf' for Windows

for d in decode(img):
    draw.rectangle(((d.rect.left, d.rect.top), (d.rect.left + d.rect.width, d.rect.top + d.rect.height)),
                   outline=(0, 0, 255), width=3)
    draw.polygon(d.polygon, outline=(0, 255, 0), width=3)
    draw.text((d.rect.left, d.rect.top + d.rect.height), d.data.decode(),
              (255, 0, 0), font=font)

img.save('data/dst/barcode_qrcode_pillow.jpg')

Detect and decode barcode and QR code with pyzbar and Pillow

Pillowの図形描画についての詳細は以下の記事を参照。

ImageFont.truetype()のフォント指定がうまくいかない場合はフォントファイルの絶対パスを指定すればよい。

OpenCVとpyzbarの組み合わせ

OpenCVの図形描画機能を用いて、検出したバーコード・QRコードを枠で囲み、デコードした文字列を重畳して表示する。

上述のように、pyzbar.pyzbar.decode()にはOpenCVで読み込んだnumpy.ndarrayの画像もそのまま指定できる。

import cv2
import numpy as np
from pyzbar.pyzbar import decode

img = cv2.imread('data/src/barcode_qrcode.jpg')

for d in decode(img):
    img = cv2.rectangle(img, (d.rect.left, d.rect.top),
                        (d.rect.left + d.rect.width, d.rect.top + d.rect.height), (255, 0, 0), 2)
    img = cv2.polylines(img, [np.array(d.polygon)], True, (0, 255, 0), 2)
    img = cv2.putText(img, d.data.decode(), (d.rect.left, d.rect.top + d.rect.height),
                      cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 1, cv2.LINE_AA)

cv2.imwrite('data/dst/barcode_qrcode_opencv.jpg', img)
# True

Detect and decode barcode and QR code with pyzbar and OpenCV

OpenCVの図形描画についての詳細は以下の記事を参照。

カメラの動画からバーコード・QRコードを検出・読み取り

OpenCVを用いてカメラのリアルタイム動画からバーコード・QRコードを検出し読み取るサンプルコードを示す。

OpenCVにおける動画の扱いについては以下の記事を参照。

キーボードのqを押すと終了する。

import cv2
from pyzbar.pyzbar import decode

camera_id = 0
delay = 1
window_name = 'OpenCV pyzbar'

cap = cv2.VideoCapture(camera_id)

while True:
    ret, frame = cap.read()

    if ret:
        for d in decode(frame):
            s = d.data.decode()
            print(s)
            frame = cv2.rectangle(frame, (d.rect.left, d.rect.top),
                                  (d.rect.left + d.rect.width, d.rect.top + d.rect.height), (0, 255, 0), 3)
            frame = cv2.putText(frame, s, (d.rect.left, d.rect.top + d.rect.height),
                                cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 0, 255), 2, cv2.LINE_AA)
        cv2.imshow(window_name, frame)

    if cv2.waitKey(delay) & 0xFF == ord('q'):
        break

cv2.destroyWindow(window_name)

なお、実際のアプリケーションでは、ターゲットのバーコード・QRコードに格納された文字列を取得できた時点でbreakwhileループを終了し、その文字列を用いた処理に移行する形になることが多いだろう。

関連カテゴリー

関連記事