Find Minimum area ellipse enclosing a set of points in c++

后端 未结 3 1149
孤街浪徒
孤街浪徒 2021-01-27 22:07

I have a set of 2D points. I need to find a minimum area ellipse enclosing all the points. Could someone give an idea of how the problem has to be tackled. For a circle it was s

相关标签:
3条回答
  • 2021-01-27 22:19

    Not sure if I can prove it, but it seems to me that the optimal solution would be characterized by tangenting (at least) 3 of the points, while all the other points are inside the ellipse (think about it!). So if nothing else, you should be able to brute force it by checking all ~n^3 triplets of points and checking if they define a solution. Should be possible to improve on that by removing all points that would have to be strictly inside any surrounding ellipse, but I'm not sure how that could be done. Maybe by sorting the points by x and y coordinates and then doing something fancy.

    Not a complete solution, but it's a start.

    EDIT: Unfortunately 3 points aren't enough to define an ellipse. But perhaps if you restrict it to the ellipse of the smallest area tangenting 3 points?

    0 讨论(0)
  • 2021-01-27 22:25

    as Rory Daulton suggest you need to clearly specify the constraints of solution and removal of any will greatly complicates things. For starters assume this for now:

    • it is 2D problem
    • ellipse is axis aligned
    • center is arbitrary instead of (0,0)

    I would attack this as standard genere and test problem with approximation search (which is hybrid between binary search and linear search) to speed it up (but you can also try brute force from start so you see if it works).

    1. compute constraints of solution

      To limit the search you need to find approximate placement position and size of the ellipse. For that you can use out-scribed circle for your points. It is clear that ellipse area will be less or equal to the circle and placement will be near by. The circle does not have to be necessarily the smallest one possible so we can use for example this:

      1. find bounding box of the points
      2. let the circle be centered to that bounding box and with radius be the max distance from its center to any of the points.

      This will be O(n) complexity where n is number of your points.

    2. search "all" the possible ellipses and remember best solution

      so we need to find ellipse center (x0,y0) and semi-axises rx,ry while area = M_PI*rx*ry is minimal. With approximation search each variable has factor of O(log(m)) and each iteration need to test validity which is O(n) so final complexity would be O(n.log^4(m)) where m is average number of possible variations of each search parameter (dependent on accuracy and search constraints). With simple brute search it would be O(n.m^4) which is really scary especially for floating point where m can be really big.

      To speed this up we know that the area of ellipse will be less then or equal to area of found circle so we can ignore all the bigger ellipses. The constrains to rx,ry can be derived from the aspect ratio of the bounding box +/- some reserve.

    Here simple small C++ example using that approx class from link above:

    //---------------------------------------------------------------------------
    // input points
    const int n=15; // number of random points to test
    float pnt[n][2];
    // debug bounding box
    float box_x0,box_y0,box_x1,box_y1;
    // debug outscribed circle
    float circle_x,circle_y,circle_r;
    // solution ellipse
    float ellipse_x,ellipse_y,ellipse_rx,ellipse_ry;
    //---------------------------------------------------------------------------
    void compute(float x0,float y0,float x1,float y1) // cal with bounding box where you want your points will be generated
        {
        int i;
        float x,y;
        // generate n random 2D points inside defined area
        Randomize();
        for (i=0;i<n;i++)
            {
            pnt[i][0]=x0+(x1-x0)*Random();
            pnt[i][1]=y0+(y1-y0)*Random();
            }
        // compute bounding box
        x0=pnt[0][0]; x1=x0;
        y0=pnt[0][1]; y1=y0;
        for (i=0;i<n;i++)
            {
            x=pnt[i][0]; if (x0>x) x0=x; if (x1<x) x1=x;
            y=pnt[i][1]; if (y0>y) y0=y; if (y1<y) y1=y;
            }
        box_x0=x0; box_x1=x1;
        box_y0=y0; box_y1=y1;
        // "outscribed" circle
        circle_x=0.5*(x0+x1);
        circle_y=0.5*(y0+y1);
        circle_r=0.0;
        for (i=0;i<n;i++)
            {
            x=pnt[i][0]-circle_x; x*=x;
            y=pnt[i][1]-circle_y; y*=y; x+=y;
            if (circle_r<x) circle_r=x;
            }
        circle_r=sqrt(circle_r);
        // smallest area ellipse
    
        int N;
        double m,e,step,area;
        approx ax,ay,aa,ab;
        N=3; // number of recursions each one improves accuracy with factor 10
        area=circle_r*circle_r; // solution will not be bigger that this
        step=((x1-x0)+(y1-y0))*0.05; // initial position/size step for the search as 1/10 of avg bounding box size
        for (ax.init(         x0,          x1,step,N,&e);!ax.done;ax.step())    // search x0
        for (ay.init(         y0,          y1,step,N,&e);!ay.done;ay.step())    // search y0
        for (aa.init(0.5*(x1-x0),2.0*circle_r,step,N,&e);!aa.done;aa.step())    // search rx
        for (ab.init(0.5*(y1-y0),2.0*circle_r,step,N,&e);!ab.done;ab.step())    // search ry
            {
            e=aa.a*ab.a;
            // is ellipse outscribed?
            if (aa.a>=ab.a)
                {
                m=aa.a/ab.a; // convert to circle of radius rx
                for (i=0;i<n;i++)
                    {
                    x=(pnt[i][0]-ax.a);   x*=x;
                    y=(pnt[i][1]-ay.a)*m; y*=y;
                    // throw away this ellipse if not
                    if (x+y>aa.a*aa.a) { e=2.0*area; break; }
                    }
                }
            else{
                m=ab.a/aa.a; // convert to circle of radius ry
                for (i=0;i<n;i++)
                    {
                    x=(pnt[i][0]-ax.a)*m; x*=x;
                    y=(pnt[i][1]-ay.a);   y*=y;
                    // throw away this ellipse if not
                    if (x+y>ab.a*ab.a) { e=2.0*area; break; }
                    }
                }
             }
    
        ellipse_x =ax.aa;
        ellipse_y =ay.aa;
        ellipse_rx=aa.aa;
        ellipse_ry=ab.aa;
        }
    //---------------------------------------------------------------------------
    

    Even this simple example with only 15 points took around 2 seconds to compute. You can improve performance by adding heuristics like test only areas lower then circle_r^2 etc, or better select solution area with some math rule. If you use brute force instead of approximation search that expect the computation time could be even minutes or more hence the O(scary)...

    Beware this example will not work for any aspect ratio of the points as I hardcoded the upper bound for rx,ry to 2.0*circle_r which may not be enough. Instead you can compute the upper bound from aspect ratio of the points and or condition that rx*ry<=circle_r^2...

    There are also other ("faster") methods for example variation of CCD (cyclic coordinate descend) can be used. But such methods usually can not guarantee that optimal solution will be found or any at all ...

    Here overview of the example output:

    The dots are individual points from pnt[n], the gray dashed stuff are bounding box and used out-scribed circle. The green ellipse is found solution.

    0 讨论(0)
  • 2021-01-27 22:27

    These don't go as far as giving you C++ code, but they include in-depth discussion of effective algorithms for what you need to do.

    https://www.cs.cornell.edu/cv/OtherPdf/Ellipse.pdf

    http://www.stsci.edu/~RAB/Backup%20Oct%2022%202011/f_3_CalculationForWFIRSTML/Gaertner%20&%20Schoenherr.pdf

    0 讨论(0)
提交回复
热议问题