【Python/OpenCV】カラートラッキングで振り子の運動を測定

Python+OpenCVのカラートラッキングで振り子の運動を測定する方法をソースコード付きで解説します。

Python+OpenCVで振り子の運動を測定

前回、ブロブ解析+カラートラッキングで物体追跡を行う方法を紹介しました。

【Python/OpenCV】ブロブ解析+カラートラッキングで物体追跡
Python+OpenCVのブロブ解析+カラートラッキングで物体追跡する方法をソースコード付きで解説します。

今回は、上記ページのプログラムを応用して、赤色の振り子を色追跡し、その中心座標を記録してグラフ化してみます。

今回解説するプログラムの実行結果

■赤色の振り子を追跡している様子

■グラフ化した結果

グラフの横軸は経過時間[sec]、縦軸は赤色物体の位置座標(x,y)[px]です。
振り子の周期運動を観測できていることがわかります。

サンプルコード①赤色の振り子を追跡し、中心座標を記録

赤色の振り子を追跡し、中心座標をテキストファイル(data.txt)に記録するサンプルプログラムのソースコードです。


サンプルコード②記録データをグラフ化

記録データ(data.txt)をグラフ化するサンプルプログラムのソースコードです。


サンプルコード①の詳細解説

サンプルコード①のの各部分を詳しく解説します。

インポートと初期設定

import cv2
import numpy import time
  • cv2: OpenCVライブラリを使用して画像処理を行います。OpenCVはコンピュータビジョンのための強力なツールです。
  • np: NumPyライブラリを使用して数値計算を行います。NumPyは配列操作や数値計算に便利です。
  • time: 時間計測を行います。プログラムの実行時間を取得し、座標と一緒に記録するために使用します。

赤色検出関数

def red_detect(img):
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    hsv_min = np.array([0,127,0])
    hsv_max = np.array([30,255,255])
    mask1 = cv2.inRange(hsv, hsv_min, hsv_max)
    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
  1. HSV色空間への変換:
    • cv2.cvtColor(img, cv2.COLOR_BGR2HSV)は、BGR色空間からHSV色空間に画像を変換します。HSV色空間は色相(Hue)、彩度(Saturation)、明度(Value)で色を表現します。
  2. 赤色の範囲を定義:
    • hsv_minhsv_maxは赤色の範囲を定義します。赤色は2つの範囲(0-30度と150-179度)で表現されます。
  3. マスクの作成:
    • cv2.inRange(hsv, hsv_min, hsv_max)は、指定した範囲内のピクセルを白(255)、それ以外を黒(0)にするマスクを作成します。
  4. マスクの合成:
    • mask1 + mask2で2つのマスクを合成し、最終的な赤色検出マスクを作成します。

ブロブ解析関数

