【问题标题】:Footprint finding algorithm足迹查找算法
【发布时间】:2016-07-16 08:21:29
【问题描述】:

我正在尝试提出一种算法来优化多边形(或多个多边形)的形状,以最大化该形状中包含的值。

我有 3 列的数据:

  • X:x 轴上的位置
  • Y:y 轴上的位置
  • 值:块的值,可以有正值和负值。

此数据来自规则网格,因此每个 x 和 y 值之间的间距是一致的。

我想创建一个边界多边形,通过添加的条件最大化包含的值。

  • 多边形的所有点都需要保持最小半径。这意味着我们要么失去一些正值块,要么获得一些负值块。

我正在使用的当前算法执行以下操作

  1. 查找最大块值作为起点(或用户定义)
  2. 找到最小半径内的所有方块,并通过检查整体值为正来确定它是否是可行的点
  3. 从进一步的值计算中删除最小搜索半径内的所有块,并将它们标记为最终形状的一部分
  4. 移动到由围绕原始点的螺旋确定的下一个点。 (中心始终是一个网格点,因此移动 deltaX 或 deltaY)

这似乎正在拾取一些不需要的单元格。我确定那里有形状算法,但我不知道要查找什么来寻求帮助。

下面是一张图片,希望有助于概述问题。阳性细胞以红色显示(阴性细胞未显示)。黑色轮廓显示了我当前例程返回的形状。我认为应该更多地引入左侧。最小半径100m左下黑圈大概是这个。

现在代码在 R 中运行,但如果我能得到正确的算法,我可能会转向别的东西。

针对不清楚的投票,我试图在没有背景或尝试解决方案的情况下解决的问题是:

“围绕一系列点创建一个边界多边形(或多个多边形)以最大化包含的值,同时保持沿多边形的最小曲率半径”

编辑:

数据

我应该包含一些可以在here 找到的数据。

文件是 csv。 4 列(X、Y、Z [未使用]、值),长度约为 25k,大小为 800kb。

【问题讨论】:

  • 你如何定义多边形的“曲率半径”?
  • 基本上在多边形的任何点,您都应该能够拟合一个半径为 R 的(粗)圆。在图像中,左下方的黑色圆圈是可以选择的最小尺寸(这就是为什么它占用了这么多的负值空白)。我的多边形跟随网格,这就是为什么它是一个粗糙的圆圈,这很好。
  • 我想我明白了。这与通常所说的“曲率”略有不同(例如:在更传统的定义下,不允许在 X=421500 和 Y=6259100 附近的锐利凹入区域)。说多边形 P 内的每个点都可以被完全位于 P 内的半径为 R 的圆所覆盖就足够了吗?特别是,即使中间部分有点窄,你会接受看起来像经典Venn diagram 的多边形吗?
  • @gtwebb 有趣的问题是为了取证目的吗? (例如,比较不同材料中的足迹样本,尽可能减少误差?)
  • @mhum,是的,我会接受维恩图。为缺乏明确性表示歉意,不确定定义它的最佳方式。

标签: algorithm image-processing 2d polygon


【解决方案1】:

图形方法

我会以图形方式处理这个问题。我的直觉告诉我,内部点完全位于铸造圆内,最小半径为r,距离附近的所有足迹点。这意味着如果您从半径为r 的每个足迹点投射圆,那么所有位于所有相邻圆的至少一半内的所有点都在您的多边形内。如果您深入多边形内部,那么为了不那么模糊,那么您在任何像素处都会得到Pi*r^2 这样的重叠圆圈。如果你处于优势,你得到了一半。这很容易计算。

首先我需要数据集。正如你只提供了 jpg 文件,我没有 vales 只是情节。所以我像处理二进制图像一样处理这个问题。首先,我需要重新着色图像以消除 jpg 颜色失真。之后这是我的输入:

我选择黑色背景来轻松地在图像上应用加法数学,而且我更喜欢它而不是白色,并将足迹保留为红色(最大饱和度)。现在算法:

  1. 创建临时图像

    它应该是相同的大小并清除为黑色(color=0)。像处理重叠圆圈的整数计数器一样处理它的像素。

  2. 投射圈子

    对于source image 中的每个红色 像素,将+1 添加到具有最小半径的圆内的每个像素r 在同一像素周围但在temp image 中。结果是这样的(蓝色是我pixelformat的低位):

    作为r,我使用r=24,因为这是您示例中左下角的圆半径+/-像素。

  3. 仅选择内部像素

    所以重新着色临时图像。所有带有color < 0.5*pi*r^2 的像素都重新着色为黑色,其余像素重新着色为红色。结果是这样的:

  4. 仅选择多边形圆周点

    只需将黑色像素附近的所有 红色 像素重新着色为某种中性色 蓝色,其余为 黑色。结果:

    现在只需将结果多边形化。要与输入图像进行比较,您可以将它们两者结合起来(我 OR 将它们组合在一起):

[备注]

您可以使用最小半径或区域阈值属性来实现不同的行为。但我认为这与您的问题非常接近。

这里有一些 C++ 源代码:

//picture pic0,pic1;
    // pic0 - source
    // pic1 - output/temp
