因为最近有一个需求是识别视频流中的指定物体,一些轻量级的算法实际测试效果都不太好,所以考虑到目标追踪算法。(是自动识别!!!!!!)
Opencv八种目标追踪算法:
BOOSTING Tracker:和Haar cascades(AdaBoost)背后所用的机器学习算法相同,但是距其诞生已有十多年了。这一追踪器速度较慢,并且表现不好,但是作为元老还是有必要提及的。(最低支持OpenCV 3.0.0)
MIL Tracker:比上一个追踪器更精确,但是失败率比较高。(最低支持OpenCV 3.0.0)
KCF Tracker:比BOOSTING和MIL都快,但是在有遮挡的情况下表现不佳。(最低支持OpenCV 3.1.0)
CSRT Tracker:比KCF稍精确,但速度不如后者。(最低支持OpenCV 3.4.2)
MedianFlow Tracker:在报错方面表现得很好,但是对于快速跳动或快速移动的物体,模型会失效。(最低支持OpenCV 3.0.0)
TLD Tracker:我不确定是不是OpenCV和TLD有什么不兼容的问题,但是TLD的误报非常多,所以不推荐。(最低支持OpenCV 3.0.0)
MOSSE Tracker:速度真心快,但是不如CSRT和KCF的准确率那么高,如果追求速度选它准没错。(最低支持OpenCV 3.4.1)
GOTURN Tracker:这是OpenCV中唯一一深度学习为基础的目标检测器。它需要额外的模型才能运行,本文不详细讲解。(最低支持OpenCV 3.2.0)
如果追求高准确度,又能忍受慢一些的速度,那么就用CSRT
如果对准确度的要求不苛刻,想追求速度,那么就选KCF
纯粹想节省时间就用MOSSE
因为MOSSE算法可以说是相关滤波目标追踪的开山之作,可以先通过学习MOSSE算法进而学习KCF。
对于MOSSE算法目前我仅限于把源码总体阅读一遍,其原理的讲解我就不班门弄斧了hhhhhhh。
经验参考自: http://www.elecfans.com/d/722414.html.
下面是通过对想识别的物体制作模板,以此模板对视频帧进行模板匹配,获取视频帧中该模板的位置,以此来初始化滤波器,从而正式进入MOSSE算法的工作。
效果还可以
放代码:
tracking.h
#include <opencv2/opencv.hpp>
#include <sstream>
class Tracking{
public:
cv::Mat divDFTs( const cv::Mat &src1, const cv::Mat &src2 ) const;
void preProcess( cv::Mat &window ) const;
double correlate( const cv::Mat &image_sub, cv::Point &delta_xy ) ;
cv::Mat randWarp( const cv::Mat& a ) const;
bool initImpl( const cv::Mat& image, const cv::Rect2d& boundingBox );
bool updateImpl( const cv::Mat& image, cv::Rect2d& boundingBox );
cv::Mat VisualDft(cv::Mat input);
public:
cv::Point2d center; //center of the bounding box
cv::Size size; //size of the bounding box
cv::Mat hanWin;
cv::Mat g,G; //goal
cv::Mat H, A,B; //state
};
tracking.cpp
#include "tracking.h"
using namespace cv;
using namespace std;
const double eps=0.00001; // for normalization
const double rate=0.2; // learning rate
const double psrThreshold=1.7; // no detection, if PSR is smaller than this
Mat Tracking::VisualDft(cv::Mat input) {
Mat padded; //以0填充输入图像矩阵
int m = getOptimalDFTSize(input.rows); //getOptimalDFTSize函数返回给定向量尺寸的傅里叶最优尺寸大小。
int n = getOptimalDFTSize(input.cols);
//填充输入图像I,输入矩阵为padded,上方和左方不做填充处理
copyMakeBorder(input, padded, 0, m - input.rows, 0, n - input.cols, BORDER_CONSTANT, Scalar::all(0));//四个方向的常量0扩充
Mat planes[] = { Mat_<float>(padded), Mat::zeros(padded.size(),CV_32F) };//两个矩阵
Mat complexI;
merge(planes, 2, complexI); //将planes融合合并成一个多通道数组complexI
dft(complexI, complexI); //进行傅里叶变换
//计算幅值,转换到对数尺度(logarithmic scale)
//=> log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
split(complexI, planes); //planes[0] = Re(DFT(I),planes[1] = Im(DFT(I))
//即planes[0]为实部,planes[1]为虚部
magnitude(planes[0], planes[1], planes[0]); //planes[0] = magnitude,求幅度谱
Mat magI = planes[0];
magI += Scalar::all(1);
log(magI, magI); //转换到对数尺度(logarithmic scale)
//如果有奇数行或列,则对频谱进行裁剪
magI = magI(Rect(0, 0, magI.cols&-2, magI.rows&-2)); //magI.rows&-2得到不大于magI.rows的最大偶数
//重新排列傅里叶图像中的象限,使得原点位于图像中心
int cx = magI.cols / 2;
int cy = magI.rows / 2;
Mat q0(magI, Rect(0, 0, cx, cy)); //左上角图像划定ROI区域
Mat q1(magI, Rect(cx, 0, cx, cy)); //右上角图像
Mat q2(magI, Rect(0, cy, cx, cy)); //左下角图像
Mat q3(magI, Rect(cx, cy, cx, cy)); //右下角图像
//变换左上角和右下角象限
Mat tmp;
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
//变换右上角和左下角象限
q1.copyTo(tmp);
q2.copyTo(q1);
tmp.copyTo(q2);
//归一化处理,用0-1之间的浮点数将矩阵变换为可视的图像格式
normalize(magI, magI, 0, 1, CV_MINMAX);
return magI;
}//图像的傅利叶变换。
Mat Tracking::divDFTs( const Mat &src1, const Mat &src2 ) const{
Mat c1[2],c2[2],a1,a2,s1,s2,denom,re,im;
// split into re and im per src
cv::split(src1, c1);
cv::split(src2, c2);
// (Re2*Re2 + Im2*Im2) = denom
// denom is same for both channels
cv::multiply(c2[0], c2[0], s1);
cv::multiply(c2[1], c2[1], s2);
cv::add(s1, s2, denom);
// (Re1*Re2 + Im1*Im1)/(Re2*Re2 + Im2*Im2) = Re
cv::multiply(c1[0], c2[0], a1);
cv::multiply(c1[1], c2[1], a2);
cv::divide(a1+a2, denom, re, 1.0 );
// (Im1*Re2 - Re1*Im2)/(Re2*Re2 + Im2*Im2) = Im
cv::multiply(c1[1], c2[0], a1);
cv::multiply(c1[0], c2[1], a2);
cv::divide(a1+a2, denom, im, -1.0);
// Merge Re and Im back into a complex matrix
Mat dst, chn[] = {re,im};
cv::merge(chn, 2, dst);
return dst;
}
void Tracking::preProcess( Mat &window ) const{
imshow("灰度图片",window);
moveWindow("灰度图片",20,20);
window.convertTo(window, CV_32F);//转化为32位浮点型
log(window + 1.0f, window); //对灰度图像进行对数变换,增强低光照情况下的对比度
//normalize
Scalar mean,StdDev;//val[4] 各个通道。
meanStdDev(window, mean, StdDev); //计算得到均值和标准差
window = (window-mean[0]) / (StdDev[0]+eps); //图像归一化
imshow("经过对数变换后",window);
moveWindow("经过对数变换后",220,20);
//Gaussain weighting
window = window.mul(hanWin); //空域对图像加汉宁窗,突出图像中心区域,弱化周围背景
imshow("加汉宁窗后",window);
moveWindow("加汉宁窗后",420,20);
}
double Tracking::correlate( const Mat &image_sub, Point &delta_xy )
{
Mat IMAGE_SUB, RESPONSE, response;
// filter in dft space
dft(image_sub, IMAGE_SUB, DFT_COMPLEX_OUTPUT); //对图像做dft,转化到频域,生成共轭对称矩阵,分别储存实部和虚部
mulSpectrums(IMAGE_SUB, H, RESPONSE, 0, true ); //时域卷积,频域相乘(矩阵点乘),矩阵中对应元素相乘
idft(RESPONSE, response, DFT_SCALE|DFT_REAL_OUTPUT); //离散傅里叶反变换,返回到时域
Mat response2show = VisualDft(response);//求实际响应
imshow("实际输出响应",response2show);
moveWindow("实际输出响应",1220,20);
// update center position
double maxVal; Point maxLoc;
minMaxLoc(response, 0, &maxVal, 0, &maxLoc);//寻找输出相关中的响应最大值位置,即为下一帧物体中心
delta_xy.x = maxLoc.x - int(response.size().width/2); //获得x方向运动偏移
delta_xy.y = maxLoc.y - int(response.size().height/2);//获得y方向运动偏移
// normalize response
Scalar mean,std;
meanStdDev(response, mean, std);//mean为均值,std为标准差
cout<<"psr is :"<<(maxVal-mean[0]) / (std[0]+eps)<<endl;
return (maxVal-mean[0]) / (std[0]+eps); // PSR返回波峰旁瓣比
}
Mat Tracking::randWarp( const Mat& a ) const
{
cv::RNG rng(8031965);
// random rotation
double C=0.1;
double ang = rng.uniform(-C,C);
double c=cos(ang), s=sin(ang);
// 生成随机仿射矩阵,只产生微小形变,不产生位移。防止单一训练样本带来的过拟合,在初始化时引入训练样本,增加滤波器的鲁棒性。
Mat_<float> W(2,3);
W << c + rng.uniform(-C,C), -s + rng.uniform(-C,C), 0,
s + rng.uniform(-C,C), c + rng.uniform(-C,C), 0;
// random translation
Mat_<float> center_warp(2, 1);
center_warp << a.cols/2, a.rows/2;
W.col(2) = center_warp - (W.colRange(0, 2))*center_warp;
Mat warped;
warpAffine(a, warped, W, a.size(), BORDER_REFLECT); //进行仿射变换
return warped;
}
bool Tracking::initImpl( const Mat& image, const Rect2d& boundingBox ) {
//根据框选的追踪物体初始化滤波器参数
Mat img;
if (image.channels() == 1)
img = image;
else
cvtColor(image, img, COLOR_BGR2GRAY);//如果不是单通道图片,则对其灰度化,对每个通道DFT效果相同
//对于二维dft,由于周期性,需要先对图片进行填零补充,以避免缠绕错误
int w = getOptimalDFTSize(int(boundingBox.width));//长度扩充到合适的尺寸,选择2倍,同时2的倍数时离散傅里叶变换速度较快
int h = getOptimalDFTSize(int(boundingBox.height));//getOptimalDFTSize函数返回给定向量尺寸的傅里叶最优尺寸大小。
//更新矩形框左上点坐标,向下取整
int x1 = int(floor((2*boundingBox.x+boundingBox.width-w)/2));
int y1 = int(floor((2*boundingBox.y+boundingBox.height-h)/2));
center.x = x1 + (w)/2;
center.y = y1 + (h)/2;
size.width = w;
size.height = h;
Mat window;
getRectSubPix(img, size, center, window); // 根据扩充后的尺寸截取追踪卷积区域
createHanningWindow(hanWin, size, CV_32F); // 创建汉宁窗
// 创建理想的输出响应
Mat goal=Mat::zeros(size,CV_32F);
goal.at<float>(h/2, w/2) = 1; //中心的响应为1(归一化)
GaussianBlur(goal, goal, Size(-1,-1), 2.0); //高斯滤波
double maxVal;
minMaxLoc(goal, 0, &maxVal);//获得理想响应最大值
g = goal / maxVal;//归一化
dft(g, G, DFT_COMPLEX_OUTPUT);//转化到频域
// initial A,B and H
A = Mat::zeros(G.size(), G.type());
B = Mat::zeros(G.size(), G.type());
for(int i=0; i<8; i++) {
Mat window_warp = randWarp(window); //初始化时对物体进行随机仿射变换
preProcess(window_warp);//图像预处理,灰度化、对数变换、加窗
Mat WINDOW_WARP, A_i, B_i;
dft(window_warp, WINDOW_WARP, DFT_COMPLEX_OUTPUT);//将输入图像转化到频域
mulSpectrums(G , WINDOW_WARP, A_i, 0, true);
mulSpectrums(WINDOW_WARP, WINDOW_WARP, B_i, 0, true);
A+=A_i;
B+=B_i;
}
H = divDFTs(A,B);//矩阵相除得到滤波器
return true;
}
bool Tracking::updateImpl( const Mat& image, Rect2d& boundingBox ) {
//更新滤波器参数,通过上一帧框选的位置与当前帧卷积获得运动中心偏移
if (H.empty()) // not initialized
return false;
Mat image_sub;
getRectSubPix(image, size, center, image_sub);//通过上一帧的框大小先截出卷积的区域
if (image_sub.channels() != 1)
cvtColor(image_sub, image_sub, COLOR_BGR2GRAY);//图像灰度化
preProcess(image_sub);//进行预处理
Point delta_xy;
double PSR = correlate(image_sub, delta_xy);//相关计算,同时获得物体中心偏移
if (PSR < psrThreshold)//以波峰旁瓣比为评判标准,设定阈值,如果小于阈值时,认为物体已经丢失
return false;
//更新物体中心坐标
center.x += delta_xy.x;
center.y += delta_xy.y;
Mat img_sub_new;
getRectSubPix(image, size, center, img_sub_new);//根据更新后的坐标,重新框选物体区域
if (img_sub_new.channels() != 1)
cvtColor(img_sub_new, img_sub_new, COLOR_BGR2GRAY);
preProcess(img_sub_new);//对更新后图像预处理
Mat srcdft = VisualDft(img_sub_new);
imshow("原始图像dft",srcdft);
moveWindow("原始图像dft",620,20);
// new state for A and B
Mat F, A_new, B_new;
dft(img_sub_new, F, DFT_COMPLEX_OUTPUT);//对更新后输入图像做dft
mulSpectrums(G, F, A_new, 0, true );//更新滤波器参数分子
mulSpectrums(F, F, B_new, 0, true );
// update A ,B, and H
A = A*(1-rate) + A_new*rate;//引入后续帧的样本进行训练,提高滤波器的鲁棒性,同时实现归一化
B = B*(1-rate) + B_new*rate;
H = divDFTs(A, B);
Mat h_show;
idft(H, h_show, DFT_SCALE|DFT_REAL_OUTPUT);
Mat h_dft = VisualDft(h_show);
imshow("滤波器频谱",h_dft);
moveWindow("滤波器频谱",820,20);
stringstream so;
stringstream st;
stringstream str;
string cenx;
string ceny;
string psr;
int font_face = cv::FONT_HERSHEY_COMPLEX;
// return tracked rect
double x=center.x, y=center.y;
so<<x;
so>>cenx;
st<<y;
st>>ceny;
putText(image, "center's position is:("+cenx+","+ceny+")",Point(0,30) , font_face, 1, cv::Scalar(255,0,0), 2, 8, 0);
str<<PSR;
str>>psr;
putText(image, "target's par is:"+psr,Point(0,65) , font_face, 1, cv::Scalar(0,0,0), 2, 8, 0);
int w = size.width, h=size.height;
boundingBox = Rect2d(Point2d(x-0.5*w, y-0.5*h), Point2d(x+0.5*w, y+0.5*h));
return true;
}
plot.h
#include"opencv2/opencv.hpp"
#include <cmath>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <vector>
#include <math.h>
#define WINDOW_WIDTH 600
#define WINDOW_HEIGHT 600
using namespace cv;
using namespace std;
struct LineType
{
char type;
bool is_need_lined;
Scalar color;
};
class CPlot
{
public:
void DrawAxis (IplImage *image); //画坐标轴
void DrawData (IplImage *image); //画点
int window_height; //窗口大小
int window_width;
vector< vector<CvPoint2D64f> >dataset; //点集合
vector<LineType> lineTypeSet; //线的类型
//color
CvScalar backgroud_color;
CvScalar axis_color;
CvScalar text_color;
IplImage* Figure;
// manual or automatic range
bool custom_range_y;
double y_max;
double y_min;
double y_scale;
bool custom_range_x;
double x_max;
double x_min;
double x_scale;
//边界大小
int border_size;
template<class T>
void plot(T *y, size_t Cnt, CvScalar color, char type = '*',bool is_need_lined = true);
template<class T>
void plot(T *x, T *y, size_t Cnt, CvScalar color, char type = '*',bool is_need_lined = true);
void xlabel(string xlabel_name, CvScalar label_color);
void ylabel(string ylabel_name, CvScalar label_color);
//清空图片上的数据
void clear();
void title(string title_name,CvScalar title_color);
CPlot();
~CPlot();
};
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
//采用范型设计,因此将实现部分和声明部分放在一个文件中
CPlot::CPlot()
{
this->border_size = 10; //图外围边界
this->window_height = WINDOW_HEIGHT;
this->window_width = WINDOW_WIDTH;
this->Figure = cvCreateImage(cvSize(this->window_height, this->window_width),IPL_DEPTH_8U, 3);
memset(Figure->imageData, 255, sizeof(unsigned char)*Figure->widthStep*Figure->height);
//color
this->backgroud_color = CV_RGB(255,255,255); //背景白色
this->axis_color = CV_RGB(0,0,0);//坐标黑色
this->text_color = CV_RGB(255,0 ,0); //文字红色
this->x_min = 0;
this->x_max = 0;
this->y_min = 0;
this->y_max = 0;
}
CPlot::~CPlot()
{
this->clear();
cvReleaseImage( &(this->Figure) );
}
//范型设计
template<class T>
void CPlot::plot(T *X, T *Y, size_t Cnt, CvScalar color, char type,bool is_need_lined)
{
//对数据进行存储
T tempX, tempY;
vector<CvPoint2D64f>data;
for(int i = 0; i < Cnt;i++)
{
tempX = X[i];
tempY = Y[i];
data.push_back( cvPoint2D64f((double)tempX, (double)tempY) );
}
this->dataset.push_back(data);
LineType LT;
LT.type = type;
LT.color = color;
LT.is_need_lined = is_need_lined;
this->lineTypeSet.push_back(LT);
//printf("data count:%d\n", this->dataset.size());
this->DrawData(this->Figure); //每次都是重新绘制
}
template<class T>
void CPlot::plot(T *Y, size_t Cnt, CvScalar color, char type,bool is_need_lined)
{
//对数据进行存储
T tempX, tempY;
vector<CvPoint2D64f>data;
for(int i = 0; i < Cnt;i++)
{
tempX = i;
tempY = Y[i];
data.push_back(cvPoint2D64f((double)tempX, (double)tempY));
}
this->dataset.push_back(data);
LineType LT;
LT.type = type;
LT.color = color;
LT.is_need_lined = is_need_lined;
this->lineTypeSet.push_back(LT);
this->DrawData(this->Figure);
}
void CPlot::clear()
{
this->dataset.clear();
this->lineTypeSet.clear();
}
void CPlot::title(string title_name,CvScalar title_color = Scalar(0,0,0))
{
int chw = 6, chh = 10;
IplImage *image = this->Figure;
CvFont font;
cvInitFont(&font,CV_FONT_HERSHEY_PLAIN,2,0.7, 0,1,CV_AA);
int x = (this->window_width - 2 * this->border_size ) / 2 + this->border_size - ( title_name.size() / 2.0 ) * chw;
int y = this->border_size / 2;
cvPutText( image, title_name.c_str(), cvPoint( x, y), &font, title_color);
}
void CPlot::xlabel(string xlabel_name, CvScalar label_color = Scalar(0,0,0))
{
int chw = 6, chh = 10;
int bs = this->border_size;
int h = this->window_height;
int w = this->window_width;
// let x, y axies cross at zero if possible.
double y_ref = this->y_min;
if ( (this->y_max > 0 ) && ( this->y_min <= 0 ) )
{
y_ref = 0;
}
int x_axis_pos = h - bs - cvRound((y_ref - this->y_min) * this->y_scale);
CvFont font;
cvInitFont(&font,CV_FONT_HERSHEY_PLAIN,1.5,0.7, 0,1,CV_AA);
int x = this->window_width - this->border_size - chw * xlabel_name.size();
int y = x_axis_pos + bs / 1.5;
cvPutText(this->Figure, xlabel_name.c_str(), cvPoint( x, y), &font, label_color);
}
void CPlot::ylabel(string ylabel_name, CvScalar label_color = Scalar(0,0,0))
{
CvFont font;
cvInitFont(&font,CV_FONT_HERSHEY_PLAIN,1.5,0.7, 0,1,CV_AA);
int x = this->border_size;
int y = this->border_size;
cvPutText(this->Figure, ylabel_name.c_str(), cvPoint( x, y), &font, label_color);
}
void CPlot::DrawAxis (IplImage *image)
{
CvScalar axis_color = this->axis_color;
int bs = this->border_size;
int h = this->window_height;
int w = this->window_width;
// size of graph
int gh = h - bs * 2;
int gw = w - bs * 2;
// draw the horizontal and vertical axis
// let x, y axies cross at zero if possible.
double y_ref = this->y_min;
if ( (this->y_max > 0 ) && ( this->y_min <= 0 ) )
{
y_ref = 0;
}
int x_axis_pos = h - bs - cvRound((y_ref - this->y_min) * this->y_scale);
//X 轴
cvLine(image, cvPoint(bs, x_axis_pos),
cvPoint(w - bs, x_axis_pos),
axis_color);
//Y 轴
cvLine(image, cvPoint(bs, h - bs),
cvPoint(bs, h - bs - gh),
axis_color);
// Write the scale of the y axis
CvFont font;
cvInitFont(&font,CV_FONT_HERSHEY_PLAIN,0.55,0.7, 0,1,CV_AA);
int chw = 6, chh = 10;
char text[16];
// y max
if ( (this->y_max - y_ref) > 0.05 * (this->y_max - this->y_min) )
{
sprintf(text, "%.1f", this->y_max);
cvPutText(image, text, cvPoint(bs, bs / 2), &font, this->text_color );
}
// y min
if ( (y_ref - this->y_min) > 0.05 * (this->y_max - this->y_min) )
{
sprintf(text,"%.1f", this->y_min);
cvPutText(image, text, cvPoint(bs, h - bs / 2), &font, this->text_color);
}
//画Y轴的刻度 每隔 scale_pixes 个像素
//Y正半轴
double y_scale_pixes = chh * 2;
for (int i = 0; i < ceil( (x_axis_pos - bs) / y_scale_pixes ) + 1; i++)
{
sprintf(text, "%.1f", i * y_scale_pixes / this->y_scale );
cvPutText(image, text, cvPoint(bs / 5, x_axis_pos - i * y_scale_pixes), &font, this->axis_color);
}
//Y负半轴
for (int i = 1; i < ceil (( h - x_axis_pos - bs ) / y_scale_pixes ) + 1; i++)
{
sprintf(text, "%.1f", -i * y_scale_pixes / this->y_scale );
cvPutText(image, text, cvPoint(bs / 5, x_axis_pos + i * y_scale_pixes), &font, this->axis_color);
}
// x_max
sprintf(text, "%.1f", this->x_max );
cvPutText(image, text, cvPoint(w - bs/2 - strlen(text) * chw, x_axis_pos), &font, this->text_color);
// x min
sprintf(text, "%.1f", this->x_min );
cvPutText(image, text, cvPoint(bs, x_axis_pos ), &font, this->text_color);
//画X轴的刻度 每隔 scale_pixes 个像素
double x_scale_pixes = chw * 4;
for (int i = 1; i < ceil( gw / x_scale_pixes ) + 1; i++)
{
sprintf(text, "%.0f", this->x_min + i * x_scale_pixes / this->x_scale );
cvPutText(image, text, cvPoint(bs + i * x_scale_pixes - bs / 4, x_axis_pos + chh), &font, this->axis_color);
}
}
//添加对线型的支持
//TODO线型未补充完整
//标记 线型
//l 直线
//* 星
//. 点
//o 圈
//x 叉
//+ 十字
//s 方块
void CPlot::DrawData (IplImage *image)
{
//this->x_min = this->x_max = this->dataset[0][0].x;
//this->y_min = this->y_max = this->dataset[0][0].y;
int bs = this->border_size;
for(size_t i = 0; i < this->dataset.size(); i++)
{
for(size_t j = 0; j < this->dataset[i].size(); j++)
{
if(this->dataset[i][j].x < this->x_min)
{
this->x_min = this->dataset[i][j].x;
}else if(this->dataset[i][j].x > this->x_max)
{
this->x_max = this->dataset[i][j].x;
}
if(this->dataset[i][j].y < this->y_min)
{
this->y_min = this->dataset[i][j].y;
}else if(this->dataset[i][j].y > this->y_max)
{
this->y_max = this->dataset[i][j].y;
}
}
}
double x_range = this->x_max - this->x_min;
double y_range = this->y_max - this->y_min;
this->x_scale = (image->width - bs*2)/ x_range;
this->y_scale = (image->height- bs*2)/ y_range;
//清屏
memset(image->imageData, 255, sizeof(unsigned char)*Figure->widthStep*Figure->height);
this->DrawAxis(image);
//printf("x_range: %f y_range: %f\n", x_range, y_range);
//绘制点
double tempX, tempY;
CvPoint prev_point, current_point;
int radius = 3;
int slope_radius = (int)( radius * 1.414 / 2 + 0.5);
for(size_t i = 0; i < this->dataset.size(); i++)
{
for(size_t j = 0; j < this->dataset[i].size(); j++)
{
tempX = (int)((this->dataset[i][j].x - this->x_min)*this->x_scale);
tempY = (int)((this->dataset[i][j].y - this->y_min)*this->y_scale);
current_point = cvPoint(bs + tempX, image->height - (tempY + bs));
if(this->lineTypeSet[i].type == 'l')
{
// draw a line between two points
if (j >= 1)
{
cvLine(image, prev_point, current_point, lineTypeSet[i].color, 1, CV_AA);
}
prev_point = current_point;
}else if(this->lineTypeSet[i].type == '.')
{
cvCircle(image, current_point, 1, lineTypeSet[i].color, -1, 8);
if (lineTypeSet[i].is_need_lined == true)
{
if (j >= 1)
{
cvLine(image, prev_point, current_point, lineTypeSet[i].color, 1, CV_AA);
}
prev_point = current_point;
}
}else if(this->lineTypeSet[i].type == '*')
{
//画*
cvLine(image, cvPoint(current_point.x - slope_radius, current_point.y - slope_radius),
cvPoint(current_point.x + slope_radius, current_point.y + slope_radius), lineTypeSet[i].color, 1, 8);
cvLine(image, cvPoint(current_point.x - slope_radius, current_point.y + slope_radius),
cvPoint(current_point.x + slope_radius, current_point.y - slope_radius), lineTypeSet[i].color, 1, 8);
cvLine(image, cvPoint(current_point.x - radius, current_point.y),
cvPoint(current_point.x + radius, current_point.y), lineTypeSet[i].color, 1, 8);
cvLine(image, cvPoint(current_point.x, current_point.y - radius),
cvPoint(current_point.x, current_point.y + radius), lineTypeSet[i].color, 1, 8);
if (lineTypeSet[i].is_need_lined == true)
{
if (j >= 1)
{
cvLine(image, prev_point, current_point, lineTypeSet[i].color, 1, CV_AA);
}
prev_point = current_point;
}
}else if(this->lineTypeSet[i].type == 'o')
{
cvCircle(image, current_point, radius, this->text_color, 1, CV_AA);
if (lineTypeSet[i].is_need_lined == true)
{
if (j >= 1)
{
cvLine(image, prev_point, current_point, lineTypeSet[i].color, 1, CV_AA);
}
prev_point = current_point;
}
}else if(this->lineTypeSet[i].type == 'x')
{
cvLine(image, cvPoint(current_point.x - slope_radius, current_point.y - slope_radius),
cvPoint(current_point.x + slope_radius, current_point.y + slope_radius), lineTypeSet[i].color, 1, 8);
cvLine(image, cvPoint(current_point.x - slope_radius, current_point.y + slope_radius),
cvPoint(current_point.x + slope_radius, current_point.y - slope_radius), lineTypeSet[i].color, 1, 8);
if (lineTypeSet[i].is_need_lined == true)
{
if (j >= 1)
{
cvLine(image, prev_point, current_point, lineTypeSet[i].color, 1, CV_AA);
}
prev_point = current_point;
}
}else if(this->lineTypeSet[i].type == '+')
{
cvLine(image, cvPoint(current_point.x - radius, current_point.y),
cvPoint(current_point.x + radius, current_point.y), lineTypeSet[i].color, 1, 8);
cvLine(image, cvPoint(current_point.x, current_point.y - radius),
cvPoint(current_point.x, current_point.y + radius), lineTypeSet[i].color, 1, 8);
if (lineTypeSet[i].is_need_lined == true)
{
if (j >= 1)
{
cvLine(image, prev_point, current_point, lineTypeSet[i].color, 1, CV_AA);
}
prev_point = current_point;
}
}else if(this->lineTypeSet[i].type == 's')
{
cvRectangle(image, cvPoint(current_point.x - slope_radius, current_point.y - slope_radius),
cvPoint(current_point.x + slope_radius, current_point.y + slope_radius), lineTypeSet[i].color, 1, 8);
if (lineTypeSet[i].is_need_lined == true)
{
if (j >= 1)
{
cvLine(image, prev_point, current_point, lineTypeSet[i].color, 1, CV_AA);
}
prev_point = current_point;
}
}
}
}
}
/**
上面提供是符合C语言使用习惯的用法,下面提供C++类型,减少传入的参数
*/
class Plot : public CPlot
{
public:
//重载这两个函数 传参简单
template<class T>
void plot( vector<T> Y,CvScalar color, char type = '*',bool is_need_lined = true);
template<class T>
void plot(vector< Point_<T> > p,CvScalar color, char type = '*',bool is_need_lined = true);
//增加一个函数把C版本的 IplImage 转换成Mat
Mat figure()
{
return cvarrToMat(this->Figure);
}
};
template<class T>
void Plot::plot(vector<T> Y, CvScalar color, char type,bool is_need_lined)
{
//对数据进行存储
T tempX, tempY;
vector<CvPoint2D64f>data;
for(int i = 0; i < Y.size();i++)
{
tempX = i;
tempY = Y[i];
data.push_back(cvPoint2D64f((double)tempX, (double)tempY));
}
this->dataset.push_back(data);
LineType LT;
LT.type = type;
LT.color = color;
LT.is_need_lined = is_need_lined;
this->lineTypeSet.push_back(LT);
this->DrawData(this->Figure);
}
template<class T>
void Plot::plot(vector< Point_<T> > p, CvScalar color, char type,bool is_need_lined)
{
//对数据进行存储
T tempX, tempY;
vector<CvPoint2D64f>data;
for(int i = 0; i < p.size();i++)
{
tempX = p[i].x;
tempY = p[i].y;
data.push_back( cvPoint2D64f((double)tempX, (double)tempY) );
}
this->dataset.push_back(data);
LineType LT;
LT.type = type;
LT.color = color;
LT.is_need_lined = is_need_lined;
this->lineTypeSet.push_back(LT);
//printf("data count:%d\n", this->dataset.size());
this->DrawData(this->Figure); //每次都是重新绘制
}
tenple_maching.h
#include "opencv2/opencv.hpp"
#include <iostream>
using namespace std;
using namespace cv;
class Temple{
public:
Temple()= default;
Rect2d match( Mat temple, Mat fram);
private:
//Mat temple;
};
Rect2d Temple::match( Mat temp, Mat src)
{
Mat dst=src.clone();
imshow("temp",temp);
int width=src.cols-temp.cols+1;//result宽度
int height=src.rows-temp.rows+1;//result高度
Mat result(height,width,CV_32FC1);//创建结果映射图像
// matchTemplate(srcImg, templateImg, resultImg, CV_TM_SQDIFF); //平方差匹配法(最好匹配0)
//matchTemplate(srcImg, templateImg, resultImg, CV_TM_SQDIFF_NORMED); //归一化平方差匹配法(最好匹配0)
//matchTemplate(srcImg, templateImg, resultImg, CV_TM_CCORR); //相关匹配法(最坏匹配0)
//matchTemplate(srcImg, templateImg, resultImg, CV_TM_CCORR_NORMED); //归一化相关匹配法(最坏匹配0)
//matchTemplate(srcImg, templateImg, resultImg, CV_TM_CCOEFF); //系数匹配法(最好匹配1)
matchTemplate(src,temp,result,CV_TM_CCOEFF_NORMED);//化相关系数匹配,最佳值1
imshow("result",result);
normalize(result,result,0,1,NORM_MINMAX,-1);//归一化到0-1范围
double minValue,maxValue;
Point minLoc,maxLoc;
minMaxLoc(result,&minValue,&maxValue,&minLoc,&maxLoc);
// cout<<"minValue="<
// cout<<"maxValue="<
rectangle(dst,maxLoc,Point(maxLoc.x+temp.cols,maxLoc.y+temp.rows),Scalar(0,255,0),2,8);
imshow("dst",dst);
Rect2d rst;
rst.width =temp.cols;
rst.height=temp.rows;
rst.x=maxLoc.x;
rst.y=maxLoc.y;
return rst;
}
main.cpp
#include <opencv2/core/utility.hpp>
#include <opencv2/tracking.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/opencv.hpp>
#include <iostream>
#include <cstring>
#include "tracking.h"
#include "plot.h"
#include "temple_maching.h"
using namespace std;
using namespace cv;
Point center;
//绘制贝塞尔曲线
Point pointAdd(Point p, Point q) {
p.x += q.x; p.y += q.y;
return p;
}
Point pointTimes(float c, Point p) {
p.x *= c; p.y *= c;
return p;
}
Point Bernstein(float u, Point qi,Point mid,Point mo)
{
Point a, b, c, r;
a = pointTimes(pow(u, 2), mo);
b = pointTimes(pow((1 - u), 2), qi);
c = pointTimes(2 * u*(1 - u), mid);
r = pointAdd(pointAdd(a, b), c); //mo*u^2+qi*(1-u)^2+mid*(2*u*(1-u))=r
return r;
}
Size size;
int main(){
Tracking tracking;
Plot plotx,ploty;
Temple temp;
plotx.x_max = 100; //可以设定横纵坐标的最大,最小值
plotx.x_min = 0;
plotx.y_max = 480;
plotx.y_min = 0;
ploty.x_max = 100; //可以设定横纵坐标的最大,最小值
ploty.x_min = 0;
ploty.y_max = 480;
ploty.y_min = 0;
vector<double> centerx;
vector<double> centery;
vector<Point2d> centers;
Mat frame;
Rect2d roi;
int m = 0;
VideoCapture cap("/home/sundekai/桌面/reset_basketball.avi");//"/home/fatcat/Desktop/robcon2019/Kcf_tracker/tracking/SampleVideo.avi"
if (!cap.isOpened())
{
return 0;
}
// cout << "press c to leap current Image" << endl;
// cout << "press q to slect current Image" << endl;
// cout << "press empty key to start track RIO Object" << endl;
// cap >> frame;
// while (1){
// char key = waitKey(1);
// if (key == 'c'){ // 按c键跳帧
// cap >> frame;
// }
// if (key == 'q'){ // 按q键退出跳帧
// break;
// }
// imshow("first", frame);
// }
//
// cv::destroyWindow("first");
// roi = selectROI("tracker", frame);
cap >> frame;
if(frame.empty()) return -1;
imshow("tracker", frame);
Mat basket=imread("/home/sundekai/桌面/temple_1.jpg");
roi=temp.match(basket,frame);
if (roi.width == 0 || roi.height == 0)
return 0;
if(tracking.initImpl(frame,roi)) {
for (;;) {
// get frame from the video
cap >> frame;
m++;
// stop the program if no more images
if (frame.rows == 0 || frame.cols == 0) {
// cv::destroyWindow("tracker");
break;
}
// update the tracking result
Mat showG = tracking.VisualDft(tracking.g);
imshow("理想输出响应",showG);
moveWindow("理想输出响应",1020,20);
if(tracking.updateImpl(frame, roi)){
double x=tracking.center.x, y=tracking.center.y;
int w = tracking.size.width, h=tracking.size.height;
roi = Rect2d(Point2d(x-0.5*w, y-0.5*h), Point2d(x+0.5*w, y+0.5*h));
rectangle(frame,roi,Scalar(255,0,0),3);//绘制矩形框
centerx.push_back(x);
centery.push_back(y);
centers.push_back(tracking.center);
plotx.plot(centerx, Scalar(0,255, 0));
ploty.plot(centery, Scalar(0,255, 0));
imshow("x方向运动分布",plotx.figure());
moveWindow("x方向运动分布",700,320);
imshow("y方向运动分布",ploty.figure());
moveWindow("y方向运动分布",1300,320);
if(centers.size()>3){
for (int j = 2; j < centers.size(); j += 2)
{
Point pre, last, mid;
pre = centers[j - 2];
mid = centers[j - 1];
last = centers[j];
Point pt_pre = centers[j-2];
Point pt_now;
//绘制贝塞尔曲线,一小段一小段的直线就能组合成曲线
for (int k = 0; k <= 10; k++)
{
float u = (float)k / 10;
Point new_point = Bernstein(u, pre, mid, last);
pt_now.x = (int)new_point.x;
pt_now.y = (int)new_point.y;
line(frame, pt_pre, pt_now, Scalar(255, 0, 0), 2, CV_AA);//绘制直线
pt_pre = pt_now;
}
}
}
imshow("tracker", frame);
moveWindow("tracker",20,320);
waitKey(20);
if (char(waitKey(1)) == 'q') {
cv::destroyWindow("tracker");
break;
}
}else{
cout<<"lost the RIO Object"<<endl;
break;
}
}
}
return 0;
}
视频源和模板自己去制作吧。。。。。