テンプレートマッチングについて
OpenCVのテンプレートマッチングという機能を使って、数字認識を行います。先回の投稿で顔認識にトライしましたので、分類器自作も検討していますが、テンプレートマッチングについて知り、限定された条件下であれば文字認識等にも応用できると考え、今回 試してみることにしました。
まず、テンプレートマッチングについて整理します。テンプレートマッチングは、下図の①元画像から、②テンプレート画像を検索し、③検索結果の様に場所等を特定するロジックです。
①元画像
②テンプレート画像
③検索結果(赤枠部がテンプレート画像)
上記処理のプログラムを示します。
import cv2 try: i_orgn=cv2.imread('flower_all.jpg') i_tmpl=cv2.imread('flower_part.jpg') if i_orgn is None or i_tmpl is None: print('ファイルを読み込めません') import sys sys.exit() # リサイズ i_orgn = cv2.resize(i_orgn, (int(i_orgn.shape[1]*0.4), int(i_orgn.shape[0]*0.4))) i_tmpl = cv2.resize(i_tmpl, (int(i_tmpl.shape[1]*0.4), int(i_tmpl.shape[0]*0.4))) result= cv2.matchTemplate(i_orgn,i_tmpl,cv2.TM_CCOEFF_NORMED) mmr=cv2.minMaxLoc(result) pos=mmr[3] dst=i_orgn.copy() cv2.rectangle(dst,pos,(pos[0]+i_tmpl.shape[1],pos[1]+i_tmpl.shape[0]),(0,0,255),2) cv2.imshow('dst',dst) cv2.waitKey(0) cv2.destroyAllWindows() except: import sys print("Error:",sys.exc_info()[0]) print(sys.exc_info()[1]) import traceback print(traceback.format_tb(sys.exc_info()[2]))
数字認識(テンプレートマッチング)
① 認識対象
計測器等の現在値を記録する場合、パソコン等との通信インタフェースがあれば、電子的データ取得が可能ですが、インタフェースが無い場合、目で見て手書きすることになります。今回は下記の様なデジタル表示値を画像処理で文字データ等に変換し、電子的に記録することを想定しています。
但し、実際には上図の様に表示する表示器は持っていないので、容易に準備できるエクセルのシート上に1セル1文字中央配置表示し、ラズパイ4に接続するUSBカメラで撮像後、python + openCVでテンプレートマッチングを試みました。
下図はエクセルシートを直接キャプチャーしたものです。
② テンプレート画像作成
下の写真はUSBカメラで撮影したそのままの状態です。
下の図は撮影画像を二値化したものです。実際評価していませんが、距離や角度による大きさ・歪の影響等は除去できませんが、明るさ等の影響は取り除くことが出来そうなので、二値化処理後、テンプレートマッチングすることにしました。
二値化画像を切り抜いて、下図の様に数字の0~9に対応するテンプレート画像を作成します。例えば、“template8.jpg”ファイルは数字の “8” に対応しています。
テンプレートを作成する時に使ったプログラムです。
上段{0,1,2,3,4},下段{5,6,7,8,9} の順になっている前提で画像を切り抜き、保存します。X_S, WDT, Y_S, HGT, X_D, Y_D等の切り抜き位置,サイズのパターンを指定する変数は実際の撮影画像にあわせて調整が必要です。
#テンプレート作成 import sys import cv2 import math capture = cv2.VideoCapture(0) try: if capture.isOpened() is False: print("カメラを開けませんでした。") sys.exit() X_S=90 WDT=58 Y_S=119 HGT=88 X_D=98 Y_D=143 while True: ret, frame = capture.read() ret, frame = cv2.threshold(frame , 60, 255 , cv2.THRESH_BINARY) cv2.imwrite("img_org.jpg",frame) if cv2.waitKey(1) & 0xFF==ord('q'): break for i in range(10): T_XS = X_S + (i-5*math.floor(i/5)) * X_D T_YS = Y_S + Y_D * math.floor(i/5) T_XE = T_XS + WDT T_YE = T_YS + HGT img_crop=frame[T_YS:T_YE,T_XS:T_XE] cv2.imwrite("template"+str(i)+".jpg",img_crop) cv2.rectangle(frame,(T_XS,T_YS),(T_XE,T_YE),(255, 255, 0),thickness=1) cv2.imshow('frame',frame) capture.release() cv2.destroyAllWindows() except: print("Error:",sys.exc_info()[0]) print(sys.exc_info()[1]) import traceback print(traceback.format_tb(sys.exc_info()[2]))
③ テンプレートマッチング
次の動画は実際に文字列変換している様子です。
画面左側モニターのエクセルシート上の数字をラズパイ4に接続する手前USBカメラで撮影し、ラズパイ側でpython+OpenCVテンプレートマッチング機能を使って、文字列変換後、右側モニターに結果表示しています。赤字部分が変換した文字列をテキスト表示したものです。
処理の流れを概略説明します。下図の結果表示例に示す通り、10箇所ある表示位置(水色の枠内)を1ヶ所づつ、“0”~“9” に対応する全テンプレート画像のテンプレートマッチングを行い ます。一致レベルを示すスコアを取得できるので、最もスコア値が大きいテンプレートの数字をその位置の数字としています。
(赤字部分は変換文字列(結果)をテキスト表示)
ラズパイ側 python プログラムは次の通りです。
import sys import cv2 import math import time try: capture=cv2.VideoCapture(0) if capture.isOpened() is False: print("カメラを開けませんでした。") sys.exit() X_S=65 WDT=98 Y_S=85 HGT=145 X_D=100 Y_D=148 while(True): ret,frame=capture.read() ret,frame = cv2.threshold(frame , 60, 255 , cv2.THRESH_BINARY) getVal=["*","*","*","*","*","*","*","*","*","*"] for i in range(10): T_XS = X_S + (i-5*math.floor(i/5)) * X_D T_YS = Y_S + Y_D * math.floor(i/5) T_XE = T_XS + WDT T_YE = T_YS + HGT cv2.rectangle(frame,(T_XS,T_YS),(T_XE,T_YE),(255, 255, 0),thickness=1) img_crop = frame[T_YS:T_YE,T_XS:T_XE] maxVal_All = 0.7 num_dsp = -1 #0〜9のテンプレートと照合 for j in range(10): i_tmpl=cv2.imread("template"+str(j)+".jpg") result= cv2.matchTemplate(img_crop,i_tmpl,cv2.TM_CCOEFF_NORMED) mmr=cv2.minMaxLoc(result) maxVal=mmr[1] if maxVal > maxVal_All: num_dsp = j maxVal_All = maxVal if num_dsp != -1: getVal[i] = str(num_dsp) #print("Position "+str(i)+" : "+str(num_dsp)) #print(getVal) cv2.putText(frame, getVal[0]+getVal[1]+getVal[2]+getVal[3]+getVal[4], (X_S, Y_S-10), cv2.FONT_HERSHEY_SIMPLEX,2,(0,0,255),2,cv2.LINE_AA) cv2.putText(frame, getVal[5]+getVal[6]+getVal[7]+getVal[8]+getVal[9], (X_S, Y_S+HGT+Y_D+50), cv2.FONT_HERSHEY_SIMPLEX,2,(0,0,255),2,cv2.LINE_AA) cv2.imshow('frame',frame) if cv2.waitKey(1) & 0xFF==ord('q'): break #break #time.sleep(0.1) capture.release() cv2.destroyAllWindows() except: print("Error:",sys.exc_info()[0]) print(sys.exc_info()[1]) import traceback print(traceback.format_tb(sys.exc_info()[2]))
まとめ
結果が出るのに少し時間がかかりますが、私個人は許容範囲内です。機会があれば、実際にデジタル表示器で試してみたいと思います。ラズパイはこの様な処理を連続的にできるのか、という疑問を感じました。