#include <bits/stdc++.h>
#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/video.hpp"
#include "opencv2/objdetect.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/ml.hpp"
#define p(a) putchar(a)
#define For(i,a,b) for(int i=a;i<=b;++i)

using namespace std;
using namespace cv;

int w,h,w0,h0,bc0,bc1;
double fps,part,diff,val;
bool lu_flag,flag;
Point lu,rd,mid,st,ed,best_st,best_ed;
Mat temp,image,IMAGE,tb;
MatND hist;

int channels[] = {0,1,2};
int SIZE[] = {32,32,32};
float R[] = {0,255};
float G[] = {0,255};
float B[] = {0,255};
const float *ranges[] = {R, G ,B};

const char * path = "/Users/war/Desktop//track0.mp4";

void in(int &x){
    int y=1;char c=getchar();x=0;
    while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
    while(c<='9'&&c>='0'){ x=(x<<1)+(x<<3)+c-'0';c=getchar();}
    x*=y;
}
void o(int x){
    if(x<0){p('-');x=-x;}
    if(x>9)o(x/10);
    p(x%10+'0');
}

void onMouse(int event, int x, int y, int flags, void *ustc){
    if(event == EVENT_LBUTTONDOWN){
        lu_flag = 1;
        lu = Point(x,y);
    }
    if(event == EVENT_MOUSEMOVE && lu_flag){
        temp = image.clone();
        rd = Point(x,y);
        if(lu != rd) rectangle(temp, lu, rd, Scalar(255, 0, 0), 3);
        imshow("temp",temp);
    }
    if(event == EVENT_LBUTTONUP){
        lu_flag = 0;flag = 1;
        rd = Point(x,y);
        IMAGE = image(Rect(lu, rd));
    }
}

void init(){
    VideoCapture video(path);
    if(!video.isOpened()){
        cout<<"视频打开失败!"<<endl;
        return;
    }
    fps = video.get(CAP_PROP_FPS);
    part = 1000.0 / fps;
    namedWindow("temp");
    setMouseCallback("temp", onMouse);
    while(1){
        if(!lu_flag) video >> image;
        if(!image.data || waitKey(part) == 27) break;
        imshow("temp",image);
        if(flag){
            destroyWindow("temp");
            break;
        }
    }
    video.release();
}

double cal(){
    Mat temp0;
    calcHist(&temp, 1, channels, Mat(), temp0, 3, SIZE, ranges, true, false);
    normalize(temp0, temp0, 0, 1, NORM_MINMAX);
    return compareHist(hist, temp0, 3);
}

void tracking(){
    w = abs(lu.x - rd.x);
    h = abs(lu.y - rd.y);
    auto x0 = lu.x - w;
    auto x1 = lu.x + w;
    auto y0 = rd.y - h;
    auto y1 = rd.y + h;
    x0 = max(0, x0);
    x1 = min(x1, image.cols);
    y0 = max(0, y0);
    y1 = min(y1, image.rows);
    VideoCapture video(path);
    if(!video.isOpened()){
        cout<<"视频打开失败!"<<endl;
        return;
    }
    fps = video.get(CAP_PROP_FPS);
    part = 1000.0 / fps;
    namedWindow("tracking");
    while(1){
        video >> image;
        if(!image.data || waitKey(part) == 27) break;
        diff = 1.0; bc0 = IMAGE.rows / 10; bc1 =  IMAGE.cols / 10;
        for(int i = y0; i <= y1; i += bc0){
            for(st.x=x0,st.y=i; st.x <= x1; st.x += bc1){
                ed.x = min(st.x + w, image.cols-1);
                ed.y = min(st.y + h, image.rows-1);
                temp = image(Rect(st,ed));
                val = cal();
                if(diff > val){
                    diff = val;
                    best_st = st;
                    best_ed = ed;
                    tb = temp;
                }
            }
        }
        if(diff < 1.0){
            auto x0 = best_st.x - w;
            auto x1 = best_st.x + w;
            auto y0 = best_ed.y - h;
            auto y1 = best_ed.y + h;
            x0 = max(0, x0);
            x1 = min(x1, image.cols);
            y0 = max(0, y0);
            y1 = min(y1, image.rows);
            rectangle(image, best_st, best_ed, Scalar(0, 0, 255), 2);
        }
        imshow("tracking",image);
    }
    video.release();
}

signed main(){
    init();
    calcHist(&IMAGE, 1, channels, Mat(), hist, 3, SIZE, ranges, true, false);
    normalize(hist, hist, 0, 1, NORM_MINMAX);
    tracking();
    waitKey(0);
    return 0;
}