pyzbarによるバーコード、二次元コード(QRコード)スキャン【Python 画像処理】

Python

今すぐ試したい!機械学習・深層学習(ディープラーニング)画像認識プログラミングレシピ [ 川島 賢 ]

 

こんにちは、monachan_papa です。
今回は Python による バーコード、二次元コード(※QRコード) のスキャンについて解説します。
これらのスキャンをするには、pyzbar というライブラリが必要ですが、このライブラリを使うと驚くほど簡単にスキャンが実現できます。

今回、私は名古屋のちんどん べんてんやのCDアルバム『千客万来』 のバーコードと、べんてんやYoutubeチャンネルの二次元コードをスキャンしてみたいと思います。

千客万来のバーコード

べんてんやYoutubeチャンネルの二次元コード

なお、バーコードも二次元コードも全く同じ手法でスキャンができますので、本当に便利なライブラリです。

是非、pyzbar の使い方をマスターし、色々なバーコードや二次元コードをスキャンしてみましょう。

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

pyzbar のインストール

以下のコマンドで、pyzbar のインストールをします。

pip install pyzbar

Windows ユーザーの御方はこれだけでOKです。しかし、mac ユーザーの御方は別途、以下もインストールが必要です。

brew install zbar

ライブラリのインポート

pyzbar をはじめとし、以下のライブラリをインポートします。

import numpy as np
from PIL import Image
from pyzbar.pyzbar import decode

画像読込と前処理

まずは、バーコード画像を読み込みます。カレントディレクトリに用意しておいたバーコード画像 senkyakubanrai.jpg を読み込んで表示します。

# 画像読込
src_img = Image.open('senkyakubanrai.jpg')
src_img

さて、この後すぐに pyzbarライブラリを使って、バーコードスキャンを試みても良いのですが、元画像を 0.1〜2.0倍にリサイズした画像群を別途用意する前処理を行います。
なぜ、この前処理が必要か?画像サイズが小さすぎたり、大きすぎたりした場合、スキャンに失敗してしまうことがあるからです。私自身、成功するための最適な画像サイズが把握できていないので、失敗するたびにいちいち手動でリサイズするのは面倒です。
よって、事前にリサイズ画像群を用意しておき、最終的にそれらすべてをスキャンするという流れにします。

以下のコードは、元画像を 0.1倍〜2.0倍にリサイズしたものをリスト格納しています。
なお、リサイズ用メソッドの引数 Image.LANCZOSは、リサイズ時の画像品質を良くするための指定です。

# 0.1倍〜2.0倍にリサイズ
rate = np.arange(0.1, 2.1, 0.1)
imgs = [src_img.resize((int(src_img.width * i), int(src_img.height * i)), Image.LANCZOS) for i in rate]
imgs
[<PIL.Image.Image image mode=RGB size=67x32 at 0x7F8A31693FA0>,
 <PIL.Image.Image image mode=RGB size=134x65 at 0x7F8A316F2DC0>,
 <PIL.Image.Image image mode=RGB size=202x98 at 0x7F8A316F2D30>,
 <PIL.Image.Image image mode=RGB size=269x130 at 0x7F8A316F2DF0>,
 <PIL.Image.Image image mode=RGB size=337x163 at 0x7F8A316F2E20>,
 <PIL.Image.Image image mode=RGB size=404x196 at 0x7F8A316F2910>,
 <PIL.Image.Image image mode=RGB size=471x228 at 0x7F8A316F2B80>,
 <PIL.Image.Image image mode=RGB size=539x261 at 0x7F8A316F2B50>,
 <PIL.Image.Image image mode=RGB size=606x294 at 0x7F8A316F2D00>,
 <PIL.Image.Image image mode=RGB size=674x327 at 0x7F8A31704760>,
 <PIL.Image.Image image mode=RGB size=741x359 at 0x7F8A31704820>,
 <PIL.Image.Image image mode=RGB size=808x392 at 0x7F8A31704880>,
 <PIL.Image.Image image mode=RGB size=876x425 at 0x7F8A31704940>,
 <PIL.Image.Image image mode=RGB size=943x457 at 0x7F8A31704910>,
 <PIL.Image.Image image mode=RGB size=1011x490 at 0x7F8A317049A0>,
 <PIL.Image.Image image mode=RGB size=1078x523 at 0x7F8A317049D0>,
 <PIL.Image.Image image mode=RGB size=1145x555 at 0x7F8A31704A00>,
 <PIL.Image.Image image mode=RGB size=1213x588 at 0x7F8A31704A30>,
 <PIL.Image.Image image mode=RGB size=1280x621 at 0x7F8A31704A60>,
 <PIL.Image.Image image mode=RGB size=1348x654 at 0x7F8A31704A90>]
