tvl1_optical_flow.cpp 5.34 KB
Newer Older
1 2 3
#include <iostream>
#include <fstream>

4 5
#include <opencv2/core/utility.hpp>
#include "opencv2/video.hpp"
6
#include "opencv2/imgcodecs.hpp"
7
#include "opencv2/highgui.hpp"
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150

using namespace cv;
using namespace std;

inline bool isFlowCorrect(Point2f u)
{
    return !cvIsNaN(u.x) && !cvIsNaN(u.y) && fabs(u.x) < 1e9 && fabs(u.y) < 1e9;
}

static Vec3b computeColor(float fx, float fy)
{
    static bool first = true;

    // relative lengths of color transitions:
    // these are chosen based on perceptual similarity
    // (e.g. one can distinguish more shades between red and yellow
    //  than between yellow and green)
    const int RY = 15;
    const int YG = 6;
    const int GC = 4;
    const int CB = 11;
    const int BM = 13;
    const int MR = 6;
    const int NCOLS = RY + YG + GC + CB + BM + MR;
    static Vec3i colorWheel[NCOLS];

    if (first)
    {
        int k = 0;

        for (int i = 0; i < RY; ++i, ++k)
            colorWheel[k] = Vec3i(255, 255 * i / RY, 0);

        for (int i = 0; i < YG; ++i, ++k)
            colorWheel[k] = Vec3i(255 - 255 * i / YG, 255, 0);

        for (int i = 0; i < GC; ++i, ++k)
            colorWheel[k] = Vec3i(0, 255, 255 * i / GC);

        for (int i = 0; i < CB; ++i, ++k)
            colorWheel[k] = Vec3i(0, 255 - 255 * i / CB, 255);

        for (int i = 0; i < BM; ++i, ++k)
            colorWheel[k] = Vec3i(255 * i / BM, 0, 255);

        for (int i = 0; i < MR; ++i, ++k)
            colorWheel[k] = Vec3i(255, 0, 255 - 255 * i / MR);

        first = false;
    }

    const float rad = sqrt(fx * fx + fy * fy);
    const float a = atan2(-fy, -fx) / (float)CV_PI;

    const float fk = (a + 1.0f) / 2.0f * (NCOLS - 1);
    const int k0 = static_cast<int>(fk);
    const int k1 = (k0 + 1) % NCOLS;
    const float f = fk - k0;

    Vec3b pix;

    for (int b = 0; b < 3; b++)
    {
        const float col0 = colorWheel[k0][b] / 255.f;
        const float col1 = colorWheel[k1][b] / 255.f;

        float col = (1 - f) * col0 + f * col1;

        if (rad <= 1)
            col = 1 - rad * (1 - col); // increase saturation with radius
        else
            col *= .75; // out of range

        pix[2 - b] = static_cast<uchar>(255.f * col);
    }

    return pix;
}

static void drawOpticalFlow(const Mat_<Point2f>& flow, Mat& dst, float maxmotion = -1)
{
    dst.create(flow.size(), CV_8UC3);
    dst.setTo(Scalar::all(0));

    // determine motion range:
    float maxrad = maxmotion;

    if (maxmotion <= 0)
    {
        maxrad = 1;
        for (int y = 0; y < flow.rows; ++y)
        {
            for (int x = 0; x < flow.cols; ++x)
            {
                Point2f u = flow(y, x);

                if (!isFlowCorrect(u))
                    continue;

                maxrad = max(maxrad, sqrt(u.x * u.x + u.y * u.y));
            }
        }
    }

    for (int y = 0; y < flow.rows; ++y)
    {
        for (int x = 0; x < flow.cols; ++x)
        {
            Point2f u = flow(y, x);

            if (isFlowCorrect(u))
                dst.at<Vec3b>(y, x) = computeColor(u.x / maxrad, u.y / maxrad);
        }
    }
}

// binary file format for flow data specified here:
// http://vision.middlebury.edu/flow/data/
static void writeOpticalFlowToFile(const Mat_<Point2f>& flow, const string& fileName)
{
    static const char FLO_TAG_STRING[] = "PIEH";

    ofstream file(fileName.c_str(), ios_base::binary);

    file << FLO_TAG_STRING;

    file.write((const char*) &flow.cols, sizeof(int));
    file.write((const char*) &flow.rows, sizeof(int));

    for (int i = 0; i < flow.rows; ++i)
    {
        for (int j = 0; j < flow.cols; ++j)
        {
            const Point2f u = flow(i, j);

            file.write((const char*) &u.x, sizeof(float));
            file.write((const char*) &u.y, sizeof(float));
        }
    }
}

int main(int argc, const char* argv[])
{
ValeryTyumen's avatar
ValeryTyumen committed
151 152 153
    cv::CommandLineParser parser(argc, argv, "{help h || show help message}"
            "{ @frame0 | | frame 0}{ @frame1 | | frame 1}{ @output | | output flow}");
    if (parser.has("help"))
154
    {
ValeryTyumen's avatar
ValeryTyumen committed
155 156 157 158 159 160 161 162 163
        parser.printMessage();
        return 0;
    }
    string frame0_name = parser.get<string>("@frame0");
    string frame1_name = parser.get<string>("@frame1");
    string file = parser.get<string>("@output");
    if (frame0_name.empty() || frame1_name.empty() || file.empty())
    {
        cerr << "Usage : " << argv[0] << " [<frame0>] [<frame1>] [<output_flow>]" << endl;
164 165 166
        return -1;
    }

ValeryTyumen's avatar
ValeryTyumen committed
167 168
    Mat frame0 = imread(frame0_name, IMREAD_GRAYSCALE);
    Mat frame1 = imread(frame1_name, IMREAD_GRAYSCALE);
169 170 171

    if (frame0.empty())
    {
ValeryTyumen's avatar
ValeryTyumen committed
172
        cerr << "Can't open image ["  << parser.get<string>("frame0") << "]" << endl;
173 174 175 176
        return -1;
    }
    if (frame1.empty())
    {
ValeryTyumen's avatar
ValeryTyumen committed
177
        cerr << "Can't open image ["  << parser.get<string>("frame1") << "]" << endl;
178 179 180 181 182 183 184 185 186 187
        return -1;
    }

    if (frame1.size() != frame0.size())
    {
        cerr << "Images should be of equal sizes" << endl;
        return -1;
    }

    Mat_<Point2f> flow;
188
    Ptr<DenseOpticalFlow> tvl1 = createOptFlow_DualTVL1();
189 190

    const double start = (double)getTickCount();
191
    tvl1->calc(frame0, frame1, flow);
192 193 194 195 196
    const double timeSec = (getTickCount() - start) / getTickFrequency();
    cout << "calcOpticalFlowDual_TVL1 : " << timeSec << " sec" << endl;

    Mat out;
    drawOpticalFlow(flow, out);
ValeryTyumen's avatar
ValeryTyumen committed
197 198
    if (!file.empty())
        writeOpticalFlowToFile(flow, file);
199 200 201 202 203 204

    imshow("Flow", out);
    waitKey();

    return 0;
}