概要
このプロジェクトで書いたコードをすべて以下に示します:
#include <opencv2/opencv.hpp>
bool selectObject = false; // オブジェクトが選択されているかどうかを表す
int trackObject = 0; // 1 はトラッキング対象があることを意味し、0 は対象がないことを意味し、-1 はカムシフト特性が計算されていないことを意味します
cv::Rect selection; // マウスによって選択された領域を保存する
cv::Mat image; // ビデオからのキャッシュ画像
// OpenCV からのマウスのコールバック関数:
// void onMouse(int event, int x, int y, int flag, void *param)
// 4 番目のパラメータ `flag` は追加の状態を表し、
// param はユーザーパラメータを意味しますが、これらは必要ないので、名前は付けません。
void onMouse( int event, int x, int y, int, void* ) {
static cv::Point origin;
if(selectObject) {
// 選択された高さと幅、左上隅の位置を決定する
selection.x = MIN(x, origin.x);
selection.y = MIN(y, origin.y);
selection.width = std::abs(x - origin.x);
selection.height = std::abs(y - origin.y);
// &は cv::Rect によって上書きされます
// 2 つの領域の交差を意味します
// ここでの主な目的は、選択領域外の領域を処理することです
selection &= cv::Rect(0, 0, image.cols, image.rows);
}
switch(event) {
// 左ボタンが押されたときの処理
case CV_EVENT_LBUTTONDOWN:
origin = cv::Point(x, y);
selection = cv::Rect(x, y, 0, 0);
selectObject = true;
break;
// 左ボタンが離されたときの処理
case CV_EVENT_LBUTTONUP:
selectObject = false;
if( selection.width > 0 && selection.height > 0 )
trackObject = -1; // トラッキング対象のカムシフト特性が計算されていない
break;
}
}
int main( int argc, const char** argv ) {
cv::VideoCapture video("video.ogv");
cv::namedWindow("CamShift at LabEx");
cv::setMouseCallback("CamShift at LabEx", onMouse, NULL);
cv::Mat frame, hsv, hue, mask, hist, backproj;
cv::Rect trackWindow; // トラッキングウィンドウ
int hsize = 16; // ヒストグラム用
float hranges[] = {0,180}; // ヒストグラム用
const float* phranges = hranges; // ヒストグラム用
while(true) {
video >> frame;
if(frame.empty()) break;
frame.copyTo(image);
// HSV 空間に変換する
cv::cvtColor(image, hsv, cv::COLOR_BGR2HSV);
// オブジェクトがある場合の処理
if(trackObject) {
// H: 0~180、S: 30~256、V: 10~256 のみを処理し、それ以外をフィルタリングして残りの部分を mask にコピーする
cv::inRange(hsv, cv::Scalar(0, 30, 10), cv::Scalar(180, 256, 256), mask);
// hsv からチャンネル h を分離する
int ch[] = {0, 0};
hue.create(hsv.size(), hsv.depth());
cv::mixChannels(&hsv, 1, &hue, 1, ch, 1);
// トラッキング対象が計算されていない場合の特性抽出
if( trackObject < 0 ) {
// チャンネル h と mask ROI を設定する
cv::Mat roi(hue, selection), maskroi(mask, selection);
// ROI ヒストグラムを計算する
calcHist(&roi, 1, 0, maskroi, hist, 1, &hsize, &phranges);
// ヒストグラムの正規化
normalize(hist, hist, 0, 255, CV_MINMAX);
// トラッキング対象を設定する
trackWindow = selection;
// トラッキング対象が計算されたことを示す
trackObject = 1;
}
// ヒストグラムのバックプロジェクション
calcBackProject(&hue, 1, 0, hist, backproj, &phranges);
// 共通領域を取得する
backproj &= mask;
// カムシフトアルゴリズムを呼び出す
cv::RotatedRect trackBox = CamShift(backproj, trackWindow, cv::TermCriteria( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1 ));
// 描画用の領域が小さすぎる場合の処理
if( trackWindow.area() <= 1 ) {
int cols = backproj.cols, rows = backproj.rows, r = (MIN(cols, rows) + 5)/6;
trackWindow = cv::Rect(trackWindow.x - r, trackWindow.y - r,
trackWindow.x + r, trackWindow.y + r) & cv::Rect(0, 0, cols, rows);
}
// トラッキング領域を描画する
ellipse( image, trackBox, cv::Scalar(0,0,255), 3, CV_AA );
}
if( selectObject && selection.width > 0 && selection.height > 0 ) {
cv::Mat roi(image, selection);
bitwise_not(roi, roi);
}
imshow("CamShift at LabEx", image);
int key = cv::waitKey(1000/15.0);
if(key == 27) break;
}
cv::destroyAllWindows();
video.release();
return 0;
}
main.cppを再コンパイルしましょう:
g++ main.cpp $(pkg-config opencv --libs --cflags opencv) -o main
そして実行します:
./main
これで、プログラム内でオブジェクトを選択してトラッキングを行うことができます:
上の画像では、木星を選択し、トラッキングウィンドウは赤い楕円です。