はじめまして、AMDlabの番匠です。
今回は、OpenCVを使って動画の中に特定のパターンを探しその秒数を特定する方法を紹介したいと思います。
まず、OpenCVとは画像(動画)処理のためのオープンソースライブラリです。
公式サポート言語は
- Python
- C++
- C
- Java
となっています。今回はPythonを使っていきます。
動画の中から特定のパターンを見つける
動画からのパターンマッチングは以下の流れで行います。
- 動画を一定の間隔で画像にする。
- 画像の情報量を減らす。
- パターンマッチングを行う。
下準備をする
以下のコードで1と2を行いパターンマッチングの下準備をします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
import cv2 import os # 1秒ごとにフレーム分け def save_sec_interval(video_path): interval_sec = 1 # OpenCVで動画を開く cap = cv2.VideoCapture(video_path) if not cap.isOpened(): return # 作業ディレクトリ作成 os.makedirs('./workdir', exist_ok=True) # 動画のfps(動画1秒間に何枚のフレームが使われているか)を取得 fps = cap.get(cv2.CAP_PROP_FPS) # 総フレーム数取得 frame = cap.get(cv2.CAP_PROP_FRAME_COUNT) # 動画総秒数取得 len_sec = round(frame / fps) for i in range(1, len_sec, interval_sec): # 取得するフレーム位置セット cap.set(cv2.CAP_PROP_POS_FRAMES, round(fps * i)) # 動画取得 ret, frame = cap.read() if ret: result_path = os.path.join('workdir', str(i) + '.jpg') # パターンが現れる位置が決まっているならその位置を残してできる限り小さくトリムする frame_trim = trim_img_ratio(frame) # フレームをカラーからグレースケールに処理し情報量する frame_trim_gray = cv2.cvtColor(frame_trim, cv2.COLOR_BGR2GRAY) # グレースケールから白黒の2値に処理する(2値化) _, frame_trim_binary = cv2.threshold(frame_trim_gray, 0, 255, cv2.THRESH_OTSU) # 処理したフレームを書き出し cv2.imwrite(result_path, frame_trim_binary) def trim_img_ratio(img): # 下記パラメータは試行してトリム位置を調整 left = 0.35 right = 0.35 top = 0.42 down = 0.45 left_edge = int(img.shape[1] * left) right_edge = int(img.shape[1] - img.shape[1] * right) top_edge = int(img.shape[0] * top) down_edge = int(img.shape[0] - img.shape[0] * down) return img[top_edge:down_edge, left_edge:right_edge] |
OpenCVで動画をinterval_sec秒毎のフレーム(フルカラー)からグレーのグラデーションに加工し、さらにそれを閾値を境に白黒に加工し出力しています。
閾値の設定はいろいろありますが、今回は画像の特徴が出やすい閾値を算出してくれる大津の2値化の手法を利用します。
34 |
_, frame_trim_binary = cv2.threshold(frame_trim_gray, 0, 255, cv2.THRESH_OTSU) |
画像の情報量が多かったり動画時間が長いと処理時間が伸びてしまいますのでなるべくトリムなどをしましょう・
次に出力した画像ごとにパターンマッチングを行います。
パターンマッチングを行う
ではいよいよ画像のパターンマッチングを行います。
1 2 3 4 5 6 7 8 9 10 |
# マッチング判定 def judge_match(frame_path, template_path): template = cv2.imread(template_path) # 検索元画像ファイルパスを指定 frame = cv2.imread(frame_path) # 検索先画像のファイルパスを指定 # パターンマッチング実施 result = cv2.matchTemplate(frame, template, cv2.TM_CCORR_NORMED) # マッチング結果を類似度最小、最大、最小の座標、最大の座標 minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(result) return if 0.90 < maxVal |
第一引数、第二引数にテンプレートとなる画像、検出対象の物体を探すフレームを設定し、第三引数に類似度の計算方法を設定します。
5 6 |
# パターンマッチング実施 result = cv2.matchTemplate(frame, template, cv2.TM_CCORR_NORMED) |
マッチング結果をパースし類似度最大箇所が90%を越えるかどうかで判断します。
では、例でインベーダーゲームの自機の位置を探してみましょう!
入力画像はこちらです
ここから緑の自機を探してみようと思います。
テンプレート画像
検索結果ヒートマップ
赤黒に近いところほどマッチ度が高くなります。
1 2 3 4 |
print(f"類似度最大: {maxVal}") # 類似度最大: 0.9967182874679565 print(f"最大の場所: {maxLoc}") # 最大の場所: (207, 332) |
閾値を95%に設定し、マッチした箇所を囲ってみましょう
自機を三機見つけられました!
いかがでしょうか。今回は動画のパターンマッチングを例に取り上げましたが、OpenCVには他にも顔認識、文字認識など画像処理を簡単に扱える機能が搭載されていますので是非使ってみてください!
COMMENTS