def analysis_blob(binary_img):
    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
  1. ラベリング処理:
    • cv2.connectedComponentsWithStats(binary_img)は、2値画像の連結成分(ブロブ)をラベリングし、各ブロブの統計情報を取得します。
  2. 不要なデータの削除:
    • np.delete(label[2], 0, 0)np.delete(label[3], 0, 0)で背景のラベルを削除します。
  3. 最大ブロブの検出:
    • np.argmax(data[:, 4])で面積が最大のブロブのインデックスを取得します。
  4. 最大ブロブの情報取得:
    • 最大ブロブの左上座標、幅、高さ、面積、中心座標を取得し、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)
        data.append([time.time() - start, center_x, center_y])
        cv2.imshow("Frame", frame)
        cv2.imshow("Mask", mask)
        if cv2.waitKey(25) & 0xFF == ord('q'):
            break
    np.savetxt(csvfile_path, np.array(data), delimiter=",")
    cap.release()
    cv2.destroyAllWindows()
  1. データ格納用リストの初期化:
    • data = []で追跡データを格納するリストを初期化します。
  2. 動画ファイルとCSVファイルのパス設定:
    • videofile_pathcsvfile_pathで動画ファイルとCSVファイルのパスを設定します。
  3. 動画キャプチャの開始:
    • cv2.VideoCapture(videofile_path)で動画ファイルを読み込みます。
  4. 開始時間の記録:
    • start = time.time()でプログラムの開始時間を記録します。
  5. フレームごとの処理:
    • while(cap.isOpened())で動画が終了するまでフレームを読み込みます。
    • ret, frame = cap.read()でフレームを取得します。
    • mask = red_detect(frame)で赤色物体を検出します。
    • target = analysis_blob(mask)で最大ブロブの情報を取得します。
    • center_xcenter_yでブロブの中心座標を取得します。
    • cv2.circle(frame, (center_x, center_y), 30, (0, 200, 0), thickness=3, lineType=cv2.LINE_AA)でフレームに円を描画します。
    • data.append([time.time() - start, center_x, center_y])で経過時間と中心座標をリストに追加します。
    • cv2.imshow("Frame", frame)cv2.imshow("Mask", mask)でフレームとマスクを表示します。
    • if cv2.waitKey(25) & 0xFF == ord('q')で’q’キーが押されたらループを終了します。
  6. データの保存とリソースの解放:
    • np.savetxt(csvfile_path, np.array(data), delimiter=",")でデータをCSVファイルに保存します。
    • cap.release()でキャプチャを解放し、cv2.destroyAllWindows()でウィンドウを閉じます。

サンプルコード②の詳細解説

サンプルコード②は、サンプルコード①で記録した赤色物体の動きをCSVファイルから読み込み、時間に対するx座標とy座標の変化をグラフにプロットするものです。
以下に各部分の詳細な解説をします。

インポートと初期設定

import numpy as np
import matplotlib.pyplot as plt
  • numpy: 数値計算ライブラリで、配列操作や数値計算に使用します。
  • matplotlib.pyplot: グラフ描画ライブラリで、データの可視化に使用します。

メイン関数

def main():
    data = np.genfromtxt("/Users/github/sample/python/opencv/video/color_tracking/data.csv", delimiter=",", dtype='float')
  • np.genfromtxt: CSVファイルを読み込み、NumPy配列としてデータを取得します。delimiter=","はカンマ区切りを指定し、dtype='float'はデータ型を浮動小数点数に指定します。

データの分割

    t = data[:,0]
    x = data[:,1]
    y = data[:,2]
  • data[:,0]: 経過時間のデータを取得します。
  • data[:,1]: x座標のデータを取得します。
  • data[:,2]: y座標のデータを取得します。

グラフのプロット

    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)
    plt.ylabel("Position[px]", fontsize=16)
    plt.grid()
    plt.legend(loc=1, fontsize=16)
    plt.show()
  1. フォントの設定:
    • plt.rcParams["font.family"] = "Times New Roman"でグラフのフォントをTimes New Romanに設定します。
  2. データのプロット:
    • plt.plot(t, x, "r-", label="x")で経過時間に対するx座標を赤色の線でプロットします。
    • plt.plot(t, y, "b-", label="y")で経過時間に対するy座標を青色の線でプロットします。
  3. 軸ラベルの設定:
    • plt.xlabel("Time[sec]", fontsize=16)でx軸のラベルを設定します。
    • plt.ylabel("Position[px]", fontsize=16)でy軸のラベルを設定します。
  4. グリッドの表示:
    • plt.grid()でグラフにグリッドを表示します。
  5. 凡例の表示:
    • plt.legend(loc=1, fontsize=16)で凡例を表示します。loc=1は凡例の位置を右上に設定します。
  6. グラフの表示:
    • plt.show()でグラフを表示します。

プログラムの実行

if __name__ == "__main__":
    main()
  • この部分は、スクリプトが直接実行された場合にmain()関数を呼び出します。

関連ページ

【Python版OpenCV超入門】使い方とサンプルコードを解説
Python版OpenCVで画像処理プログラミングを行う方法を入門者向けにソースコード付きで解説するページです。
【画像処理入門】アルゴリズム&プログラミング
画像処理における基本的なアルゴリズムとその実装例(プログラム)についてまとめました。

コメント