BETA

OpenCVでQRCodeスキャンをやってみた

投稿日:2020-05-06
最終更新:2020-05-06

概要

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でトライしてみました。

処理概要

  1. カメラから画像をキャプチャ
  2. 黒背景画像とキャプチャ画像をalphaBlendの画像編集
  3. QRCodeスキャン用の四角枠をトリミングし、alphaBlend画像の上にペースト
  4. QRCodeスキャン用の四角枠の角近くを白線で点滅表示
  5. QRCodeスキャン用の四角枠の画像をQRCode検出メソッドに入力
  6. 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()  
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

@主にプログラム技術ブログ

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう