C++
 Computer >> コンピューター >  >> プログラミング >> C++

OpenCVとC++を使った色追跡の実装方法:HSV変換から軌跡の描画まで


色追跡(カラートラッキング)は、色検出とよく似た画像処理です。追跡を実現するには、検出したオブジェクトの面積を計算し、その領域の現在位置を求める処理を数行追加します。最後に、OpenCVのline()関数を使って、オブジェクトが移動した軌跡を線として描画します。

この記事では、トラックバーでHSVのしきい値をリアルタイムに調整しながら、Webカメラの映像から特定色のオブジェクトを検出し、その動きを追跡する方法を解説します。

色追跡の基本的な流れ

  1. 映像の取得:VideoCaptureクラスでWebカメラからフレームを取得します。
  2. HSVへの変換:cvtColor()関数でBGR画像をHSV形式に変換します。HSVは色相・彩度・明度の3要素で色を表すため、照明の変化の影響を受けにくく、特定色の検出に適しています。
  3. 色範囲の抽出:inRange()関数とcreateTrackbar()で作成したトラックバーにより、指定したHSV範囲内の色だけを残します。
  4. ノイズ除去:erode()とdilate()によるモルフォロジー演算(オープニングとクロージング)で、小さなノイズを取り除き、検出領域の小さな穴を埋めます。
  5. 重心の計算:moments()関数で画像モーメントを求め、m00(面積)、m01・m10からオブジェクトの重心座標を計算します。
  6. 軌跡の描画:前回の座標と現在の座標を結ぶ赤い線をline()関数で描き、オブジェクトの動きを可視化します。

サンプルコード

#include<iostream>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;
int main(int argc, char** argv) {
    VideoCapture video_load(0);//デフォルトカメラから映像を取得//
    namedWindow("Adjust");//画像を表示するウィンドウを作成//
    int Hue_Low = 0;//色相(Hue)の下限//
    int Hue_high = 22;//色相(Hue)の上限//
    int Sat_Low = 99;//彩度(Saturation)の下限//
    int Sat_high = 255;//彩度(Saturation)の上限//
    int Val_Low = 0;//明度(Value)の下限//
    int Val_high = 255;//明度(Value)の上限//
    createTrackbar("LowH", "Adjust", &Hue_Low, 179);//最小色相を調整するトラックバー//
    createTrackbar("HighH", "Adjust", &Hue_high, 179);//最大色相を調整するトラックバー//
    createTrackbar("LowS", "Adjust", &Sat_Low, 255);//最小彩度を調整するトラックバー//
    createTrackbar("HighS", "Adjust", &Sat_high, 255);//最大彩度を調整するトラックバー//
    createTrackbar("LowV", "Adjust", &Val_Low, 255);//最小明度を調整するトラックバー//
    createTrackbar("HighV", "Adjust", &Val_high, 255);//最大明度を調整するトラックバー//
    int Horizontal_Last = -1;//初期の水平方向位置//
    int vertical_Last = -1;//初期の垂直方向位置//
    Mat temp;//映像ストリームからフレームを読み込むための行列//
    video_load.read(temp);//映像ストリームからフレームを読み込み//
    Mat track_motion = Mat::zeros(temp.size(), CV_8UC3);//検出用の黒い行列を作成//
    while (true) {
        Mat actual_Image;//実際の画像を格納する行列//
        bool temp_load = video_load.read(actual_Image);//映像からフレームを読み込む//
        Mat converted_to_HSV;//変換後の画像を保存する行列//
        cvtColor(actual_Image, converted_to_HSV, COLOR_BGR2HSV);//BGR画像をHSVに変換//
        Mat adjusted_frame;//検出した色を格納する行列//
        inRange(converted_to_HSV, Scalar(Hue_Low, Sat_Low, Val_Low),
        Scalar(Hue_high, Sat_high, Val_high), adjusted_frame);//トラックバーの値を反映//
        erode(adjusted_frame, adjusted_frame, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));//モルフォロジーオープニングで前景の小さな物体を除去//
        dilate(adjusted_frame, adjusted_frame, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));//モルフォロジーオープニングで前景の小さな物体を除去//
        dilate(adjusted_frame, adjusted_frame, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));//モルフォロジークロージングで前景の小さな穴を埋める//
        erode(adjusted_frame, adjusted_frame, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));//モルフォロジークロージングで前景の小さな穴を埋める//
        Moments detecting_object = moments(adjusted_frame);//検出した色のフレームからモーメントを生成//
        double vertical_moment = detecting_object.m01;//垂直方向の位置を取得//
        double horizontal_moment = detecting_object.m10;//水平方向の位置を取得//
        double tracking_area = detecting_object.m00;//オブジェクトの面積を取得//
        if (tracking_area > 10000) {//オブジェクトの面積が10000ピクセルより大きい場合//
            int posX = horizontal_moment / tracking_area;//オブジェクトの水平位置を計算//
            int posY = vertical_moment / tracking_area;//オブジェクトの垂直位置を計算//
            if (Horizontal_Last >= 0 && vertical_Last >= 0 && posX >= 0 && posY >= 0) {//検出されたオブジェクトが移動している場合//
                line(track_motion, Point(posX, posY), Point(Horizontal_Last, vertical_Last), Scalar(0, 0, 255), 2);//検出オブジェクトの移動経路に赤い線を描画//
            }
            Horizontal_Last = posX;//新しい水平位置を記録//
            vertical_Last = posY;//新しい垂直位置を記録//
        }
        imshow("Detected_Object", adjusted_frame);//検出したオブジェクトを表示//
        actual_Image = actual_Image + track_motion;//元の映像フレームに連続した線を重ねて描画//
        imshow("Actual", actual_Image);//元の映像を表示//
        cout << "position of the object is:" << Horizontal_Last << "," << vertical_Last << endl;//追跡した座標値を表示//
        if (waitKey(30) == 27) {//Escキーが押されるとループを抜ける//
            cout << "esc key is pressed by user" << endl;
            break;
        }
    }
    return 0;
}

