fourier_descriptors_demo.cpp 7.23 KB
Newer Older
LaurentBerger's avatar
LaurentBerger committed
1 2 3 4 5 6 7 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
#include <opencv2/core.hpp>
#include <opencv2/core/utility.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/ximgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

struct ThParameters {
    int levelNoise;
    int angle;
    int scale10;
    int origin;
    int xg;
    int yg;
    bool update;
} ;

static vector<Point> NoisyPolygon(vector<Point> pRef, double n);
static void UpdateShape(int , void *r);
static void AddSlider(String sliderName, String windowName, int minSlider, int maxSlider, int valDefault, int *valSlider, void(*f)(int, void *), void *r);

int main(void)
{
    vector<Point> ctrRef;
    vector<Point> ctrRotate, ctrNoisy, ctrNoisyRotate, ctrNoisyRotateShift;
    // build a shape with 5 vertex
    ctrRef.push_back(Point(250,250)); ctrRef.push_back(Point(400, 250));
    ctrRef.push_back(Point(400, 300)); ctrRef.push_back(Point(250, 300));ctrRef.push_back(Point(180, 270));
    Point cg(0,0);
    for (int i=0;i<static_cast<int>(ctrRef.size());i++)
        cg+=ctrRef[i];
    cg.x /= static_cast<int>(ctrRef.size());
    cg.y /= static_cast<int>(ctrRef.size());
    ThParameters p;
    p.levelNoise=6;
    p.angle=45;
    p.scale10=5;
    p.origin=10;
    p.xg=150;
    p.yg=150;
    p.update=true;
    namedWindow("FD Curve matching");
    // A rotation with center at (150,150) of angle 45 degrees and a scaling of 5/10
    AddSlider("Noise", "FD Curve matching", 0, 20, p.levelNoise, &p.levelNoise, UpdateShape, &p);
    AddSlider("Angle", "FD Curve matching", 0, 359, p.angle, &p.angle, UpdateShape, &p);
    AddSlider("Scale", "FD Curve matching", 5, 100, p.scale10, &p.scale10, UpdateShape, &p);
    AddSlider("Origin%%", "FD Curve matching", 0, 100, p.origin, &p.origin, UpdateShape, &p);
    AddSlider("Xg", "FD Curve matching", 150, 450, p.xg, &p.xg, UpdateShape, &p);
    AddSlider("Yg", "FD Curve matching", 150, 450, p.yg, &p.yg, UpdateShape, &p);
    int code=0;
    double dist;
    vector<vector<Point> > c;
    Mat img;
LaurentBerger's avatar
LaurentBerger committed
57
    cout << "******************** PRESS g TO MATCH CURVES *************\n";
LaurentBerger's avatar
LaurentBerger committed
58 59
    do
    {
LaurentBerger's avatar
LaurentBerger committed
60
        code = waitKey(30);
LaurentBerger's avatar
LaurentBerger committed
61 62 63 64
        if (p.update)
        {
            Mat r = getRotationMatrix2D(Point(p.xg, p.yg), p.angle, 10.0/ p.scale10);
            ctrNoisy= NoisyPolygon(ctrRef,static_cast<double>(p.levelNoise));
65
            cv::transform(ctrNoisy, ctrNoisyRotate, r);
LaurentBerger's avatar
LaurentBerger committed
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
            ctrNoisyRotateShift.clear();
            for (int i=0;i<static_cast<int>(ctrNoisy.size());i++)
                ctrNoisyRotateShift.push_back(ctrNoisyRotate[(i+(p.origin*ctrNoisy.size())/100)% ctrNoisy.size()]);
            // To draw contour using drawcontours
            c.clear();
            c.push_back(ctrRef);
            c.push_back(ctrNoisyRotateShift);
            p.update = false;
            Rect rglobal;
            for (int i = 0; i < static_cast<int>(c.size()); i++)
            {
                rglobal = boundingRect(c[i]) | rglobal;
            }
            rglobal.width += 10;
            rglobal.height += 10;
            img = Mat::zeros(2 * rglobal.height, 2 * rglobal.width, CV_8UC(3));
            drawContours(img, c, 0, Scalar(255,0,0));
            drawContours(img, c, 1, Scalar(0, 255, 0));
            circle(img, c[0][0], 5, Scalar(255, 0, 0));
            circle(img, c[1][0], 5, Scalar(0, 255, 0));
            imshow("FD Curve matching", img);
        }
        if (code == 'd')
        {
            destroyWindow("FD Curve matching");
            namedWindow("FD Curve matching");
            // A rotation with center at (150,150) of angle 45 degrees and a scaling of 5/10
            AddSlider("Noise", "FD Curve matching", 0, 20, p.levelNoise, &p.levelNoise, UpdateShape, &p);
            AddSlider("Angle", "FD Curve matching", 0, 359, p.angle, &p.angle, UpdateShape, &p);
            AddSlider("Scale", "FD Curve matching", 5, 100, p.scale10, &p.scale10, UpdateShape, &p);
            AddSlider("Origin%%", "FD Curve matching", 0, 100, p.origin, &p.origin, UpdateShape, &p);
            AddSlider("Xg", "FD Curve matching", 150, 450, p.xg, &p.xg, UpdateShape, &p);
            AddSlider("Yg", "FD Curve matching", 150, 450, p.yg, &p.yg, UpdateShape, &p);

        }
        if (code == 'g')
        {
            ximgproc::ContourFitting fit;
            vector<Point2f> ctrRef2d, ctrRot2d;
            // sampling contour we want 256 points
            ximgproc::contourSampling(ctrRef, ctrRef2d, 256); // use a mat
            ximgproc::contourSampling(ctrNoisyRotateShift, ctrRot2d, 256); // use a vector og point
            fit.setFDSize(16);
            Mat t;
            fit.estimateTransformation(ctrRot2d, ctrRef2d, t, &dist, false);
            cout << "Transform *********\n "<<"Origin = "<< t.at<double>(0,0)*ctrNoisy.size() <<" expected "<< (p.origin*ctrNoisy.size()) / 100 <<" ("<< ctrNoisy.size()<<")\n";
            cout << "Angle = " << t.at<double>(0, 1) * 180 / M_PI << " expected " << p.angle  <<"\n";
            cout << "Scale = " << t.at<double>(0, 2) << " expected " << p.scale10 / 10.0 << "\n";
            Mat dst;
115
            ximgproc::transformFD(ctrRot2d, t, dst, false);
LaurentBerger's avatar
LaurentBerger committed
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 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
            c.push_back(dst);
            drawContours(img, c, 2, Scalar(0,255,255));
            circle(img, c[2][0], 5, Scalar(0, 255, 255));
            imshow("FD Curve matching", img);
        }
    }
    while (code!=27);

    return 0;
}

