【opencv】目标跟踪之MOSSE算法配合模板匹配实现初始滤波器的自动初始化

因为最近有一个需求是识别视频流中的指定物体,一些轻量级的算法实际测试效果都不太好,所以考虑到目标追踪算法。(是自动识别!!!!!!

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算法的工作。

先上效果图:
1.模板
【opencv】目标跟踪之MOSSE算法配合模板匹配实现初始滤波器的自动初始化_第1张图片
2.视频流第一帧进行模板匹配的效果

【opencv】目标跟踪之MOSSE算法配合模板匹配实现初始滤波器的自动初始化_第2张图片
3.目标追踪效果:(怎么上传视频!!!)

效果还可以

放代码:
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;

}

视频源和模板自己去制作吧。。。。。

你可能感兴趣的:(【opencv】目标跟踪之MOSSE算法配合模板匹配实现初始滤波器的自动初始化)