【问题标题】:Efficiently recognize a rectangular shape from a massive array of points从大量点阵中有效识别矩形
【发布时间】:2021-11-17 05:42:57
【问题描述】:

你们将如何解决这个问题?

我有一系列沿直线路径的设备。 让我们假设所有这些都完全一致。

我的应用程序将构建为带有 WPF 前端和 .NET 5 后端的 C# 桌面项目。

由于我还将实现数据的历史化,我还将实现实体框架来管理 SQL Server 实例。

所有这些设备都能够报告 4000 个点的数组。 这些点以极坐标传递,因此与设备本身的角度和距离。

我将所有这些点都集中在一个数组中。根据部署的配置,阵列中的点数甚至可能超过 40,000 个。

我将这个点数组全部带回笛卡尔坐标 (X, Y) 并旋转平移到单个参考平面中。

在这个星云中识别一系列矩形形状并确定它们的大小的有效方法是什么?

我只知道这一点:

  1. 过滤孤立点。
  2. 过滤可能读取区域之外的点。
  3. 使用 ransac 和共识方法来识别直线。
  4. 过滤线短于可接受的最小值。
  5. 相互交叉线以确定对象的角。

我有一种感觉,现在可能会有一种更具创新性的方法来做到这一点,但我总是忙于要做的事情,以至于我无法找到时间来解决我脑海中的这个错误......

你们有什么想法吗? 提前感谢您的帮助。

附录:

不保证矩形是完全笔直的或以任何方式完美的形状。

这意味着,矩形可以从 x 轴稍微旋转,或者矩形的头部比尾部窄。当然在限制范围内。

【问题讨论】:

  • 我猜你最希望的就是创建一个dictionary<x,y[]>dictionary<y,x[]> 以及一系列循环来检查是否存在一个有效的矩形。您可以使用排序字典,这会使检查更快,尽管它可能不会使过程更有效。我开始为此编写代码,但是我将在工作之前用完时间。我不确定它是否比你拥有的更快
  • 这是我的代码,dotnetfiddle.net/HDlBkE 它可能对您或其他任何人有用也可能没用,并且不完整并且可能有各种问题:)
  • 感谢您明天早上的支持,我会尝试crossfingers找时间打开它。感谢您抽出宝贵时间:)

标签: c# arrays shapes point lidar


【解决方案1】:

我想最好的方法是使用字典,尽管您必须对任何一种方法进行基准测试,但我选择了排序字典。

免责声明:我只做了最少的测试,可能完全错误。

给定

public static class Extensions
{
   public static SortedDictionary<TKey, TValue> ToSortedDictionary<TKey,TValue>(this Dictionary<TKey, TValue> existing) => new(existing);
}

实施

public static IEnumerable<Rectangle> Enumerate(SortedDictionary<int, int[]> xs, SortedDictionary<int, int[]> ys)
{
   // iterate the x dictionary
   foreach (var (x1, value) in xs)
   {
      // get all y values
      foreach (var y1 in value)
      {
         // does the Y dictionary contain the y ?
         if(!ys.TryGetValue(y1, out var x2s)) continue;

         // get all xs where is greater then the original
         foreach (var x2 in x2s.Where(xa => xa > x1))
         {

            // check if x exists in x dictionary
            if (!xs.TryGetValue(x2, out var y2s)) continue;

            foreach (var y2 in y2s.Where(ya => ya >y1))
            {
               if (!ys.ContainsKey(y2)) continue;

               yield return new Rectangle(x1, y1, x2 - x1, y2 - y1);

               break;
            }
         }
      }
   }
}

用法

var points = new Point[]
{
   new(1, 1),
   new(1, 2),
   new(1, 3),
   new(2, 3),
   new(3, 3),
   new(3, 1),
   new(3, 2),
   new(3, 3),
};

var xs = points
   .GroupBy(p => p.X, p => p.Y)
   .ToDictionary(g => g.Key, g => g.ToArray())
   .ToSortedDictionary();

var ys = points
   .ToLookup(p => p.Y, p => p.X)
   .ToDictionary(g => g.Key, g => g.ToArray())
   .ToSortedDictionary();

Console.Write(string.Join(Environment.NewLine, Enumerate(xs, ys)));

输出

{X=1,Y=1,Width=2,Height=2}
{X=1,Y=2,Width=2,Height=1}

Full Demo Here

基准测试

总结

速度取决于您希望找到多少个矩形,在这种情况下,我使用的范围是 0..10000 x/y。我相信你可以更快地得到它。它在内存和速度上并没有完全线性扩展,但绝对不是二次时间复杂度或更糟。

注意我已经包含了一个粗略的并行版本,你期望找到的矩形越多,它会运行得越好,在这个化身中,并行版本可以在我的系统上大约 80 毫秒内完成 100,000 个点

环境

BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19043.1237 (21H1/May2021Update)
Intel Core i7-7700 CPU 3.60GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
.NET SDK=5.0.401
  [Host]   : .NET 5.0.10 (5.0.1021.41214), X64 RyuJIT  [AttachedDebugger]
  .NET 5.0 : .NET 5.0.10 (5.0.1021.41214), X64 RyuJIT

Job=.NET 5.0  Runtime=.NET 5.0

结果

Method N Mean Error StdDev Allocated
Single 100 62.02 us 0.841 us 0.745 us 76 KB
Para 100 119.63 us 1.375 us 1.286 us 165 KB
Single 1000 827.67 us 16.159 us 15.870 us 733 KB
Para 1000 652.92 us 12.901 us 28.317 us 959 KB
Single 10000 11,761.19 us 220.978 us 415.050 us 5,454 KB
Para 10000 7,281.76 us 148.422 us 430.598 us 6,591 KB
Single 100000 260,526.78 us 4,947.544 us 5,293.817 us 63,186 KB
Para 100000 80,518.47 us 1,595.902 us 3,150.154 us 72,006 KB
Single 1000000 23,062,589.42 us 424,680.649 us 397,246.542 us 4,342,997 KB
Para 1000000 4,907,088.21 us 33,959.608 us 30,104.308 us 5,127,048 KB

完整的测试代码

[SimpleJob(RuntimeMoniker.Net50)]
[MemoryDiagnoser()]
public class Points
{

   private Point[] _points;

   [Params(100, 1000, 10000, 100000, 1000000)] public int N;

   [GlobalSetup]
   public void GetPoints()
   {
      var r = new Random(32);
      _points = Enumerable
         .Range(0, N).Select(x =>
            new Point(
               r.Next(10000),
               r.Next(10000)))
         .ToArray();

   }

   public static IEnumerable<Rectangle> Enumerate(SortedDictionary<int, int[]> xs, SortedDictionary<int, int[]> ys)
   {
      // iterate the x dictionary
      foreach (var (x1, value) in xs)
      foreach (var y1 in value) // get all y values
      {
         // does the Y dictionary contain the y ?
         if (!ys.TryGetValue(y1, out var x2s)) continue;

         // get all xs where is greater then the original
         foreach (var x2 in x2s.Where(xa => xa > x1))
         {
            // check if x exists in x dictionary
            if (!xs.TryGetValue(x2, out var y2s)) continue;

            foreach (var y2 in y2s.Where(ya => ya > y1))
            {
               if (!ys.ContainsKey(y2)) continue;
               yield return new Rectangle(x1, y1, x2 - x1, y2 - y1);
               break;
            }
         }
      }

   }

   public static IEnumerable<Rectangle> EnumerateParallel(SortedDictionary<int, int[]> xs, SortedDictionary<int, int[]> ys)
   {
      IEnumerable<Rectangle> Rectangles(int[] value, int x1)
      {
         foreach (var y1 in value) // get all y values
         {
            // does the Y dictionary contain the y ?
            if (!ys.TryGetValue(y1, out var x2s)) continue;

            // get all xs where is greater then the original
            foreach (var x2 in x2s.Where(xa => xa > x1))
            {
               // check if x exists in x dictionary
               if (!xs.TryGetValue(x2, out var y2s)) continue;

               foreach (var y2 in y2s.Where(ya => ya > y1))
               {
                  if (!ys.ContainsKey(y2)) continue;
                  yield return new Rectangle(x1, y1, x2 - x1, y2 - y1);
                  break;
               }
            }
         }
      }

      return xs.AsParallel().AsUnordered().SelectMany(x => Rectangles(x.Value, x.Key));

   }
   [Benchmark]
   public Rectangle[] Single()
   {
      var xs = _points
         .GroupBy(p => p.X, p => p.Y)
         .ToDictionary(g => g.Key, g => g.ToArray())
         .ToSortedDictionary();

      var ys = _points
         .ToLookup(p => p.Y, p => p.X)
         .ToDictionary(g => g.Key, g => g.ToArray())
         .ToSortedDictionary();

      return Enumerate(xs, ys)
         .ToArray();
   }

   
   [Benchmark]
   public Rectangle[] Para()
   {
      var task1 = Task.Run(() => _points
         .GroupBy(p => p.X, p => p.Y)
         .ToDictionary(g => g.Key, g => g.ToArray())
         .ToSortedDictionary());

      var task2 = Task.Run(() =>_points
         .ToLookup(p => p.Y, p => p.X)
         .ToDictionary(g => g.Key, g => g.ToArray())
         .ToSortedDictionary());

      return EnumerateParallel(task1.Result, task2.Result)
         .ToArray();
   }
}

【讨论】:

  • 哇哦,天哪,我没想到会收到如此广泛而完整的答案!非常感谢先生!我一定会试一试的!
猜你喜欢
  • 1970-01-01
  • 2016-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-09
相关资源
最近更新 更多