参考图像跟踪(四)KCF算法
我们知道KCF 是一个效率和速度都比较好的一种跟踪算法。而模板匹配的目标跟踪是比较笨的一种方式,因此有必要放在一起比比看。代码已经在下面!
大家可以自己去比较!
当然基本条件是要有特殊库,要基于opencv 3.0以上的配置!
实现用鼠标选取一个跟踪区域,然后就有不同颜色的框来指示跟踪效果了。
总得来说,KCF 效果好些,特别是当跟踪目标跑到录像机外面去的时候,也有作用。
// ConsoleApplication1.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#include "kcftracker.hpp"
#include
using namespace std;
using namespace cv;
cv::Mat org, dst, img, tmp;
float xMin = 0.0;
float yMin = 0.0;
float width = 0.0;
float height = 0.0;
bool CHOSE = false;
bool STATE_1 = false;
bool STATE_2 = false;
bool STATE_3 = false;
bool isKCF = false;
void templatematchtracking(Mat frame, Mat &templ, Rect2d &rect);
void on_mouse(int event, int x, int y, int flags, void *ustc)//event
{
static Point pre_pt(-1, -1);
static Point cur_pt(-1, -1);
char temp[16];
if (event == CV_EVENT_LBUTTONDOWN)
{
// 使用四个顶点计算出目标框
org.copyTo(img);
sprintf_s(temp, "(%d,%d)", x, y);
pre_pt = Point(x, y);
xMin = x;
yMin = y;
putText(img, temp, pre_pt, FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 0, 255), 1, 8);
circle(img, pre_pt, 2, Scalar(255, 0, 0, 0), CV_FILLED, CV_AA, 0);
imshow("img", img);
}
else if (event == CV_EVENT_MOUSEMOVE && !(flags & CV_EVENT_FLAG_LBUTTON))
{
img.copyTo(tmp);
sprintf_s(temp, "(%d,%d)", x, y);
cur_pt = Point(x, y);
putText(tmp, temp, cur_pt, FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 0, 255));
imshow("img", tmp);
STATE_2 = true;
}
else if (event == CV_EVENT_MOUSEMOVE && (flags & CV_EVENT_FLAG_LBUTTON))
{
img.copyTo(tmp);
sprintf_s(temp, "(%d,%d)", x, y);
cur_pt = Point(x, y);
putText(tmp, temp, cur_pt, FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 0, 255));
rectangle(tmp, pre_pt, cur_pt, Scalar(0, 255, 0, 0), 5, 8, 0);
imshow("img", tmp);
}
else if (event == CV_EVENT_LBUTTONUP)//
{
org.copyTo(img);
sprintf_s(temp, "(%d,%d)", x, y);
cur_pt = Point(x, y);
putText(img, temp, cur_pt, FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 0, 255));
circle(img, pre_pt, 2, Scalar(255, 0, 0, 0), CV_FILLED, CV_AA, 0);
rectangle(img, pre_pt, cur_pt, Scalar(0, 255, 0, 0), 1, 8, 0);//
imshow("img", img);
img.copyTo(tmp);
//
width = abs(pre_pt.x - cur_pt.x);
height = abs(pre_pt.y - cur_pt.y);
if (width == 0 || height == 0)
{
printf("width == 0 || height == 0");
return;
}
CHOSE = true;
Rect2d rect = Rect2d(min(cur_pt.x, pre_pt.x), min(cur_pt.y, pre_pt.y), width, height);
rect &= Rect2d(0, 0, org.cols, org.rows);
dst = org(rect);
// namedWindow("dst");
// imshow("dst",dst);
// waitKey(0);
STATE_3 = STATE_1 = STATE_2 = false;
}
}
#define HEIGHT 0.97
#define THETA 60
#define FOCUS 1100
#define PI 3.141592653
void adjustPosition(float x, float y, float &actual_x, float &actual_y) {
float move_pixel_x = x - (xMin + width / 2);
float move_pixel_y = y - (yMin + height / 2);
float S = HEIGHT / cos(THETA*PI / 180);
float DX = (S*move_pixel_x) / FOCUS;
float DY = (S*move_pixel_y) / FOCUS;
actual_x = DX;
actual_y = DY / cos(THETA*PI / 180);
}
int main()
{
// save_dir的路径,用于读取图像
string save_dir = "d:\1.jpg";
VideoCapture cap(0);
if (!cap.isOpened())
{
return -1;
}
// 当前帧
Mat frame;
Mat last_gray;
Mat first;
Mat cur_gray;
bool HOG = true;// 是否使用hog特征
bool FIXEDWINDOW = false;// 是否使用修正窗口
bool MULTISCALE = true;// 是否使用多尺度
bool SILENT = false;// 是否不做显示
bool LAB = false;// 是否使用LAB颜色
float sum_error_ncc = 0.0;
int err_num = 0;
float mean_error_ncc = 0.0;
bool LOST = false;
// 创建KCF跟踪器
// KCFTracker tracker(HOG, FIXEDWINDOW, MULTISCALE, LAB);
// KCFTracker tracker_redetect(HOG, FIXEDWINDOW, MULTISCALE, LAB);
// create the tracker
Ptr tracker = Tracker::create("KCF");
Rect2d templRect,kcfRect;// 跟踪结果目标框
Mat templmat;
bool stop = false;
bool FIRST = true;
int nFramesNochose = 0;
int nFrames = 0;// 帧号计数
while (!stop)
{
cap >> frame;//获取图像
org = frame;//?
org.copyTo(img);
org.copyTo(tmp);
if (!CHOSE) {
namedWindow("img");
imshow("img", frame);
setMouseCallback("img", on_mouse, 0);
}
if (CHOSE && FIRST)
{
templRect.x = xMin;
templRect.y = yMin;
templRect.width = width;
templRect.height = height;
templRect &= Rect2d(0, 0, org.cols, org.rows);
kcfRect = templRect;
printf("Initial Box : %f, %f, %f, %f\n", xMin, yMin, width, height);
// 使用第一帧和目标框来初始化跟踪器
tracker->init(frame, kcfRect);
cvtColor(frame, last_gray, CV_RGB2GRAY);
rectangle(frame, kcfRect, Scalar(0, 255, 255), 5, 8);
templmat = last_gray(templRect);
FIRST = false;
}
else if (CHOSE && !FIRST)
{
//double start = clock();
cvtColor(frame, last_gray, CV_RGB2GRAY);
//if (isKCF)
{
// 更新当前帧的结果
tracker->update(frame, kcfRect);
}
//else
{
templatematchtracking(frame, templmat, templRect);
}
sum_error_ncc += mean_error_ncc;
err_num++;
//double finish = clock();
//////////////////////////////////////////////////////////////////
//printf("TIME : %f ms!\n", (finish - start) * 1000 / CLOCKS_PER_SEC);
Scalar color1,color;
if (mean_error_ncc < 0.1)
{
color = Scalar(0, 255, 0, 0);
color1 = Scalar(255, 0, 0,0);
}
else
{
color = Scalar(0, 0, 255, 0);
color1 = Scalar(0, 255, 125, 0);
}
rectangle(frame, templRect, color, 3, 8);
rectangle(frame, kcfRect, color1, 2, 8);
float actual_x = 0.0;
float actual_y = 0.0;
char buff[50] = { '\0' };
adjustPosition((templRect.x + templRect.width / 2), (templRect.y + templRect.height / 2), actual_x, actual_y);
sprintf_s(buff, "adjust (x : %.5f, y : %.5f)", actual_x, actual_y);
putText(frame, buff, Point(templRect.x, templRect.y - 20), FONT_HERSHEY_SIMPLEX, 0.5, color);
}
if (CHOSE && !FIRST) {
cvDestroyWindow("img");
namedWindow("tracking");//
imshow("tracking", frame);
}
else
imshow("img", frame);
if (waitKey(1) == 27)
break;
if (CHOSE)
nFrames++;
}
return 0;
}
void templatematchtracking(Mat frame, Mat &templ, Rect2d &rect)
{
Mat grayFrame;
cvtColor(frame, grayFrame, CV_RGB2GRAY);//由于模板匹配只支持单通道,因此需要转为灰度图像
//设置搜索窗口大小,按照移动的速度,设置为原来兴趣窗口的2倍大小,也可以使很大。
Rect2d findRect;
findRect.width = min((int)rect.width * 2, frame.cols);
findRect.height = min((int)rect.height * 2, frame.rows);
findRect.x = rect.x + rect.width * 0.5 - findRect.width * 0.5;
findRect.y = rect.y + rect.height * 0.5 - findRect.height * 0.5;
findRect &= Rect2d(0, 0, frame.cols, frame.rows);
Mat matchResult;
matchTemplate(grayFrame(findRect), templ, matchResult, CV_TM_CCOEFF);//使用归一化相关系数匹配法,最强匹配法,值最大表示匹配最好 1表示最好。
double maxVal = 0;
Point maxLoc;
//筛选出最大匹配点
minMaxLoc(matchResult, 0, &maxVal, 0, &maxLoc);
cout << "the maxVal =" << maxVal << endl;
//找到下一次模板新的起始点
rect.x = maxLoc.x + findRect.x;
rect.y = maxLoc.y + findRect.y;
//更新模板,这样表示可能导致当初跟踪的目标会发生变化。
rect &= Rect2d(0, 0, frame.cols, frame.rows);
templ = grayFrame(rect);
}