【问题标题】:Identifying the corners of a polygon in 2d space识别二维空间中多边形的角
【发布时间】:2010-11-21 21:40:05
【问题描述】:

给定四个 (x,y) 对,它们代表二维空间中任意多边形(四边形)的四个角,我想确定:

  1. 多边形是否凸
  2. 如果是凸的,哪个特定点代表左上角、右上角、左下角和右下角

我是作为图像处理程序的一部分执行此操作的,因此假设 Y 轴是翻转的(即,正值从顶部向下移动)。


我的第一个想法是获取多边形的边界框,然后测量每个顶点到边界框角的距离。然后对于多边形的每个顶点,确定它最接近边界框的哪个角并相应地标记它。这不适用于多边形,例如:

http://matt.bridges.name/polygon.png

多边形的左上角最靠近边界框的右上角。它也不是距离边界框左上角最近的顶点。

【问题讨论】:

  • 你知道点的顺序还是点无序?
  • 必须知道点的顺序;有多个具有 4 个给定角的多边形。
  • 有以下几种情况。 1.三个点组成一个三角形,最后一个点在这个三角形内。在这种情况下,有三个可能的四边形,它们都是非凸的。 2. 在所有其他情况下,也存在三个可能的四边形。一个是凸的,另外两个是自相交的。因此,如果顺序未知(并且不考虑自相交四边形),也可以回答这些问题。
  • 点数是无序的,由用户任意选择。我想检测上面的情况(1),并报错。在案例 2 中,我想找到一个凸的非自相交多边形并将每个角标识为左上角、右上角等。

标签: image-processing geometry


【解决方案1】:

为了确定多边形是否为凸面,您可以使用类似于Graham scan 中使用的方法,并遍历这些点,检查您是否每次都向右(或向左)转弯。

为了确定哪些角在哪里,您可以查看哪些点的 x 和 y 最小;并选择其中一个作为左下角。他们可能会同时出现,这很好,但如果不是,那么,并不总是很容易分辨出左下角应该是什么,比如这里:

"Bottom left corner" is quite ambiguous http://a.imagehost.org/0894/bottomleftiswhich.png

确定左下角是哪一个后,您可以简单地按顺序穿过角落并相应地标记它们。要找出它们的顺序,只需通过上述检查检查您是向右转还是向左转。

【讨论】:

    【解决方案2】:

    既然我已经简洁地陈述了这个问题,我想到了另一个解决方案:

    1. 在每个顶点之间画线
    2. 通过检查剩余的两个点是位于直线的相对侧还是同一侧来确定哪些直线是对角线
    3. 如果使用此方法恰好找到两条对角线,则多边形是凸多边形。
    4. 负斜率的对角线连接左下角和右上角,正斜率的对角线连接左上角和右下角。

    有没有更简单的方法?

    【讨论】:

      【解决方案3】:

      如果两条对角线都在四边形内部并因此相交,则该四边形是凸的。左下角是位于交点下方和左侧的点。其他三个角的模拟条件成立。

      如果您事先不知道点的顺序,则您不知道对角,因此不知道对角线。在这种情况下,您必须计算所有可能的六个段的交点。如果多边形是凸的,你将得到一个交点,你可以用它来确定四个角。如果多边形不是凸的,就不会有交点。

      更新

      我创建了一个小型 C# 程序来测试我的建议。 Convex-concave-detection 按预期工作,但仍有角点检测失败的情况(参见代码中的测试用例 3)。但是解决这个问题应该很简单。

      代码

      using System;
      using System.Collections.Generic;
      using System.Drawing;
      using System.Globalization;
      using System.Linq;
      using System.Text;
      using System.Threading;
      
      namespace Quadrilaterals
      {
          public class Program
          {
              public static void Main()
              {
                  Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
      
                  Int32[,] tests = { { 0, 1, 2, 3 }, { 0, 2, 1, 3 }, { 0, 3, 1, 2 } };
      
                  PointF[] points = { new PointF(4, -2), new PointF(2, 5), new PointF(8, -6), new PointF(10, 7) };
                  //PointF[] points = { new PointF(0, 0), new PointF(10, 0), new PointF(5, 10), new PointF(5, 3) };
                  //PointF[] points = { new PointF(4, -2), new PointF(3, -1), new PointF(0, 0), new PointF(1, 0) };
      
                  PointF? p = null;
      
                  for (Int32 i = 0; i < 3; i++)
                  {
                      Console.WriteLine("Intersecting segments ({0}|{1}) and ({2}|{3}).", tests[i, 0], tests[i, 1], tests[i, 2], tests[i, 3]);
      
                      Single? f1 = IntersectLines(points[tests[i, 0]], points[tests[i, 1]], points[tests[i, 2]], points[tests[i, 3]]);
                      Single? f2 = IntersectLines(points[tests[i, 2]], points[tests[i, 3]], points[tests[i, 0]], points[tests[i, 1]]);
      
                      if ((f1 != null) && (f2 != null))
                      {
                          PointF pp = PointOnLine(points[tests[i, 0]], points[tests[i, 1]], f1.Value);
      
                          Console.WriteLine("  Lines intersect at ({0}|{1}) with factors {2} and {3}.", pp.X, pp.Y, f1, f2);
      
                          if ((f1 > 0) && (f1 < 1) && (f2 > 0) && (f2 < 1))
                          {
                              Console.WriteLine("  Segments intersect.");
      
                              p = pp;
                          }
                          else
                          {
                              Console.WriteLine("  Segments do not intersect.");
                          }
                      }
                      else
                      {
                          Console.WriteLine("  Lines are parallel.");
                      }
                  }
      
                  if (p == null)
                  {
                      Console.WriteLine("The quadrilateral is concave.");
                  }
                  else
                  {
                      Console.WriteLine("The quadrilateral is convex.");
      
                      for (Int32 j = 0; j < 4; j++)
                      {
                          Console.WriteLine("   Point {0} ({3}|{4}) is the {1} {2} corner.", j, (points[j].Y < p.Value.Y) ? "bottom" : "top", (points[j].X < p.Value.X) ? "left" : "right", points[j].X, points[j].Y);
                      }
                  }
      
                  Console.ReadLine();
              }
      
              private static Single? IntersectLines(PointF a1, PointF a2, PointF b1, PointF b2)
              {
                  PointF r = Difference(a1, b1);
                  PointF a = Difference(a2, a1);
                  PointF b = Difference(b2, b1);
      
                  Single p = r.X * b.Y - r.Y * b.X;
                  Single q = a.Y * b.X - a.X * b.Y;
      
                  return (Math.Abs(q) > Single.Epsilon) ? (p / q) : (Single?)null;
              }
      
              private static PointF Difference(PointF a, PointF b)
              {
                  return new PointF(a.X - b.X, a.Y - b.Y);
              }
      
              private static PointF PointOnLine(PointF a, PointF b, Single f)
              {
                  return new PointF(a.X + f * (b.X - a.X), a.Y + f * (b.Y - a.Y));
              }
          }
      }
      

      输出

      Intersecting segments (0|1) and (2|3).
        Lines intersect at (7|-12.5) with factors -1.5 and -0.5.
        Segments do not intersect.
      Intersecting segments (0|2) and (1|3).
        Lines intersect at (-2|4) with factors -1.5 and -0.5.
        Segments do not intersect.
      Intersecting segments (0|3) and (1|2).
        Lines intersect at (5|-0.4999999) with factors 0.1666667 and 0.5.
        Segments intersect.
      The quadrilateral is convex.
         Point 0 (4|-2) is the bottom left corner.
         Point 1 (2|5) is the top left corner.
         Point 2 (8|-6) is the bottom right corner.
         Point 3 (10|7) is the top right corner.
      

      【讨论】:

      • 这对于类似飞镖的凹四边形是可以的,但对于类似领结的凹形(例如 (0,0) (0,2) (2,0) (2,2) )仍然是一个交点,因此将被检测为凸面。
      • 这些点是无序的,自相交的多边形不被视为评论中所述的开启者。因此,您的示例将被检测为凸多边形 (0,0)(2,0)(2,2)(0,2)。
      • OK 忽略了 cmets 中的点是无序的这一事实。我认为值得在您的回答中澄清这些假设。目前,它的表述方式并不能使其 100% 清楚 IMO。
      猜你喜欢
      • 1970-01-01
      • 2011-07-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-15
      • 2022-12-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多