img.resize((img.width * 倍率, img.height * 倍率), Image.LANCZOS)
※img はイメージオブジェクト

コードスキャン

さあ、今回の目玉についてやるときが来ました。
と言っても、リサイズ画像群に対して、以下の関数を使ってコードスキャンをするだけという超絶簡単仕様です。

decode(img)
※img はイメージオブジェクト

decode関数の引数に、用意しておいたイメージオブジェクトを指定してスキャンします。

# コードスキャン
datas = [decode(img) for img in imgs]
datas
[[],
 [],
 [Decoded(data=b'0021281699295', type='EAN13', rect=Rect(left=24, top=8, width=158, height=61), polygon=[Point(x=24, y=9), Point(x=24, y=11), Point(x=26, y=69), Point(x=182, y=68), Point(x=182, y=60), Point(x=181, y=24), Point(x=180, y=8)], quality=65, orientation='UP')],
 [Decoded(data=b'0021281699295', type='EAN13', rect=Rect(left=33, top=12, width=209, height=80), polygon=[Point(x=33, y=13), Point(x=33, y=37), Point(x=34, y=71), Point(x=35, y=91), Point(x=242, y=92), Point(x=242, y=78), Point(x=241, y=36), Point(x=240, y=12)], quality=82, orientation='UP')],
 [Decoded(data=b'0021281699295', type='EAN13', rect=Rect(left=41, top=14, width=262, height=102), polygon=[Point(x=41, y=15), Point(x=41, y=33), Point(x=43, y=99), Point(x=44, y=115), Point(x=303, y=116), Point(x=303, y=96), Point(x=302, y=52), Point(x=301, y=14), Point(x=176, y=14)], quality=109, orientation='UP')],
 [Decoded(data=b'0021281699295', type='EAN13', rect=Rect(left=49, top=17, width=314, height=122), polygon=[Point(x=49, y=17), Point(x=49, y=35), Point(x=52, y=131), Point(x=53, y=139), Point(x=363, y=138), Point(x=363, y=108), Point(x=362, y=64), Point(x=361, y=26), Point(x=360, y=18)], quality=132, orientation='UP')],
 [Decoded(data=b'0021281699295', type='EAN13', rect=Rect(left=57, top=19, width=367, height=144), polygon=[Point(x=57, y=21), Point(x=57, y=37), Point(x=60, y=133), Point(x=61, y=163), Point(x=424, y=162), Point(x=424, y=158), Point(x=422, y=76), Point(x=421, y=40), Point(x=420, y=20), Point(x=58, y=19)], quality=164, orientation='UP')],
 [Decoded(data=b'0021281699295', type='EAN13', rect=Rect(left=65, top=22, width=420, height=165), polygon=[Point(x=65, y=23), Point(x=65, y=37), Point(x=69, y=167), Point(x=70, y=187), Point(x=485, y=186), Point(x=485, y=174), Point(x=483, y=94), Point(x=481, y=22)], quality=196, orientation='UP')],
 [Decoded(data=b'0021281699295', type='EAN13', rect=Rect(left=73, top=25, width=472, height=184), polygon=[Point(x=73, y=27), Point(x=73, y=35), Point(x=78, y=197), Point(x=79, y=209), Point(x=545, y=208), Point(x=545, y=184), Point(x=544, y=144), Point(x=541, y=26), Point(x=74, y=25)], quality=220, orientation='UP')],
 [Decoded(data=b'0021281699295', type='EAN13', rect=Rect(left=81, top=28, width=525, height=205), polygon=[Point(x=81, y=29), Point(x=81, y=33), Point(x=87, y=227), Point(x=88, y=233), Point(x=606, y=232), Point(x=606, y=198), Point(x=602, y=36), Point(x=601, y=28), Point(x=352, y=28)], quality=265, orientation='UP')],
 [Decoded(data=b'0021281699295', type='EAN13', rect=Rect(left=89, top=31, width=578, height=226), polygon=[Point(x=89, y=33), Point(x=89, y=35), Point(x=95, y=231), Point(x=96, y=257), Point(x=667, y=256), Point(x=665, y=168), Point(x=662, y=48), Point(x=661, y=32), Point(x=90, y=31)], quality=283, orientation='UP')],
 [Decoded(data=b'0021281699295', type='EAN13', rect=Rect(left=97, top=34, width=630, height=247), polygon=[Point(x=97, y=35), Point(x=97, y=37), Point(x=98, y=71), Point(x=104, y=263), Point(x=105, y=281), Point(x=727, y=280), Point(x=727, y=274), Point(x=725, y=168), Point(x=723, y=96), Point(x=722, y=64), Point(x=721, y=34), Point(x=422, y=34)], quality=307, orientation='UP')],
 [Decoded(data=b'0021281699295', type='EAN13', rect=Rect(left=105, top=37, width=683, height=266), polygon=[Point(x=105, y=39), Point(x=105, y=41), Point(x=113, y=293), Point(x=114, y=303), Point(x=788, y=302), Point(x=788, y=286), Point(x=786, y=180), Point(x=782, y=38), Point(x=106, y=37)], quality=338, orientation='UP')],
 [Decoded(data=b'0021281699295', type='EAN13', rect=Rect(left=113, top=39, width=735, height=288), polygon=[Point(x=113, y=41), Point(x=113, y=45), Point(x=122, y=327), Point(x=848, y=326), Point(x=848, y=282), Point(x=846, y=190), Point(x=842, y=40), Point(x=115, y=39)], quality=368, orientation='UP')],
 [Decoded(data=b'0021281699295', type='EAN13', rect=Rect(left=121, top=42, width=789, height=309), polygon=[Point(x=121, y=45), Point(x=121, y=47), Point(x=130, y=321), Point(x=131, y=351), Point(x=910, y=350), Point(x=910, y=348), Point(x=909, y=296), Point(x=907, y=202), Point(x=903, y=54), Point(x=902, y=42), Point(x=528, y=42), Point(x=122, y=43)], quality=398, orientation='UP')],
 [Decoded(data=b'0021281699295', type='EAN13', rect=Rect(left=129, top=45, width=841, height=330), polygon=[Point(x=129, y=47), Point(x=129, y=51), Point(x=139, y=357), Point(x=140, y=375), Point(x=970, y=372), Point(x=969, y=312), Point(x=967, y=214), Point(x=963, y=70), Point(x=962, y=46), Point(x=131, y=45)], quality=433, orientation='UP')],
 [Decoded(data=b'0021281699295', type='EAN13', rect=Rect(left=137, top=48, width=893, height=349), polygon=[Point(x=137, y=51), Point(x=137, y=53), Point(x=148, y=397), Point(x=1030, y=396), Point(x=1030, y=382), Point(x=1029, y=326), Point(x=1027, y=218), Point(x=1022, y=48), Point(x=598, y=48), Point(x=138, y=49)], quality=464, orientation='UP')],
 [Decoded(data=b'0021281699295', type='EAN13', rect=Rect(left=145, top=51, width=946, height=370), polygon=[Point(x=145, y=55), Point(x=145, y=57), Point(x=152, y=273), Point(x=157, y=421), Point(x=1091, y=420), Point(x=1091, y=394), Point(x=1088, y=230), Point(x=1083, y=52), Point(x=147, y=51)], quality=494, orientation='UP')],
 [Decoded(data=b'0021281699295', type='EAN13', rect=Rect(left=153, top=53, width=999, height=392), polygon=[Point(x=153, y=57), Point(x=153, y=61), Point(x=165, y=421), Point(x=166, y=445), Point(x=1152, y=444), Point(x=1152, y=442), Point(x=1151, y=392), Point(x=1148, y=244), Point(x=1143, y=54), Point(x=156, y=53)], quality=520, orientation='UP')],
 [Decoded(data=b'0021281699295', type='EAN13', rect=Rect(left=161, top=56, width=1052, height=411), polygon=[Point(x=161, y=61), Point(x=161, y=63), Point(x=169, y=307), Point(x=174, y=455), Point(x=175, y=467), Point(x=1213, y=466), Point(x=1213, y=464), Point(x=1212, y=396), Point(x=1209, y=256), Point(x=1204, y=74), Point(x=1203, y=56), Point(x=705, y=56), Point(x=164, y=57), Point(x=162, y=59)], quality=543, orientation='UP')]]

元画像のサイズが良かったようで、0.1倍、0.2倍リサイズ以外はすべてスキャン成功しています。
さて、成功したデータを見ると色々な情報があります。しかし、欲しい情報はバーコードだけなので、その情報だけを取得します。

以下のコードは、スキャン結果すべての中から成功データのみにフィルタリングした後、一番初めのデータの必要情報だけをprint出力しています。
もし、成功データが1つもない場合は、print出力で INVALID と表示します。

# スキャン成功データのみをフィルタリング
*codes, = filter(lambda x: x, datas)

# 元画像表示
display(src_img)

# 結果表示
if len(codes) > 0:
    # 一番初めにスキャン成功したものを表示
    code = codes[0]
    print(code[0][0].decode('utf8'))
else:
    print('INVARID')

0021281699295

見事にスキャン成功しています。

 


今すぐ試したい!機械学習・深層学習(ディープラーニング)画像認識プログラミングレシピ [ 川島 賢 ]

 

続いて、二次元コード(QRコード) のスキャンもしたいと思います。
以下のコードは、上記のコード群をまとめ直したものです。これで二次元コード(QRコード)のスキャンをします。
画像読込で、カレントディレクトリにある bentenya_youtube.jpg を指定して、実行します。

import numpy as np
from PIL import Image
from pyzbar.pyzbar import decode