int x,y,xx,yy;
const int r=24;                 // min radius
const int s=float(1.570796*float(r*r));     // half of min radius area
const DWORD c_foot=0x00FF0000;  // red
const DWORD c_poly=0x000000FF;  // blue
// resize and clear temp image
pic1=pic0;
pic1.clear(0);
// add min radius circle to temp around any footprint pixel found in input image
for (y=r;y<pic1.ys-r;y++)
 for (x=r;x<pic1.xs-r;x++)
  if (pic0.p[y][x].dd==c_foot)
   for (yy=-r;yy<=r;yy++)
    for (xx=-r;xx<=r;xx++)
     if ((xx*xx)+(yy*yy)<=r*r)
      pic1.p[y+yy][x+xx].dd++;
pic1.save("out0.png");
// select only pixels which are inside footprint with min radius (half of area circles are around)
for (y=0;y<pic1.ys;y++)
 for (x=0;x<pic1.xs;x++)
  if (pic1.p[y][x].dd>=s) pic1.p[y][x].dd=c_foot;
   else                   pic1.p[y][x].dd=0;
pic1.save("out1.png");
// slect only outside pixels
pic1.growfill(c_foot,0,c_poly);
for (y=0;y<pic1.ys;y++)
 for (x=0;x<pic1.xs;x++)
  if (pic1.p[y][x].dd==c_foot) pic1.p[y][x].dd=0;
pic1.save("out2.png");
pic1|=pic0; // combine in and out images to compare
pic1.save("out3.png");

我使用自己的 picture 类来处理图像,所以一些成员是:

  • xs,ys 图像大小(以像素为单位)
  • p[y][x].dd(x,y) 位置的像素,为32 位整数类型
  • clear(color) - 清除整个图像
  • resize(xs,ys) - 将图像调整为新分辨率

[Edit1]我在源代码中发现了一个小错误

我注意到有些边缘太锋利,所以我检查了代码,但我忘了在填充时添加圆形条件,所以它填充了正方形。我修复了上面的源代码。我真的只是添加了行if ((xx*xx)+(yy*yy)&lt;=r*r)。结果略有变化,所以我也用新结果更新了图像

我玩的是内区系数比和这个:

const int s=float(0.75*1.570796*float(r*r));

为您带来更好的匹配。它越小,多边形可以与外部足迹重叠的越多。结果:

【讨论】:

  • 乍一看,这似乎是一个很好的答案。今天晚些时候我会再深入研究一下(我整个周末都不在家。)
  • 这绝对是一个有趣的问题。一个问题(我不提供数据引起的)是它的二进制方面。此外,它似乎可能会采用比它需要的更多的负面材料(许多地方看起来像是一个像素缓冲区)。使用实际数据,为每个重叠的圆圈添加值而不是标准的 1 可能是最简单的,但我不确定使用这个图形过程会有多容易。
  • @gtwebb 没有附加价值没有帮助。它会打破这背后的数学原理。加 1 表示像素在足迹区域内的深度。如果您需要更少的重叠,那么只需增加s 系数s=(0, 2.Pi*r^2&gt;。我选择s,所以它类似于您的输出。要使用价值,您可以在这一切之前将图像if (value&gt;=treshold) pixel is inside;二值化
  • @gtwebb 您在点 15 单位和直径 7*15 单位之间有步长,这不是太多的像素可以使用。可能用一些样条线放大会更好。 Value 的含义是什么?除了if (value&gt;=0) set_pixel(x,y) 之外,没有适当的知识很难实现它。我发现-2200000 是背景,上面的所有值看起来都不像脚(更像是某种靴子)可用值是&gt;-100000 较小的可能由于扫描方法和/或张力反射/交互而导致一些伪影
  • 这个问题实际上与挖矿有关。足迹是矿山的底层,价值是像素产生的收入减去移除它的成本。曲率与他们可以挖掘的区域有多小有关。
【解决方案2】:

如果解决方案集必须是给定半径的磁盘的联合,我会尝试 greedy 方法。 (我怀疑这个问题可能是棘手的 - 指数运行时间 - 如果你想要一个精确的解决方案。)

对于所有像素(您的“块”),计算其周围磁盘中值的总和,并取总和最高的那个。标记此像素并通过减去其值来调整其磁盘中所有像素的总和,因为标记的像素已被“消耗”。然后扫描边缘或角与其接触的所有像素,并标记总和最高的像素。

继续此过程,直到所有总和均为负数。那么总和就不能再增加了。

为了有效地实现,您需要保留一个边界像素列表,即与标记像素相邻的未标记像素。在您选择了总和最大的边界像素并对其进行标记后,将其从列表中删除并重新计算其磁盘内未标记像素的总和;您还添加了触摸它的未标记像素。

在图片上,像素标记为蓝色,边框像素标记为绿色。突出显示的像素是

  • 被标记的那个,
  • 需要重新计算总和的那些。

计算时间将与图像面积乘以圆盘面积(用于求和的初始计算)加上形状面积乘以圆盘面积(用于更新总和)成正比),加上形状生长时连续周长的总和(以找到最大的总和)。 [由于后面的术语可能成本很高 - 按形状面积乘以周长的顺序 - 建议使用 heap 数据结构,这将减少它们的对数之和的长度。]

【讨论】:

  • 我喜欢这个解决方案的声音,它听起来就像我开始使用的相同基础加上一些我需要解决的额外的准确性(和复杂性)。
  • @gtwebb:是的。最困扰我的是最优性问题(一定要找到最佳解决方案)。我不知道如何解决。
  • 我选择了这个答案,因为它更接近我实际实施的内容。感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-05
  • 2016-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-01
相关资源
最近更新 更多