実行結果

プログラムを実行すると、「Adjust」ウィンドウ上のトラックバーで色相・彩度・明度の範囲を調整できます。検出条件を満たすオブジェクトが画面内で動くと、元の映像上にその移動経路が赤い線で次々と描かれていきます。また、コンソールにはオブジェクトの現在座標が随時出力されます。

OpenCVとC++を使った色追跡の実装方法:HSV変換から軌跡の描画まで


OpenCVとC++を使った色追跡の実装方法:HSV変換から軌跡の描画まで

まとめ

本サンプルでは、HSV色空間での閾値処理と画像モーメントを組み合わせることで、シンプルながら実用的な色追跡を実現しています。面積が10000ピクセルを超える場合のみ追跡することで、誤検出を抑制している点もポイントです。Escキーを押すとループが終了し、プログラムが終了します。トラックバーの値を調整すれば、さまざまな色のオブジェクトに柔軟に対応できます。

  1. C++とOpenCVで色空間を変換する方法!cvtColor()関数の使い方を解説

    色空間とは 色空間(Color Space)とは、色を表現するためのモデルのことです。色の記述方法にはさまざまな種類があり、代表的なものとしてRGB、CMYK、HSV、グレースケールなどが挙げられます。 imgproc.hppヘッダーについて 本記事では、「imgproc.hpp」というヘッダーを使用します。この名前は「Image Processing(画像処理)」の略です。OpenCVで色空間を変換するには、「cvtColor()」関数を使用する必要があります。この関数は「imgproc」ヘッダーファイル内で定義されているため、あらかじめ「imgproc.hpp」をインクルードしておきまし

  2. C++とOpenCVで画像のチャンネル数を取得する方法を解説

    この記事では、OpenCVを使って画像のチャンネル数を調べる方法について解説します。プログラムを実行すると、コンソール画面に画像のチャンネル数が表示されます。チャンネル数を取得するには、OpenCVが提供するchannels()メソッドを使用します。このメソッドは、Matクラスのオブジェクト(画像行列)に対して呼び出すことで、その画像のチャンネル数を整数値として返します。例えば、カラー画像の場合は通常3チャンネル(BGR)、グレースケール画像の場合は1チャンネルとなります。画像処理を行う際、扱っている画像がカラーかモノクロかを判別するために、チャンネル数の確認は非常に重要な処理の一つです。以下