CamShift

ちょっとCamShiftを使った物体追跡プログラムを書いてみました.

CamShiftはMeanShiftTrackingを改良したもので,物体の大きさや姿勢の変化も追跡することができます.
触った感じだと,CamShiftの結果はRotatedRectで帰ってくるのに対して入力には通常のRectを入れなければならないので,若干扱いづらい部分がありした.RotatedRectに外接する矩形をそのまま次の初期探索領域にすると,回転によって領域が大きくなりすぎがちだったため,今回は外接する矩形を少し小さくしたものを次の初期探索領域としています.
あと,ヒストグラムにも割りと敏感なので注意してやる必要がありそうです.

ソース(VS2013, OpenCV2.4.8)

#include <iostream>
#include <opencv2/opencv.hpp>

/********************************
 * 初期矩形指定用マウスコールバック
********************************/
void onmouse(int event, int x, int y, int flags, void* userdata) {
	static bool lbutton_is_down = false;
	cv::Rect* rect = (cv::Rect*)userdata;

	switch (event) {
	case CV_EVENT_LBUTTONDOWN:
		lbutton_is_down = true;
		rect->x = x;
		rect->y = y;
		break;
	case CV_EVENT_LBUTTONUP:
		lbutton_is_down = false;
		break;
	case CV_EVENT_MOUSEMOVE:
		if (lbutton_is_down) {
			rect->width = x - rect->x;
			rect->height = y - rect->y;
		}
		break;
	}
}

/********************************
* 初期矩形設定
********************************/
cv::Rect getInitRect(const cv::Mat& frame) {
	cv::Rect init_rect(0, 0, 100, 100);
	cv::namedWindow("frame");
	cv::setMouseCallback("frame", onmouse, &init_rect);

	while (cv::waitKey(100) != ' ') {
		cv::Mat canvas = frame.clone();
		cv::rectangle(canvas, init_rect, cv::Scalar(0, 255, 0), 2);
		cv::imshow("frame", canvas);
	}
	return init_rect;
}

/********************************
 * main
********************************/
int main(int argc, char** argv) {
	cv::VideoCapture cap("mov03.mov");
	if (!cap.isOpened()) {
		std::cerr << "failed to open input file" << std::endl;
		std::cin.ignore(1);
		return 0;
	}

	cv::Mat frame, hist;
	cap >> frame;
	cv::Rect rect = getInitRect(frame);	// 初期探索矩形

	// ターゲットのヒストグラム生成
	int hist_size[] = { 64, 64, 64 };
	float range[] = { 0, 256 };
	const float* ranges[] = { range, range, range };
	int channels[] = { 0, 1, 2 };
	cv::Mat roi(frame, rect);
	cv::calcHist(&roi, 1, channels, cv::Mat(), hist, 3, hist_size, ranges);

	// メインループ, escが押されるまで
	while (cv::waitKey(66) != 0x1b) {
		cap >> frame;
		if (!frame.data) {
			continue;
		}

		// ヒストグラムのバックプロジェクション計算
		cv::Mat back_proj;
		cv::calcBackProject(&frame, 1, channels, hist, back_proj, ranges);
		// camshift
		cv::TermCriteria term_crit(cv::TermCriteria::EPS | cv::TermCriteria::COUNT, 30, 1);
		cv::RotatedRect rotated_rect = cv::CamShift(back_proj, rect, term_crit);

		// RotatedRectから次の探索矩形,boundingboxを少し小さくしたもの
		rect = rotated_rect.boundingRect();
		rect.x += rect.width / 8;
		rect.y += rect.height / 8;
		rect.width *= (6.0 / 8.0);
		rect.height *= (6.0 / 8.0);

		// RotatedRectから頂点座標
		std::vector<cv::Point2f> pts(4);
		rotated_rect.points(&pts[0]);
		std::vector<cv::Point> ptsi(4);
		std::copy(pts.begin(), pts.end(), ptsi.begin());

		// 描画
		cv::rectangle(frame, rect, cv::Scalar(255, 0, 0), 2);
		cv::polylines(frame, ptsi, true, cv::Scalar(0, 255, 0), 2);
		cv::imshow("frame", frame);
	}
}

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください