查找包含c ++中一组点的最小区域椭圆

时间:2016-11-06 22:44:47

标签: c++ math geometry ellipse

我有一组2D点。我需要找到一个包围所有点的最小区域椭圆。有人可以知道如何解决问题。对于一个圆圈来说很简单。中心和点之间的最大距离。但对于椭圆而言,它非常复杂,我不知道。我必须在c ++中实现它。 enter image description here

3 个答案:

答案 0 :(得分:3)

这些并没有给你提供C ++代码,但它们包括对你需要做的有效算法的深入讨论。

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

答案 1 :(得分:1)

不确定我是否可以证明它,但在我看来,最佳解决方案的特点是切线(至少)3个点,而所有其他点都在椭圆内(考虑一下!)。因此,如果没有别的,你应该能够通过检查所有~3 ^三个点并检查它们是否定义解决方案来强制它。应该可以通过删除所有必须严格在任何周围椭圆内的点来改进,但我不知道如何做到这一点。也许可以通过x和y坐标对点进行排序,然后做一些奇特的事情。

不是一个完整的解决方案,但这是一个开始。

编辑: 不幸的是,3点不足以定义椭圆。但也许如果你把它限制在切线3点的最小区域的椭圆上?

答案 2 :(得分:0)

正如Rory Daulton建议您需要明确指出解决方案的约束并删除任何会使事情变得非常复杂。对于初学者现在假设:

  • 2D 问题
  • 椭圆轴对齐
  • center是任意的,而不是(0,0)

我会用approximation search(二进制搜索和线性搜索之间的混合)来标准 genere和test 问题来加速它(但你也可以试试开始,看看它是否有效。

  1. 计算解决方案的约束

    要限制搜索,您需要找到椭圆的近似放置位置和大小。为此,您可以使用外观圆圈来表示您的观点。很明显,椭圆区域将小于或等于圆形,并且位置将接近。圆圈不一定是最小的圆圈,因此我们可以使用例如:

    1. 找到分数的边界框
    2. 让圆圈以该边界框为中心,半径为从中心到任意一点的最大距离。
    3. 这将是O(n)复杂度,其中n是您的积分数。

    4. 搜索"所有"可能的省略号并记住最佳解决方案

      所以我们需要找到椭圆中心(x0,y0)和半轴rx,ry,而area = M_PI*rx*ry是最小的。使用近似搜索,每个变量的因子为O(log(m)),每次迭代需要测试O(n)的有效性,因此最终的复杂度为O(n.log^4(m)),其中m是可能的变量的平均数。每个搜索参数(取决于准确性和搜索约束)。通过简单的粗暴搜索,它将是O(n.m^4),这对于m可能非常大的浮点尤其可怕。

      为了加快速度,我们知道椭圆的面积将小于或等于找到圆的面积,因此我们可以忽略所有较大的椭圆。 rx,ry的约束可以从边界框的纵横比+/-某些保留得出。

    5. 这里使用上面链接中的approx类的简单小 C ++ 示例:

      //---------------------------------------------------------------------------
      // 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;
          }
      //---------------------------------------------------------------------------
      

      即使这个只有15分的简单例子也需要大约2秒才能计算出来。您可以通过添加启动量(例如仅低于circle_r^2等的测试区域来提高性能,或者使用某些数学规则更好地选择解决方案区域。如果你使用蛮力而不是近似搜索,期望计算时间可能是几分钟或更长,因此O(scary) ......

      请注意,此示例不适用于点的任何宽高比,因为我将rx,ry的上限硬编码为2.0*circle_r,这可能还不够。相反,您可以计算点的纵横比的上限和/或条件rx*ry<=circle_r^2 ...

      还有其他(&#34;更快&#34;)方法,例如可以使用 CCD (循环坐标下降)的变化。但是这些方法通常无法保证找到最佳解决方案或者根本不能找到最佳解决方案......

      此处输出示例概述:

      overview

      点是来自pnt[n]的单个点,灰色虚线的东西是边界框并且使用外边的圆圈。找到绿色椭圆的解决方案。