ちょっと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);
}
}