# 画像読込
file = 'bentenya_youtube.jpg'
src_img = Image.open(file)

# 0.1倍〜2.0倍にリサイズ
rate = np.arange(0.1, 2.1, 0.1)
imgs = [src_img.resize((int(src_img.width * i), int(src_img.height * i)), Image.LANCZOS) for i in rate]

# コードスキャン
datas = [decode(img) for img in imgs]

# スキャン成功データのみをフィルタリング
*codes, = filter(lambda x: x, datas)

# 元画像表示
display(src_img)

# 結果表示
if len(codes) > 0:
    # 一番初めにスキャン成功したものを表示
    code = codes[0]
    print(code[0][0].decode('utf8'))
else:
    print('INVARID')

https://www.youtube.com/channel/UCWnbWyerlLBHQpWgjQx7IKg

二次元コード(QRコード)のスキャンも成功しました。べんてんやのYoutubeチャンネルのURLが正しく表示されています。
是非、みなさんチャンネル登録とべんてんやのCDをgetしてくださいね!


千客万来 [ べんてんや ]

 


 

以上で、Python による バーコード、二次元コード(QRコード) のスキャンについて解説を終わります。

今回やっていることはとてもシンプルですが、Pythonの内包表記やlambda式などをたくさん使っていますので、それが難しかった御方は、以下記事が参考になるかと思います。

Pythonの内包表記を初心者向けに解説。リスト・集合・辞書内包表記【Python tips】

無名関数のlambda式(ラムダ式)の使いどころをマスターしよう!【Python tips】

さて、Pythonによる画像処理で出来ることは、この他にもたくさんあります。ディープ・ラーニングも取り入れれば、もっとすごいことも可能となります。スマホの顔認証など身近な例ですね。

画像処理にもっと詳しく触れてみたい!という御方は、今すぐ試したい!機械学習・深層学習(ディープラーニング)画像認識プログラミングレシピ [ 川島 賢 ]が大変おすすめです。もし、画像処理でやってみたい事がすぐにでも思いつかない場合でも、お題がちゃんと用意されています。
例えば、犬なのか猫なのかを認識する犬猫認識など。Pythonで画像処理をもっと楽しみましょう。

 


今すぐ試したい!機械学習・深層学習(ディープラーニング)画像認識プログラミングレシピ [ 川島 賢 ]

 

また、爆速で Python画像処理を学びたい御方にはプログラミングスクールを考えるのも、ひとつの手です。独学よりも効果が出やすいですが、いかんせん投資がけっこうかかります。しかし、techgymというスクールは通うか通わないかは別として、無料のサンプルテキスト&解説動画がもらえます。これをとりあえず getしてまずは試しに体験学習するのもありでしょう。

コメント

タイトルとURLをコピーしました