はじめに
QRコードは生活のあらゆる場面で利用されている便利なツールです。商品のURLや会員情報など、さまざまな情報を格納できるQRコードを、プログラムで読み取ることができれば、その用途は無限大です。
この記事では、OpenCVを使用してQRコードをスキャンする手法を解説します。
OpenCVでは、QRコードをスキャンする機能としてQRCodeDetector
クラスとQRCodeDetectorAruco
クラスが提供されていて、これらの違いと使い分けについて解説します。
また、OpenCVを使ったQRコードの生成方法については、こちらの記事で紹介していますのでご参考ください。
QRコード, QR Codeはデンソーウェーブの登録商標です。
QRコードの読み取り
OpenCVで、QRコードを読み取ることができるQRCodeDetector
クラスとQRCodeDetectorAruco
クラスについて説明します。
これらの主な違いは以下の通りです。
おおむね、QRCodeDetectorAruco
クラスの方が高性能となっています。
機能 | QRCodeDetectorクラス | QRCodeDetectorArucoクラス |
検出できるコード | QRコードのみ | QRコードとArUcoマーカー |
処理速度 | 低速 | 高速 |
検出精度 | 低 | 高 |
機能 | シンプル | 拡張性が高い |
対応しているOpenCVのバージョン | 3.4以降detectAndDecodeMulti メソッドは4.3以降 | 4.8以降 |
QRCodeDetector
クラスとQRCodeDetectorAruco
クラスはGraphicalCodeDetector
クラスを継承したものとなっています。GraphicalCodeDetector
クラスにはデコードのみを行う、decode
メソッド、decodeMulti
メソッド、QRコードの検出のみを行う、detect
メソッド、detectMulti
メソッド、1つのQRコードの検出とデコードを行うdetectAndDecode
メソッド、複数のQRコードの検出とデコードを一度で行うdetectAndDecodeMulti
メソッドがあります。
本記事では最も汎用性のあるdetectAndDecodeMulti
メソッドについてのみ解説します。
QRCodeDetector
クラスやQRCodeDetectorAruco
クラスを使用したQRコードの読み取りは以下の手順となります。
- QRコードの画像の取得
QRCodeDetector
クラスまたは、QRCodeDetectorAruco
クラスのオブジェクトを作成するdetectAndDecodeMulti
メソッドで、QRコードの検出を実行
detectAndDecodeMulti()メソッド
detectAndDecodeMulti
メソッドはOpenCVのバージョン3.4以降で利用可能な機能です。
detectAndDecode
メソッドとは異なり、画像内に複数のQRコードを同時に検出することができます。
detectAndDecodeMulti(入力画像,[, points[, straight_code]])
引数
名称 | 説明 |
入力画像(必須) | QRコードにエンコードするデータ |
points(オプション) | 検出されたQRコードの四角形の頂点座標が格納されたnumpy配列が入力されます。戻り値として取得するものと同じです。 |
straight_code(オプション) | 検出されたQRコードの各セルの白黒情報(0: 黒、1: 白)が格納されたnumpy配列が入力されます。各要素は、QRコードのバージョンに応じたサイズ(21x21、25x25、…) のベクトルです。戻り値として取得するものと同じです。 |
戻り値
下のデータが入ったタプルが戻り値となります。
名称 | 説明 |
retval | QRコードを読み取ることができた場合、True を返し、できなかった場合、False を返します。 |
decoded_info | デコードされたQRコードのデータ(UTF-8エンコード)が格納されたnumpy配列 |
points | 検出されたQRコードの四角形の頂点座標が格納されたnumpy配列 |
straight_code | 検出されたQRコードの各セルの白黒情報(0: 黒、1: 白)を格納するベクトル。各要素は、QRコードのバージョンに応じたサイズ(21x21、25x25、…) のベクトルです。戻り値として取得するものと同じです。 |
使い方
読み取るQRコードの画像データを準備します。
今回は、下の画像を用いました。
回転した6個のQRコードの画像です。
QRCodeDetectorクラス
QRCodeDetector
クラスのサンプルコードを下に示します。
上のQRコードを読みとるプログラムとなります。
import cv2 from matplotlib import pyplot as plt # QRコードの画像を読み込む image = cv2.imread("qrcodes.png") # QRCodeDetectorオブジェクトを作成する detector = cv2.QRCodeDetector() # 画像内のすべてのQRコードを検出 retval, decoded_info, points, straight_qrcode = detector.detectAndDecodeMulti(image) # 検出したQRコードの座標のデータ型を"int"に変換 points = points.astype(int) # 描画設定 is_closed = True # QRコードを囲う四角形を閉包図形とする font = cv2.FONT_HERSHEY_SIMPLEX # フォントの種類 font_color = (255, 0, 255) # フォントの色をマゼンタに設定 font_line_type = cv2.LINE_AA # フォントをアンチエイリアスで描画 # 検出したQRコードのそれぞれを四角で囲い、デコードしたデータを描画する if len(decoded_info) > 0: for i in range(len(decoded_info)): # QRコードの左上の座標を取得 x = points[i][0][0] y = points[i][0][1] # QRコードの周囲に矩形を描画 cv2.polylines(image, [points[i]], is_closed, (0, 255, 0), thickness=2) # QRコードをデコードしたデータを表示 cv2.putText(image, decoded_info[i], (x, y-10), font, 0.8, font_color, thickness=2, lineType=font_line_type) # 結果の可視化 title = "cv2.QRCodeEncoder: codevace.com" plt.figure(title) # ウィンドウタイトルを設定 plt.axis("off") # 軸目盛、軸ラベルを消す plt.imshow(image) # ウィンドウ上に画像を配置 plt.show() # ウィンドウを表示する。
読み取り結果は下の様になりました。
読み取ることができたQRコードは緑色の四角形で囲い、読み取ったコードをマゼンタで表示しましたが、回転角度が30°以上のQRコードを読み取ることができませんでした。
QRCodeDetectorArucoクラス
QRCodeDetectorAruco
クラスのサンプルコードを下に示します。QRCodeDetector
クラスの場合と同様に、上のQRコードを読みとるプログラムとなります。
import cv2 from matplotlib import pyplot as plt # RQコードの画像を読み込む image = cv2.imread("qrcodes.png") # QRCodeDetectorArucoオブジェクトを作成する detector = cv2.QRCodeDetectorAruco() # OpenCVのバージョン4.8以上 # 画像内のすべてのQRコードを検出 retval, decoded_info, points, straight_qrcode = detector.detectAndDecodeMulti(image) # 検出したQRコードの座標のデータ型を"int"に変換 points = points.astype(int) # 描画設定 is_closed = True # QRコードを囲う四角形を閉包図形とする font = cv2.FONT_HERSHEY_SIMPLEX # フォントの種類 font_color = (255, 0, 255) # フォントの色をマゼンタに設定 font_line_type = cv2.LINE_AA # フォントをアンチエイリアスで描画 # 検出したQRコードのそれぞれを四角で囲い、デコードしたデータを描画する if len(decoded_info) > 0: for i in range(len(decoded_info)): # QRコードの左上の座標を取得 x = points[i][0][0] y = points[i][0][1] # QRコードの周囲に矩形を描画 cv2.polylines(image, [points[i]], is_closed, (0, 255, 0), thickness=2) # QRコードをデコードしたデータを表示 cv2.putText(image, decoded_info[i], (x, y-10), font, 0.8, font_color, thickness=2, lineType=font_line_type) # 結果の可視化 title = "cv2.QRCodeDetectorAruco: codevace.com" plt.figure(title) # ウィンドウタイトルを設定 plt.axis("off") # 軸目盛、軸ラベルを消す plt.imshow(image) # ウィンドウ上に画像を配置 plt.show() # ウィンドウを表示する。
読み取り結果は下の様になりました。QRCodeDetectorAruco
クラスでは全てのQRコードを読み取ることができました。
QRコードを含む画像の解像度や、歪みなどの影響の検証が十分とは言えませんが、QRCodeDetector
クラスよりQRCodeDetectorAruco
クラスの方が検出精度が高いことが確認できました。
読み取り速度の比較
QRCodeDetector
クラスとQRCodeDetectorAruco
クラスの読み取り速度を比較しました。
それぞれのクラスで、これまでと同じQRコードの画像を500回読み取る時間を計測しました。
処理時間の計測は、下記の記事で紹介しています。
import cv2 # RQコードの画像を読み込む image = cv2.imread("qrcodes.png") # QRCodeDetectorオブジェクトを作成する detector = cv2.QRCodeDetector() # QRCodeDetectorを使った、QRコードの検出とデコード start_time = cv2.getTickCount() for i in range(500): retval, decoded_info, points, straight_qrcode = detector.detectAndDecodeMulti(image) end_time = cv2.getTickCount() elapsed_time = (end_time - start_time) / cv2.getTickFrequency() print("QRCodeDetector") print("Elapsed time:", elapsed_time, "seconds") print('\n') # QRCodeDetectorArucoオブジェクトを作成する detector = cv2.QRCodeDetectorAruco() # OpenCVのバージョン4.8以上 # QRCodeDetectorArucoを使った、QRコードの検出とデコード start_time = cv2.getTickCount() for i in range(500): retval, decoded_info, points, straight_qrcode = detector.detectAndDecodeMulti(image) end_time = cv2.getTickCount() elapsed_time = (end_time - start_time) / cv2.getTickFrequency() print("QRCodeDetectorAruco") print("Elapsed time:", elapsed_time, "seconds")
今回、実行環境はこちらです。
ソフトウェア:
- OpenCV 4.9.0
- Python 3.11.1
ハードウェア:
- MacBook Pro 13-inch, M1, 2020
結果はQRCodeDetectorArucoクラスの方が倍近い速さで終了しました。
QRCodeDetector Elapsed time: 15.781673167 seconds QRCodeDetectorAruco Elapsed time: 8.464325167 seconds
おまけ
読み取りに使ったQRコードの画像は下のコードで作成しました。
ただし、QRコードの回転角度をランダムに選択しているので、上のQRコードと全く同じにはならないと思います。
import random import cv2 import numpy as np from matplotlib import pyplot as plt def warpAffine_rotaion(src, angle, scale): """ 指定された回転角度に反時計回りに画像を回転し、回転の画像サイズに合わせてサイズを変化した画像を返す。 src: 入力画像 angle: 回転角度 scale: 画像の拡大・縮小を指定する倍率 戻り値: 回転した画像 """ # 入力画像のサイズを取得 height, width = src.shape[:2] # 回転の中心座標を指定 center = (width // 2, height // 2) # 変換行列(回転行列)を計算 M = cv2.getRotationMatrix2D(center, angle, scale) # 回転後の画像のサイズを計算 cos_theta = np.abs(M[0, 0]) sin_theta = np.abs(M[0, 1]) new_width = int(width * cos_theta + height * sin_theta) new_height = int(width * sin_theta + height * cos_theta) # 回転の中心のズレを修正 M[0,2] += (new_width - width)/2.0 M[1,2] += (new_height - height)/2.0 # 画像を反時計回りにangle°回転する return cv2.warpAffine(src, M, (new_width, new_height), flags=cv2.INTER_NEAREST, borderValue=(255,255,255)) # QRコードにする文字列のリスト encoded_info = ["QR Code #1", "QR Code #2", "QR Code #3", "QR Code #4", "QR Code #5", "QR Code #6"] # QRコード エンコーダーの設定 qr_params = cv2.QRCodeEncoder_Params() qr_params.correction_level = cv2.QRCODE_ENCODER_CORRECT_LEVEL_Q qr_params.mode = cv2.QRCODE_ENCODER_MODE_AUTO qr_params.version = 3 qr_params.structure_number = 1 # QRCodeEncoderクラスのインスタンスを作成 QRencoder = cv2.QRCodeEncoder.create(qr_params) qrcodes = [] for item in range(len(encoded_info)): # QRコードを作成 qrcode = QRencoder.encode(encoded_info[item]) # サイズを取得 height, width = qrcode.shape[:] # 回転の中心を指定 center = (int(width/2), int(height/2)) # 回転角を指定 angle = random.randrange(-45, 45, 5) # スケールを指定 scale = 5.0 # getRotationMatrix2D関数を使用 trans = cv2.getRotationMatrix2D(center, angle , scale) # アフィン変換 qrcode = warpAffine_rotaion(qrcode, angle, scale) # QRコードをリストに追加 qrcodes.append(qrcode) # 結果の可視化 plt.rcParams["figure.figsize"] = [8,5.5] # 表示領域のアスペクト比を設定 title = "QR Codes: codevace.com" plt.figure(title) # ウィンドウタイトルを設定 plt.subplots_adjust(left=0.05, right=0.95, bottom=0.05, top=0.9) # 余白を設定 for code in range(len(qrcodes)): pos = 231 + code plt.subplot(pos) # 2行3列のcode番目の領域にプロットを設定 plt.imshow(qrcodes[code], cmap='gray') # QRコードを表示 plt.title(encoded_info[code]) # 画像タイトル設定 plt.axis("off") # 軸目盛、軸ラベルを消す plt.savefig("qrcodes.png") # QRコードを画像として保存 plt.show() # ウィンドウを表示する。
おわりに
本記事の検証で、QRCodeDetector
クラスよりQRCodeDetectorAruco
クラスの方が高精度かつ高速にQRコードの読み取りを行うことができると言えそうです。
ご質問や取り上げて欲しい内容などがありましたら、コメントをお願いします。
最後までご覧いただきありがとうございました。
参考リンク
OpenCV: cv::QRCodeDetector Class
cv::QRCodeDetector Class Reference
OpenCV: cv::QRCodeDetector Class
cv::QRCodeDetector Class Reference
OpenCV: cv::QRCodeDetectorAruco Class
cv::QRCodeDetectorAruco Class Reference
■(広告)Pythonのオススメ書籍■
(広告)あなたに合ったプログラミング スクールが見つかるかも
プログラミングの学習が、新しい世界を開きます。副業からキャリアの転換まで、目標に向かって一歩を踏み出しましょう。
これらのプログラミング スクールは、あなたの目的に合わせて選択できるさまざまなプログラムを提供しています。どのスクールを選ぶにせよ、プログラミングは未来への扉を開き、新しい機会を切り開く手段として素晴らしい選択です。あなたの夢や目標を実現する第一歩を踏み出す準備はできていますか?