OpenCVでQRCodeスキャンをやってみた
概要
OpenCVのQRCodeスキャンメソッドを使ってwebBowserができるかトライしてみた。今回初めてトライしたのが、QRCode、webBrowser、画像編集のalphaBlend。
QRCode構成説明
QRCodeの詳しいことは知らないため、いい機会なのでサイトに簡単な説明があったので見てみた。誤り訂正もついている事に驚き。よく考えられているなーー。と勉強になった。
名称 | 内容 |
---|---|
最大データ量 | 数字:7,089字、英数字:4,296字、漢字:1,817字 |
ファインダパターン(切り出しシンボル) | QRコードの3コーナーに配置される3個(マイクロQRは1個)の位置検出用パターン |
アライメントパターン | 歪みによって生じる各セル(ドット)の位置ずれを補正 |
クワイエットゾーン | 2次元コードシンボルのまわりにある空白の部分。QRコードモデル1、モデル2で4セル分、マイクロQRコードで2セル分の空白が必要 |
タイミングパターン | 白セルと黒セルが交互に配置され、シンボル内のモジュール座標を決定するのに使用 |
フォーマット情報 | QRコードシンボルに、使用されている誤り訂正率とマスクパターンに関する情報 |
誤り訂正符号(リードソロモン符号) | QRコードの一部分が損傷した場合でもデータを損失することなく、復元することができるようにリードソロモン法を用いて生成された符号のこと。復元率は、シンボルの損傷の度合いに応じた4段階のレベル |
QRCode検出の簡単なソースイメージ
QRCode検出用のインスタンスを生成し、そのインスタンスからdetectAndDecodeメソッドを使用してスキャン。結果は以下が返却されるみたい。意外と簡単に実装できそう。
- 戻り値1:dataはスキャンした結果データ(文字列)、QRCode未の場合は空白。
- 戻り値2:pointsはQRCodeの座標情報。
# QRCodeDetectorインスタンス生成 QRDetector = cv2.QRCodeDetector() # QRCode検出 data, points,_ = QRDetector.detectAndDecode(image)
alphaBlend
alphaBlendとは、半透明化のような2つの画像を合成する。この画像編集もOpenCVの「cv2.addWeighted」で実現できる。よくスマートフォンアプリでQRCodeを映すカメラ画像の外枠が透けている感じイメージをPythonでトライしてみました。
処理概要
- カメラから画像をキャプチャ
- 黒背景画像とキャプチャ画像をalphaBlendの画像編集
- QRCodeスキャン用の四角枠をトリミングし、alphaBlend画像の上にペースト
- QRCodeスキャン用の四角枠の角近くを白線で点滅表示
- QRCodeスキャン用の四角枠の画像をQRCode検出メソッドに入力
- QRCode検出結果が「https://」の文字列がある場合は、検出したURLでwebBrowserでアクセス
環境条件
- MacBook Pro
- CPU:2.4 GHz クアッドコアIntel Core i5
- メモリ:8 GB 2133 MHz LPDDR3
- MacOS Catalina(10.15.4)
- Python 3.7.6
- Opencv 4.1.2
- VS code1.43.1
イメージ
ソースコード
無駄処理やバグがあるかも知れません。ご了承くださいませ。 ソースコード後半がQRCodeスキャンし、URLアクセス。 ソースコード前半のメソッド類は、QRCodeスキャン用のフォーム作成に関連するもの。
#!/usr/bin/env python # -*- coding: utf-8 -*- import cv2 import webbrowser import numpy as np from PIL import Image # URL保持情報初期化 url = "" # 画像サイズ _width = 640 _height = 480 # QRcodeサイズ _qrCodeWidth = 200 _qrCodeHeight = 200 # 白枠表示カウンタと非表示カウンタ _line_on_cnt = 3 _line_off_cnt = 7 #カメラデバイスオープン cap = cv2.VideoCapture(0) # カメラフレームサイズをに変更する cap.set(cv2.CAP_PROP_FRAME_WIDTH, _width) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, _height) # QRコードDetectorインスタンス生成 QRDetector = cv2.QRCodeDetector() ########################### # OPENCV -> PIL変換メソッド # ########################### def cv2pil(image_cv): image_cv = cv2.cvtColor(image_cv, cv2.COLOR_BGR2RGB) image_pil = Image.fromarray(image_cv) image_pil = image_pil.convert('RGB') return image_pil ########################### # PIL -> OPENCV変換メソッド # ########################### def pil2cv(image_pil): image_cv = np.asarray(image_pil) image_cv = cv2.cvtColor(image_cv, cv2.COLOR_RGB2BGR) return image_cv ######################### # 画像中心画像取得メソッド # ######################### def crop_center_img(pil_img, crop_width, crop_height): img_width, img_height = pil_img.size return pil_img.crop(((img_width - crop_width) // 2, (img_height - crop_height) // 2, (img_width + crop_width) // 2, (img_height + crop_height) // 2)) ########################## # 画像中心座標軸取得メソッド # ########################## def crop_center_pos(pil_img, crop_width, crop_height): img_width, img_height = pil_img.size x = img_width/2 - crop_width/2 y = img_height/2 - crop_height/2 return(x,y) ##################################### # QRコード検出用位置の角に線を引くメソッド # ##################################### def edge_line(image_cv,left_high_x,left_high_y,qr_width,qr_height): # 起点からの長さ用 line_width = 30 # 四角の外側のマージン margin = 10 ########################## # 左上のコーナーから横に線 # ########################## cv2.line(image_cv, pt1=(left_high_x-margin ,left_high_y-margin), pt2=(left_high_x-margin+line_width,left_high_y-margin), color=(255, 255, 255), thickness=2) ########################## # 左上のコーナーから下に線 # ########################## cv2.line(image_cv, pt1=(left_high_x-margin ,left_high_y-margin), pt2=(left_high_x-margin ,left_high_y-margin+line_width), color=(255, 255, 255), thickness=2) ########################## # 右上のコーナーから横に線 # ########################## cv2.line(image_cv, pt1=(left_high_x+qr_width+margin ,left_high_y-margin), pt2=(left_high_x+qr_width+margin-line_width,left_high_y-margin), color=(255, 255, 255), thickness=2) ########################## # 右上のコーナーから下に線 # ########################## cv2.line(image_cv, pt1=(left_high_x+qr_width+margin ,left_high_y-margin), pt2=(left_high_x+qr_width+margin ,left_high_y-margin+line_width), color=(255, 255, 255), thickness=2) ########################## # 左下のコーナーから横に線 # ########################## cv2.line(image_cv, pt1=(left_high_x-margin ,left_high_y+qr_height+margin), pt2=(left_high_x-margin+line_width ,left_high_y+qr_height+margin), color=(255, 255, 255), thickness=2) ########################## # 左下のコーナーから下に線 # ########################## cv2.line(image_cv, pt1=(left_high_x-margin ,left_high_y+qr_height+margin), pt2=(left_high_x-margin ,left_high_y+qr_height+margin-line_width), color=(255, 255, 255), thickness=2) ########################## # 右下のコーナーから横に線 # ########################## cv2.line(image_cv, pt1=(left_high_x+qr_width+margin ,left_high_y+qr_height+margin), pt2=(left_high_x+qr_width+margin-line_width,left_high_y+qr_height+margin), color=(255, 255, 255), thickness=2) ########################## # 右下のコーナーから下に線 # ########################## cv2.line(image_cv, pt1=(left_high_x+qr_width+margin ,left_high_y+qr_height+margin), pt2=(left_high_x+qr_width+margin ,left_high_y+qr_height+margin-line_width), color=(255, 255, 255), thickness=2) # 背景色黒作成 black_img_pil = Image.new(mode='RGB', size=(_width, _height)) black_img_cv = pil2cv(black_img_pil) # 白い枠点滅用カウンタ line_view = 0 # カメラデバイスオープン状態の時ループ while(cap.isOpened() == True): # カメラキャプチャ ret, frame_cv = cap.read() ################################ # QRコードスキャン用フォーム作成 # ################################ #アルファブレンド(黒背景とカメラ画像) alpha_blend_cv = cv2.addWeighted(black_img_cv, 0.85, frame_cv, 0.15, 1) # Opencv -> PILへ変換 frame_pil = cv2pil(frame_cv) alpha_blend_pil = cv2pil(alpha_blend_cv) # カメラキャプチャした中心画像のみ取得(QRコードのみ取得) cut_frame_pil = crop_center_img(frame_pil, _qrCodeWidth, _qrCodeHeight) # QRコードだけの画像をPIL -> Opencvへ変換(QRコード検出用) cut_frame_cv = pil2cv(cut_frame_pil) # 背景黒の中心座標位置を取得(imwshow表示用) x,y = crop_center_pos(alpha_blend_pil,_qrCodeWidth,_qrCodeHeight) # 背景黒イメージにカメラから切り取りした画像の張り付け(imshow用) alpha_blend_pil.paste(cut_frame_pil, (int(x), int(y))) # PIL -> Opencvへ変換(imshow表示用) QRcode_form = pil2cv(alpha_blend_pil) #################### # 白い枠表示/非表示 # #################### # 表示カウンタ以上 if line_view >=_line_on_cnt : # 白枠表示 edge_line(QRcode_form,int(x),int(y),_qrCodeWidth,_qrCodeHeight) line_view += 1 # 表示OFF用カウンタ以上 if line_view >= _line_off_cnt: # 表示カウンタクリア line_view = 0 ################################ # QRコード検出&URLアクセス # ################################ data, bbox,_ = QRDetector.detectAndDecode(cut_frame_cv) # データあり if len(data) > 0: # QRコードデータ表示 print("Decoded Data : {}".format(data)) # HTTPアクセス用のQRコードの場合 if data.find('https://') != -1 : # 違うURLの場合はブラウザアクセスする if url != data : # URL保存 url = data cv2.imwrite('QRcoee_image.png',QRcode_form) # 読み込んだURLでブラウザ起動 webbrowser.open_new(url) # ウィンドウに表示 cv2.namedWindow("QRCode",cv2.WINDOW_NORMAL) cv2.moveWindow("QRCode",200,100) # Window表示位置指定 cv2.resizeWindow('QRCode', 640,480) cv2.imshow('QRCode', QRcode_form) # ESCキーで終了 k = cv2.waitKey(10) & 0xff if k == 27: break # Do a bit of cleanup print("\n Exit Program") cap.release() cv2.destroyAllWindows()