# 视讯稳定

warpAffine 函数的实现是:
http://blog.csdn.net/fengbingchun/article/details/17713429

1. Find the transformation from previous to current frame using optical flow for all frames. The transformation only consists of three parameters: dx, dy, da (angle). Basically, a rigid Euclidean transform, no scaling, no sharing。找到之前到现在帧的变换，对于每帧图片我们求这个图片的光流场，我的理解是如果求光流的话，一定是两张图片，我们从一张图片引线到另一张图片，变换包含三个参数：dx，dy，da（angle（角度？)),基本上，一个刚体的欧式变换，没有尺度变换，没有sharing（不明白啥意思）？
2. Accumulate the transformations to get the “trajectory（轨迹）” for x, y, angle, at each frame.累加变换目的是得到每一帧的x,y,angle的轨迹。
3. Smooth out the trajectory using a sliding average window. The user defines the window radius, where the radius is the number of frames used for smoothing.（利用一个平均滤波器来平滑轨迹，用户可以定义窗口的半径，半径的用来平滑帧数的数量，这意思是半径越大，用越多的帧来平滑这一帧？）
4. Create a new transformation such that new_transformation = transformation + (smoothed_trajectory – trajectory).（构造一个新的变换，新变换=老的变换+ (smoothed_trajectory – trajectory)
5. Apply the new transformation to the video. 应用新变换到这个视频里面。
`#include <opencv2/opencv.hpp>#include <iostream>#include <cassert>#include <cmath>#include <fstream>using namespace std;using namespace cv;// This video stablisation smooths the global trajectory using a sliding average windowconst int SMOOTHING_RADIUS = 30; // In frames. The larger the more stable the video, but less reactive to sudden panningconst int HORIZONTAL_BORDER_CROP = 20; // In pixels. Crops the border to reduce the black borders from stabilisation being too noticeable.// 1. Get previous to current frame transformation (dx, dy, da) for all frames// 2. Accumulate the transformations to get the image trajectory// 3. Smooth out the trajectory using an averaging window// 4. Generate new set of previous to current transform, such that the trajectory ends up being the same as the smoothed trajectory// 5. Apply the new transformation to the videostruct TransformParam{    TransformParam() {}    TransformParam(double _dx, double _dy, double _da) {        dx = _dx;        dy = _dy;        da = _da;    }    double dx;    double dy;    double da; // angle};struct Trajectory{    Trajectory() {}    Trajectory(double _x, double _y, double _a) {        x = _x;        y = _y;        a = _a;    }    double x;    double y;    double a; // angle};int main(int argc, char **argv){    if(argc < 2) {        cout << "./VideoStab [video.avi]" << endl;        return 0;    }    // For further analysis    ofstream out_transform("prev_to_cur_transformation.txt");    ofstream out_trajectory("trajectory.txt");    ofstream out_smoothed_trajectory("smoothed_trajectory.txt");    ofstream out_new_transform("new_prev_to_cur_transformation.txt");    VideoCapture cap(argv[1]);    assert(cap.isOpened());    Mat cur, cur_grey;    Mat prev, prev_grey;    cap >> prev;    cvtColor(prev, prev_grey, COLOR_BGR2GRAY);    // Step 1 - Get previous to current frame transformation (dx, dy, da) for all frames    vector <TransformParam> prev_to_cur_transform; // previous to current    int k=1;    int max_frames = cap.get(CV_CAP_PROP_FRAME_COUNT);    Mat last_T;    while(true) {        cap >> cur;        if(cur.data == NULL) {            break;        }        cvtColor(cur, cur_grey, COLOR_BGR2GRAY)；// 把彩色图片转成灰度图片        // vector from prev to cur        vector <Point2f> prev_corner, cur_corner;        vector <Point2f> prev_corner2, cur_corner2;        vector <uchar> status;        vector <float> err;        goodFeaturesToTrack(prev_grey, prev_corner, 200, 0.01, 30);`
`        goodFeaturesToTrack  的函数定义：`
`               http://baike.baidu.com/link?url=TLaHlExF3xwZ3TEggi-aSRMmhv5kRKWbh1otteVQaf6WELDSZrj6wtU8M2YZnW1MR4eK4zf0ez091c9Bzy4xn_        calcOpticalFlowPyrLK(prev_grey, cur_grey, prev_corner, cur_corner, status, err);        计算一个稀疏特征集的光流，使用金字塔中的迭代 Lucas-Kanade 方法`
`     对于`
`     http://baike.baidu.com/link?url=a1RXdSuq0rY33ncnLl2ITqFShZIjIpKNgSsLjmDEfaacA8olj79edwGAikFloyUbgssfJpJiNSaeKsfrdb3Ozq        // weed out bad matches        for(size_t i=0; i < status.size(); i++) {            if(status[i]) {                prev_corner2.push_back(prev_corner[i]);                cur_corner2.push_back(cur_corner[i]);            }        }        // translation + rotation only        Mat T = estimateRigidTransform(prev_corner2, cur_corner2, false); // false = rigid transform, no scaling/shearing        // in rare cases no transform is found. We'll just use the last known good transform.        if(T.data == NULL) {            last_T.copyTo(T);        }        T.copyTo(last_T);        // decompose T        double dx = T.at<double>(0,2);        double dy = T.at<double>(1,2);        double da = atan2(T.at<double>(1,0), T.at<double>(0,0));        prev_to_cur_transform.push_back(TransformParam(dx, dy, da));        out_transform << k << " " << dx << " " << dy << " " << da << endl;        cur.copyTo(prev);        cur_grey.copyTo(prev_grey);        cout << "Frame: " << k << "/" << max_frames << " - good optical flow: " << prev_corner2.size() << endl;        k++;    }    // Step 2 - Accumulate the transformations to get the image trajectory    // Accumulated frame to frame transform    double a = 0;    double x = 0;    double y = 0;    vector <Trajectory> trajectory; // trajectory at all frames    for(size_t i=0; i < prev_to_cur_transform.size(); i++) {        x += prev_to_cur_transform[i].dx;        y += prev_to_cur_transform[i].dy;        a += prev_to_cur_transform[i].da;        trajectory.push_back(Trajectory(x,y,a));        out_trajectory << (i+1) << " " << x << " " << y << " " << a << endl;    }    // Step 3 - Smooth out the trajectory using an averaging window    vector <Trajectory> smoothed_trajectory; // trajectory at all frames    for(size_t i=0; i < trajectory.size(); i++) {        double sum_x = 0;        double sum_y = 0;        double sum_a = 0;        int count = 0;        for(int j=-SMOOTHING_RADIUS; j <= SMOOTHING_RADIUS; j++) {            if(i+j >= 0 && i+j < trajectory.size()) {                sum_x += trajectory[i+j].x;                sum_y += trajectory[i+j].y;                sum_a += trajectory[i+j].a;                count++;            }        }        double avg_a = sum_a / count;        double avg_x = sum_x / count;        double avg_y = sum_y / count;        smoothed_trajectory.push_back(Trajectory(avg_x, avg_y, avg_a));        out_smoothed_trajectory << (i+1) << " " << avg_x << " " << avg_y << " " << avg_a << endl;    }    // Step 4 - Generate new set of previous to current transform, such that the trajectory ends up being the same as the smoothed trajectory    vector <TransformParam> new_prev_to_cur_transform;    // Accumulated frame to frame transform    a = 0;    x = 0;    y = 0;    for(size_t i=0; i < prev_to_cur_transform.size(); i++) {        x += prev_to_cur_transform[i].dx;        y += prev_to_cur_transform[i].dy;        a += prev_to_cur_transform[i].da;        // target - current        double diff_x = smoothed_trajectory[i].x - x;        double diff_y = smoothed_trajectory[i].y - y;        double diff_a = smoothed_trajectory[i].a - a;        double dx = prev_to_cur_transform[i].dx + diff_x;        double dy = prev_to_cur_transform[i].dy + diff_y;        double da = prev_to_cur_transform[i].da + diff_a;        new_prev_to_cur_transform.push_back(TransformParam(dx, dy, da));        out_new_transform << (i+1) << " " << dx << " " << dy << " " << da << endl;    }    // Step 5 - Apply the new transformation to the video    cap.set(CV_CAP_PROP_POS_FRAMES, 0);    Mat T(2,3,CV_64F);    int vert_border = HORIZONTAL_BORDER_CROP * prev.rows / prev.cols; // get the aspect ratio correct    k=0;    while(k < max_frames-1) { // don't process the very last frame, no valid transform        cap >> cur;        if(cur.data == NULL) {            break;        }        T.at<double>(0,0) = cos(new_prev_to_cur_transform[k].da);        T.at<double>(0,1) = -sin(new_prev_to_cur_transform[k].da);        T.at<double>(1,0) = sin(new_prev_to_cur_transform[k].da);        T.at<double>(1,1) = cos(new_prev_to_cur_transform[k].da);        T.at<double>(0,2) = new_prev_to_cur_transform[k].dx;        T.at<double>(1,2) = new_prev_to_cur_transform[k].dy;        Mat cur2;        warpAffine(cur, cur2, T, cur.size());`
`        // 此处没有说明是使用的最近邻算法，还是bicubic的插值算法。`
`        cur2 = cur2(Range(vert_border, cur2.rows-vert_border), Range(HORIZONTAL_BORDER_CROP, cur2.cols-HORIZONTAL_BORDER_CROP));        // Resize cur2 back to cur size, for better side by side comparison        resize(cur2, cur2, cur.size());        // Now draw the original and stablised side by side for coolness        Mat canvas = Mat::zeros(cur.rows, cur.cols*2+10, cur.type());        cur.copyTo(canvas(Range::all(), Range(0, cur2.cols)));        cur2.copyTo(canvas(Range::all(), Range(cur2.cols+10, cur2.cols*2+10)));        // If too big to fit on the screen, then scale it down by 2, hopefully it'll fit :)        if(canvas.cols > 1920) {            resize(canvas, canvas, Size(canvas.cols/2, canvas.rows/2));        }        imshow("before and after", canvas);        //char str[256];        //sprintf(str, "images/%08d.jpg", k);        //imwrite(str, canvas);        waitKey(20);        k++;    }    return 0;}`

0 0