Amazonで買えるUSBカメラと、OpenCVのテンプレートマッチングでリアルタイム画像認識するやり方をご紹介したいと思います。Pythonプログラムです。
とても簡単に実装できてしまいます。
すごいぞ!OpenCV!
やること
・USBカメラをキャプチャする
・リアルタイム表示する
・テンプレートマッチングする
・結果を矩形表示する
それでは、順に見ていきましょう!
やること
OpenCV(Open Source Computer Vision Library)は、Intelが開発した、
コンピュータビジョンタスクを簡単に実行するためのオープンソースのライブラリです。
画像処理、パターン認識、機械学習などの機能を提供し、画像や動画データに対する高度な処理や解析が可能です。PythonやC++などの言語で使用でき、コンピュータビジョンアプリケーションの開発に広く活用されます。
顔検出、オブジェクトトラッキング、画像フィルタリングなど多岐にわたるアプリケーションで使用され、
研究から産業応用まで幅広い領域で利用されています
今回はテンプレートマッチングを利用します。
ちなみに、テストした環境はWindows10です。
実際に使用したUSBカメラはこちらです。
たまたま過去に購入したのものが上記なのですが、最近は次の製品も安価で人気のようです。
これらのUSBカメラを使う想定です。
詳しく見ていきましょう!
USBカメラをキャプチャする
OpenCVが無いと始まりません。
まずはライブラリをインストールします。
後ほどnumpyも利用します。インストールしておきます。
pip install opencv-python
pip install numpy
USBカメラデバイスをOpenCVで開きます。
import cv2
# カメラデバイスをオープンします。デバイス番号は通常0です。
cap = cv2.VideoCapture(0)
カメラデバイスが複数ある場合は、引数を例えば1に変えます。
とりあえず動作には、数を~9で適当に変えて調べるだけで何とかなります。
無事にオープンできたら、フレームをキャプチャします。
ret, frame = cap.read()
cv2.imshow("USB camera", frame)
リアルタイム表示する
先ほどの流れを繰り返し処理にします。
デバイスオープンしたらクローズも必要になります。
リアルタイム表示するところまでのサンプルが次です。
import cv2
# カメラデバイスをオープンします。デバイス番号は通常0です。
cap = cv2.VideoCapture(0)
# カメラが正常にオープンされたかチェックします。
if not cap.isOpened():
print("カメラがオープンできませんでした。")
exit()
while True:
# フレームをキャプチャします。
ret, frame = cap.read()
# フレームのキャプチャが成功したかチェックします。
if not ret:
print("フレームをキャプチャできませんでした。")
break
# フレームを表示します。
cv2.imshow("USB camera", frame)
# 'q'キーを押すとループを終了します。
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# カメラデバイスを解放します。
cap.release()
# ウィンドウを閉じます。
cv2.destroyAllWindows()
コマンドプロンプトやターミナルなどで実行すると次のようになります。
ウィンドウが開き、USBカメラで撮影した画像がリアルタイム表示されます。
上記のサンプルコードでは「q」キーを押すと終了します。
quitですね。
テンプレートマッチングする
それでは、リアルタイム表示される画像に対して、テンプレートマッチングしてみましょう。
テンプレートマッチングは画像処理技術です。
ある画像(テンプレート)が別の画像内でどこに位置するかを見つけるために使用されます。
テンプレート画像とリアルタイム画像を比較し、一致度を評価します。
スコアが最も高い場所が、テンプレートの位置とみなされます。
import cv2
import numpy as np
gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # カメラ画像をグレースケールに変換
# テンプレートマッチングを行う
result = cv2.matchTemplate(gray_frame, template, cv2.TM_CCOEFF_NORMED)
「cv2.TM_CCOEFF_NORMED」は、テンプレートマッチングの方法を指定しています。
相関係数を用いた評価方法です。
テンプレートと対象画像を比較し、各位置で計算されます。
相関係数は 1に近いほど類似度が高いという意味です。
この計算結果が「result」に格納されます。
OpenCVだとたった1行ですね!
結果を矩形表示する
計算結果だけですと、カメラ画像のどこで類似度が高かったかを視覚的に理解できません。
分かるように、座標を取得し、矩形表示したいと思います。
threshold = 0.7 # 閾値を設定
loc = np.where(result >= threshold)
for pt in zip(*loc[::-1]):
cv2.rectangle(frame, pt, (pt[0] + template.shape[1], pt[1] + template.shape[0]), (0, 255, 0), 2)
# 出力画像を表示
cv2.imshow("USB camera", frame)
どれぐらいの類似度だと、一致とみなすか閾値を設定します。
「loc = np.where(result >= threshold)」で閾値を超えた座標を取得しています。
「cv2.rectangle(frame, ~」部分では、対象画像に矩形表示しています。
対象画像は「frame」で、先ほどUSBカメラのフレームをキャプチャしたものです。
「for pt in zip~」からは、矩形で書くための左上の座標と右下の座標を決めています。
最後の(0, 255,0)からは矩形表示をどうするか指定しています。緑で2pt線です。
結果がこのようになります。
今回はラズパイ4の箱でテストしてみました。
判定の結果、一致とみなされた位置が緑で矩形表示されています。
うまく検出できていますね!
サンプルコード
それでは、ここまでのコードを合体して、サンプルコードにしたいと思います。
import cv2
import numpy as np
# テンプレート画像とカメラ画像の読み込み
template = cv2.imread("template.png", cv2.IMREAD_GRAYSCALE) # テンプレート画像(グレースケール)
cap = cv2.VideoCapture(0) # カメラ番号(通常は0)
# カメラが正常にオープンされたかチェックします。
if not cap.isOpened():
print("カメラがオープンできませんでした。")
exit()
while True:
ret, frame = cap.read() # カメラ画像をキャプチャ
if not ret:
print("フレームをキャプチャできませんでした。")
break
gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # カメラ画像をグレースケールに変換
# テンプレートマッチングを行う
result = cv2.matchTemplate(gray_frame, template, cv2.TM_CCOEFF_NORMED)
threshold = 0.7 # 閾値を設定
loc = np.where(result >= threshold)
for pt in zip(*loc[::-1]):
cv2.rectangle(frame, pt, (pt[0] + template.shape[1], pt[1] + template.shape[0]), (0, 255, 0), 2)
# 出力画像を表示
cv2.imshow("USB camera", frame)
# 'q' キーを押すとループを終了
if cv2.waitKey(1) & 0xFF == ord("q"):
break
# カメラデバイスを解放します。
cap.release()
# ウィンドウを閉じます。
cv2.destroyAllWindows()
注意点がいくつかあります。
・USBカメラのID(cv2.VideoCapture(0) 部分の「0」)はご自身の環境に合わせる必要があります。
・テンプレート画像(”template.png”)は、ご自身で準備する必要があります。
USBカメラをキャプチャして、対象したいものに合わせてトリミングなどで大丈夫です。
こんな感じで、うまく検出できるとともに、
ちょっと寄ったり離したりしたら見つけられない限界も感じるのではと思います。
人だと認識できるのに、機械だと見つけられないギャップです。
このあたりの課題は、カスケード分類などで機械学習を用いてたり、応用があります。
YOLOのような物体検出、Kerasのようなアプローチも考えられます。
ステップアップして、これらの記事を書けたらな~なんて思っています。
今回は以上となります。
この記事が少しでもお役に立てれば幸いです。
ここまで読んで頂きありがとうございました!