vector<Point> NoisyPolygon(vector<Point> pRef, double n)
{
    RNG rng;
    vector<Point> c;
    vector<Point> p = pRef;
    vector<vector<Point> > contour;
    for (int i = 0; i<static_cast<int>(p.size()); i++)
        p[i] += Point(Point2d(n*rng.uniform((double)-1, (double)1), n*rng.uniform((double)-1, (double)1)));
    if (n==0)
        return p;
    c.push_back(p[0]);
    int minX = p[0].x, maxX = p[0].x, minY = p[0].y, maxY = p[0].y;
    for (int i = 0; i <static_cast<int>(p.size()); i++)
    {
        int next = i + 1;
        if (next == static_cast<int>(p.size()))
            next = 0;
        Point2d u = p[next] - p[i];
        int d = static_cast<int>(norm(u));
        double a = atan2(u.y, u.x);
        int step = 1;
        if (n != 0)
            step = static_cast<int>(d / n);
        for (int j = 1; j<d; j += max(step, 1))
        {
            Point pNew;
            do
            {

                Point2d pAct = (u*j) / static_cast<double>(d);
                double r = n*rng.uniform((double)0, (double)1);
                double theta = a + rng.uniform(0., 2 * CV_PI);
                pNew = Point(Point2d(r*cos(theta) + pAct.x + p[i].x, r*sin(theta) + pAct.y + p[i].y));
            } while (pNew.x<0 || pNew.y<0);
            if (pNew.x<minX)
                minX = pNew.x;
            if (pNew.x>maxX)
                maxX = pNew.x;
            if (pNew.y<minY)
                minY = pNew.y;
            if (pNew.y>maxY)
                maxY = pNew.y;
            c.push_back(pNew);
        }
    }
    return c;
}

void UpdateShape(int , void *r)
{
    ((ThParameters *)r)->update = true;
}

void AddSlider(String sliderName, String windowName, int minSlider, int maxSlider, int valDefault, int *valSlider, void(*f)(int, void *), void *r)
{
    createTrackbar(sliderName, windowName, valSlider, 1, f, r);
    setTrackbarMin(sliderName, windowName, minSlider);
    setTrackbarMax(sliderName, windowName, maxSlider);
    setTrackbarPos(sliderName, windowName, valDefault);
}