Python版OpenCVでカラートラッキング(色追跡)を実装し、移動物体を検出する方法をソースコード付きで解説します。
【色追跡とは】動画から特定の色を検出
カラートラッキング(色追跡)は、その名の通り、特定の色のみを検出して追跡します。
カラートラッキングには、通常RGB色空間ではなく、同系統の色の範囲を数値で指定しやすいHSV色空間を用います。
– | 関連記事 |
---|---|
HSVの原理 | HSV色空間の原理・特徴・計算式 |
色追跡の原理 | HSV色空間とカラートラッキングによる物体追跡の原理 |
今回は、PythonとOpenCVでカラートラッキング(色追跡)を実装してみました。
動画解説
本ページの内容は動画でも紹介しています。
HSV色空間の赤色
– | 赤色の範囲 | 赤色の範囲(OpenCVのHSV色空間) |
---|---|---|
H | 0~60, 300~360[度] | 0~30, 150~179 |
S | 50~100[%] | 128~255 |
V | 00~100[%] | 0~255 |
サンプルコード
サンプルプログラムのソースコードです。
# -*- coding: utf-8 -*- import cv2 import numpy as np def red_detect(img): # HSV色空間に変換 hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 赤色のHSVの値域1 hsv_min = np.array([0,127,0]) hsv_max = np.array([30,255,255]) mask1 = cv2.inRange(hsv, hsv_min, hsv_max) # 赤色のHSVの値域2 hsv_min = np.array([150,127,0]) hsv_max = np.array([179,255,255]) mask2 = cv2.inRange(hsv, hsv_min, hsv_max) return mask1 + mask2 def main(): videofile_path = "C:/github/sample/python/opencv/video/color_tracking/red_pendulum.mp4" # カメラのキャプチャ cap = cv2.VideoCapture(videofile_path) while(cap.isOpened()): # フレームを取得 ret, frame = cap.read() # 赤色検出 mask = red_detect(frame) # 結果表示 cv2.imshow("Frame", frame) cv2.imshow("Mask", mask) # qキーが押されたら途中終了 if cv2.waitKey(25) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows() if __name__ == '__main__': main()
実行結果
サンプルプログラムの実行結果です。
【ブロブ解析】最も大きい赤色領域の座標を取得して追跡
画像をラベリング処理し、ラベル付けされた領域の特徴を解析することをブロブ解析といいます。
Python版OpenCVでは、cv2.connectedComponentsWithStats()で2値画像のブロブ解析ができます。
つまり、マスク画像を与えてやると、面積が最大の領域のみを取り出したりできます。
これを応用すれば、動画に複数の赤色物体が映っていても、一番大きな赤色物体だけを追跡したりできます。
– | ブロブ解析の詳細はこちら |
---|---|
1 | 【Python/OpenCV】最大面積のブロブ解析(座標・大きさなど) |
サンプルコード
# -*- coding: utf-8 -*- import cv2 import numpy as np def red_detect(img): # HSV色空間に変換 hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 赤色のHSVの値域1 hsv_min = np.array([0,127,0]) hsv_max = np.array([30,255,255]) mask1 = cv2.inRange(hsv, hsv_min, hsv_max) # 赤色のHSVの値域2 hsv_min = np.array([150,127,0]) hsv_max = np.array([179,255,255]) mask2 = cv2.inRange(hsv, hsv_min, hsv_max) return mask1 + mask2 # ブロブ解析 def analysis_blob(binary_img): # 2値画像のラベリング処理 label = cv2.connectedComponentsWithStats(binary_img) # ブロブ情報を項目別に抽出 n = label[0] - 1 data = np.delete(label[2], 0, 0) center = np.delete(label[3], 0, 0) # ブロブ面積最大のインデックス max_index = np.argmax(data[:, 4]) # 面積最大ブロブの情報格納用 maxblob = {} # 面積最大ブロブの各種情報を取得 maxblob["upper_left"] = (data[:, 0][max_index], data[:, 1][max_index]) # 左上座標 maxblob["width"] = data[:, 2][max_index] # 幅 maxblob["height"] = data[:, 3][max_index] # 高さ maxblob["area"] = data[:, 4][max_index] # 面積 maxblob["center"] = center[max_index] # 中心座標 return maxblob def main(): videofile_path = "C:/github/sample/python/opencv/video/color_tracking/red_pendulum.mp4" # カメラのキャプチャ cap = cv2.VideoCapture(videofile_path) while(cap.isOpened()): # フレームを取得 ret, frame = cap.read() # 赤色検出 mask = red_detect(frame) # マスク画像をブロブ解析(面積最大のブロブ情報を取得) target = analysis_blob(mask) # 面積最大ブロブの中心座標を取得 center_x = int(target["center"][0]) center_y = int(target["center"][1]) # フレームに面積最大ブロブの中心周囲を円で描く cv2.circle(frame, (center_x, center_y), 30, (0, 200, 0), thickness=3, lineType=cv2.LINE_AA) # 結果表示 cv2.imshow("Frame", frame) cv2.imshow("Mask", mask) # qキーが押されたら途中終了 if cv2.waitKey(25) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows() if __name__ == '__main__': main()
実行結果
【応用①】振り子の運動を観測
赤色の振り子を色追跡し、その中心座標を記録してグラフ化してみます。
# -*- coding: utf-8 -*- import cv2 import numpy as np import time def red_detect(img): # HSV色空間に変換 hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 赤色のHSVの値域1 hsv_min = np.array([0,127,0]) hsv_max = np.array([30,255,255]) mask1 = cv2.inRange(hsv, hsv_min, hsv_max) # 赤色のHSVの値域2 hsv_min = np.array([150,127,0]) hsv_max = np.array([179,255,255]) mask2 = cv2.inRange(hsv, hsv_min, hsv_max) return mask1 + mask2 # ブロブ解析 def analysis_blob(binary_img): # 2値画像のラベリング処理 label = cv2.connectedComponentsWithStats(binary_img) # ブロブ情報を項目別に抽出 n = label[0] - 1 data = np.delete(label[2], 0, 0) center = np.delete(label[3], 0, 0) # ブロブ面積最大のインデックス max_index = np.argmax(data[:, 4]) # 面積最大ブロブの情報格納用 maxblob = {} # 面積最大ブロブの各種情報を取得 maxblob["upper_left"] = (data[:, 0][max_index], data[:, 1][max_index]) # 左上座標 maxblob["width"] = data[:, 2][max_index] # 幅 maxblob["height"] = data[:, 3][max_index] # 高さ maxblob["area"] = data[:, 4][max_index] # 面積 maxblob["center"] = center[max_index] # 中心座標 return maxblob def main(): # データ格納用のリスト data = [] # 動画ファイルのパス videofile_path = "C:/github/sample/python/opencv/video/color_tracking/red_pendulum.mp4" # 記録データの保存先パス csvfile_path = "C:/github/sample/python/opencv/video/color_tracking/data.csv" # カメラのキャプチャ cap = cv2.VideoCapture(videofile_path) # 開始時間 start = time.time() while(cap.isOpened()): # フレームを取得 ret, frame = cap.read() # カラートラッキング(赤色) mask = red_detect(frame) # マスク画像をブロブ解析(面積最大のブロブ情報を取得) target = analysis_blob(mask) # 面積最大ブロブの中心座標を取得 center_x = int(target["center"][0]) center_y = int(target["center"][1]) # フレームに面積最大ブロブの中心周囲を円で描く cv2.circle(frame, (center_x, center_y), 30, (0, 200, 0), thickness=3, lineType=cv2.LINE_AA) # 経過時間, x, yをリストに追加 data.append([time.time() - start, center_x, center_y]) # ウィンドウ表示 cv2.imshow("Frame", frame) cv2.imshow("Mask", mask) # qキーが押されたら途中終了 if cv2.waitKey(25) & 0xFF == ord('q'): break # CSVファイルに保存 np.savetxt(csvfile_path, np.array(data), delimiter=",") # キャプチャ解放・ウィンドウ廃棄 cap.release() cv2.destroyAllWindows() if __name__ == '__main__': main()
■記録データをグラフ化
# -*- coding: utf-8 import numpy as np import matplotlib.pyplot as plt def main(): # CSVのロード data = np.genfromtxt( "C:/github/sample/python/opencv/video/color_tracking/data.csv", delimiter=",", dtype='float') # 2次元配列を分割(経過時間t, x座標, y座標の1次元配列) t = data[:,0] x = data[:,1] y = data[:,2] # グラフにプロット plt.rcParams["font.family"] = "Times New Roman" # フォントの種類 plt.plot(t, x, "r-", label="x") plt.plot(t, y, "b-", label="y") plt.xlabel("Time[sec]", fontsize=16) # x軸ラベル plt.ylabel("Position[px]", fontsize=16) # y軸ラベル plt.grid() # グリッド表示 plt.legend(loc=1, fontsize=16) # 凡例表示 plt.show() if __name__ == "__main__": main()
実行結果
サンプルプログラムの実行結果です。
■カメラ映像
■グラフ化
グラフの横軸は経過時間[sec]、縦軸は赤色物体の位置座標(x,y)[px]です。
振り子の周期運動を観測できていることがわかります。
– | 関連記事 |
---|---|
1 | PythonでOpenCV入門 サンプル集 |
2 | 【Python】画像処理プログラミング入門 |
3 | 【画像処理入門】アルゴリズム&プログラミング |
コメント