【问题标题】:How to iterate through neighbours in 2D coordinates?如何遍历二维坐标中的邻居?
【发布时间】:2021-04-23 22:21:51
【问题描述】:

在二维平面中,我想检查相邻点直到满足条件。例如,在此图像中取一个红色像素 (x,y)。我想迭代找到最接近所选红色像素的白色像素。

这是@Pranav Hosangadi提供的更新图片

我的原始代码现在无关紧要了。

一种可能的解决方案是创建一个按距离排序的数组(在图像上给出)。然后,通过循环遍历这个数组,检查何时满足条件(例如,该点是白色的)。但是,这个数组将是不确定的,并且是多余的。

// $x,$y is a given point
$array = array(
'0,0'=>0,
'-1,0'=>1,
'-1,-1'=>1.4
);

asort($array)

foreach($array as $a=>$b){
$c=explode(',',$a);
$x2=$c[0]; // x-axis distance from the given point
$y2=$c[1]; // y-axis distance from the given point
$x3=$x+$x2;
$y3=$y+$y2;
if( $x3,$y3 point is white) { break;}
}

$closest_distance=$b;

因此,我们需要在没有预定义$array的情况下构建循环。

【问题讨论】:

  • 周边点可以是4连通的,也可以是8连通的,即邻居是南、西、北、东,也可以包括西南、西北、东北、南-东方。您在代码中使用哪一种?
  • 您仍然需要定义“最接近”。可以对角线在你的棋盘上移动吗?
  • @Googlebot 简单地从起始像素向外螺旋的问题是,当您向外一层时,到起始像素的距离会减小。例如,在i.stack.imgur.com/Z1XFD.png 中,距离 4.2 的像素将位于距离 4.1 和 4 的像素之前。
  • 您可以使用Bresenham circle-finding algorithm 并寻找与增加的半径相匹配的点,但这仍然会留下空隙。例如在this image 中:整数半径从 6 增加到 15 的算法找到的点被着色(非紫色),但某些像素从未被选中。
  • @El_Vanja 你说的有道理——除非你知道原点,否则你无法回答“哪个点是最近的白点”这个问题。我是说起源知识对 OP 的问题无关紧要,即 他们可以使用什么算法。在图像中选择您最喜欢的点作为原点。只要你的原点在整个练习过程中保持不变,它就不会影响算法。

标签: python php loops coordinates pixel


【解决方案1】:

您可以创建一个坐标偏移列表,按照它们到基点的距离排序。那么你的搜索功能写起来会简单得多:

maxRows,maxCols = 16,16
offsets = (offset for dx in range(maxRows) for dy in range(maxCols)
                  for offset in [(dx,dy),(dx,-dy),(-dx,dy),(-dx,-dy)])
offsets = sorted(offsets,key=lambda xy:xy[0]**2+xy[1]**2)[1:]


def closestPoint(pixels,row,col,value):
    for dx,dy in offsets:
        r,c = row+dy,col+dx
        if r not in range(len(pixels)): continue
        if c not in range(len(pixels[0])): continue
        if pixels[r][c] == value: return r,c
    return None,None

输出:

bitmap = [    
[4, 2, 1, 2, 7, 3, 7, 2, 8, 7, 7, 8, 1, 7, 7, 6],
[6, 8, 3, 7, 1, 7, 8, 8, 8, 2, 4, 5, 8, 2, 5, 4],
[3, 2, 1, 6, 2, 7, 2, 2, 2, 2, 8, 2, 3, 5, 2, 7],
[8, 4, 4, 5, 2, 6, 7, 2, 6, 5, 6, 4, 5, 6, 7, 8],
[2, 6, 7, 2, 8, 6, 6, 8, 7, 6, 8, 6, 2, 8, 2, 8],
[6, 1, 7, 3, 7, 8, 8, 3, 0, 5, 6, 8, 4, 4, 5, 2],
[1, 5, 1, 7, 4, 5, 2, 2, 3, 7, 3, 3, 4, 2, 5, 4],
[1, 8, 5, 8, 1, 8, 2, 5, 4, 5, 4, 7, 2, 5, 7, 5],
[6, 6, 8, 6, 2, 6, 4, 1, 3, 4, 3, 6, 3, 6, 3, 7],
[6, 6, 5, 7, 3, 7, 3, 8, 1, 8, 2, 6, 3, 8, 2, 3],
[7, 5, 3, 1, 4, 3, 4, 3, 3, 5, 8, 5, 4, 4, 3, 3],
[2, 2, 1, 5, 1, 6, 6, 3, 8, 4, 1, 5, 2, 2, 1, 6],
[6, 2, 7, 1, 7, 4, 3, 5, 8, 6, 6, 3, 1, 2, 8, 5],
[1, 5, 1, 2, 2, 6, 3, 2, 3, 7, 4, 5, 6, 1, 8, 1],
[7, 6, 5, 2, 7, 2, 4, 8, 5, 1, 1, 4, 6, 1, 7, 7],
[2, 7, 3, 2, 1, 8, 6, 1, 6, 7, 4, 6, 7, 6, 2, 4]]

for p in range(1,9):
    print(p,closestPoint(bitmap,5,8,p))   

1 (8, 7)
2 (6, 7)
3 (6, 8)
4 (7, 8)
5 (5, 9)
6 (4, 9)
7 (4, 8)
8 (4, 7)

请注意,如果您要检查矩阵中的每个位置,使用邻近优化算法创建距离矩阵会更有效。

通过偏移量达到最大距离将跳过 75% 的结果位置。如果您事先不知道最大尺寸,或者如果您想避免在偏移循环中跳过无用的位置,您可以保留一个尺寸位置字典,该字典映射到给定基数的有效位置:

posByDist = dict()
def getPositionsByDistance(height,width,r,c):
    positions = posByDist.get((height,width,r,c),[])
    if not positions:
        offsets = ((dr*dr+dc*dc,dr,dc) for dx in range(height) 
                                       for dy in range(width)
                    for dr,dc in [(dx,dy),(dx,-dy),(-dx,dy),(-dx,-dy)]
                    if r+dr in range(height) and c+dc in range(width) )        
        positions = [(r+dr,c+dc) for _,dr,dc in sorted(offsets)]
        posByDist[height,width,r,c] = positions
    return positions

def closestPoint(pixels,row,col,value):
    h,w = len(pixels),len(pixels[0])
    return next(((r,c) for r,c in getPositionsByDistance(h,w,row,col)
                 if pixels[r][c] == value),(None,None))

这只会遍历有效位置,并会根据您实际遇到的尺寸和基本位置的需要动态创建和重复使用位置图。

【讨论】:

    【解决方案2】:

    即使这个问题已经有一个可接受的答案,我想添加我的两位(和一个 PHP 实现)与一个算法,以满足返回所有最接近像素的附加请求。

    算法的描述(不知道这个有没有名字,我只是想办法):展开到起点周围的外缘,并在这些像素之间寻找匹配。如果没有找到,则移动到下一个边缘,依此类推,直到找到匹配项或您已击中所有边的边界(整个图像中没有匹配项)。可视化可能有助于理解;假设0为起点,则1为第一轮,2为第二,3为第三:

    +---+---+---+---+---+---+---+---+---+
    |   |   | 3 | 3 | 3 | 3 | 3 | 3 | 3 |
    +---+---+---+---+---+---+---+---+---+
    |   |   | 3 | 2 | 2 | 2 | 2 | 2 | 3 |
    +---+---+---+---+---+---+---+---+---+
    |   |   | 3 | 2 | 1 | 1 | 1 | 2 | 3 |
    +---+---+---+---+---+---+---+---+---+
    |   |   | 3 | 2 | 1 | 0 | 1 | 2 | 3 |
    +---+---+---+---+---+---+---+---+---+
    |   |   | 3 | 2 | 1 | 1 | 1 | 2 | 3 |
    +---+---+---+---+---+---+---+---+---+
    |   |   | 3 | 2 | 2 | 2 | 2 | 2 | 3 |
    +---+---+---+---+---+---+---+---+---+
    

    为了避免在到达较远的边缘时处理大量元素,我们不会扩展到所有边缘元素,而是首先扩展到最近的像素,然后是下一个最近的像素,依此类推,遵守以下规则:

    • 任何边缘中最近的像素是其中一个坐标保持不变而另一个坐标偏移边缘距离的像素。例如,在轮辋 2 中,最接近的是 dx = 0 && dy = +/-2dx = +/-2 && dy = 0 的位置。
    • 任何边缘中下一个最接近的像素是之前未更改的坐标现在偏移 1 的像素,因此在我们的示例中为 dx = +/-1 && dy = +/-2dx = +/-2 && dy = +/-1
    • 我们不断将此变量偏移量增加 1,直到它等于固定偏移量(轮辋距离,在我们的示例中为 2)。

    此逐步扩展的视觉帮助(S 是起点,o 标记我们在给定步骤中获取的像素,- 标记我们已经检查过的像素):

    Step 1:
    +---+---+---+---+---+
    |   |   |   |   |   |
    +---+---+---+---+---+
    |   |   | o |   |   |
    +---+---+---+---+---+
    |   | o | S | o |   |
    +---+---+---+---+---+
    |   |   | o |   |   |
    +---+---+---+---+---+
    |   |   |   |   |   |
    +---+---+---+---+---+
    
    Step 2:
    +---+---+---+---+---+
    |   |   |   |   |   |
    +---+---+---+---+---+
    |   | o | - | o |   |
    +---+---+---+---+---+
    |   | - | S | - |   |
    +---+---+---+---+---+
    |   | o | - | o |   |
    +---+---+---+---+---+
    |   |   |   |   |   |
    +---+---+---+---+---+
    
    Step 3:
    +---+---+---+---+---+
    |   |   | o |   |   |
    +---+---+---+---+---+
    |   | - | - | - |   |
    +---+---+---+---+---+
    | o | - | S | - | o |
    +---+---+---+---+---+
    |   | - | - | - |   |
    +---+---+---+---+---+
    |   |   | o |   |   |
    +---+---+---+---+---+
    
    Step 4:
    +---+---+---+---+---+
    |   | o | - | o |   |
    +---+---+---+---+---+
    | o | - | - | - | o |
    +---+---+---+---+---+
    | - | - | S | - | - |
    +---+---+---+---+---+
    | o | - | - | - | o |
    +---+---+---+---+---+
    |   | o | - | o |   |
    +---+---+---+---+---+
    
    Step 5:
    +---+---+---+---+---+
    | o | - | - | - | o |
    +---+---+---+---+---+
    | - | - | - | - | - |
    +---+---+---+---+---+
    | - | - | S | - | - |
    +---+---+---+---+---+
    | - | - | - | - | - |
    +---+---+---+---+---+
    | o | - | - | - | o |
    +---+---+---+---+---+
    

    这种算法的优点是它实际上不需要任何距离计算,并且一次处理的像素不会超过 8 个,无论我们离起点多远。缺点是迭代非常繁重。它有利于找到相近的匹配。

    PHP 实现(假设左上角为根[0, 0]):

    /**
     * Finds pixels with the lowest geometrical distance from given point (x, y) that match the criteria.
     */
    function findGeometricallyClosestPixels(array $grid, int $x, int $y, string $criteria): array
    {
        // if grid is empty or we're out of bounds
        if (empty($grid) || !isset($grid[$y]) || !isset($grid[0][$x])) {
            return [];
        }
        if ($grid[$y][$x] === $criteria) {
            return [[$x, $y]];
        }
        $result = [];
        for ($offset = 1; $offset < calculateMaximumOffset($grid, $x, $y); $offset++) {
            $result = array_merge($result, getClosestPixels($grid, $x, $y, $offset, $criteria));
            if (!empty($result)) {
                return $result;
            }
            for ($d = 1; $d <= $offset; $d++) {
                $result = array_merge($result, getPixelsForNegativeYOffsets($grid, $x, $y, $offset, $d, $criteria));
                $result = array_merge($result, getPixelsForPositiveYOffsets($grid, $x, $y, $offset, $d, $criteria));
                if (!empty($result)) {
                    return $result;
                }
            }
        }
    
        return $result;
    }
    
    function getClosestPixels(array $grid, int $x, int $y, int $offset, string $criteria): array
    {
        $result = [];
        if (isset($grid[$y][$x - $offset]) && $grid[$y][$x - $offset] === $criteria) {
            $result[] = [$x - $offset, $y];
        }
        if (isset($grid[$y][$x + $offset]) && $grid[$y][$x + $offset] === $criteria) {
            $result[] = [$x + $offset, $y];
        }
        if (isset($grid[$y - $offset][$x]) && $grid[$y - $offset][$x] === $criteria) {
            $result[] = [$x, $y - $offset];
        }
        if (isset($grid[$y + $offset][$x]) && $grid[$y + $offset][$x] === $criteria) {
            $result[] = [$x, $y + $offset];
        }
    
        return $result;
    }
    
    function getPixelsForNegativeYOffsets(array $grid, int $x, int $y, int $offset, int $d, string $criteria): array
    {
        if (!isset($grid[$y - $d])) {
            return [];
        }
        $result = [];
        if (isset($grid[$y - $d][$x - $offset]) && $grid[$y - $d][$x - $offset] === $criteria) {
            $result[] = [$x - $offset, $y - $d];
        }
        if (isset($grid[$y - $d][$x + $offset]) && $grid[$y - $d][$x + $offset] === $criteria) {
            $result[] = [$x + $offset, $y - $d];
        }
        if (!isset($grid[$y - $offset])) {
            return $result;
        }
        if (isset($grid[$y - $offset][$x - $d]) && $grid[$y - $offset][$x - $d] === $criteria) {
            $result[] = [$x - $d, $y - $offset];
        }
        if (isset($grid[$y - $offset][$x + $d]) && $grid[$y - $offset][$x + $d] === $criteria) {
            $result[] = [$x + $d, $y - $offset];
        }
    
        return $result;
    }
    
    function getPixelsForPositiveYOffsets(array $grid, int $x, int $y, int $offset, int $d, string $criteria): array
    {
        if (!isset($grid[$y + $d])) {
            return [];
        }
        $result = [];
        if (isset($grid[$y + $d][$x + $offset]) && $grid[$y + $d][$x + $offset] === $criteria) {
            $result[] = [$x + $offset, $y + $d];
        }
        if (isset($grid[$y + $d][$x - $offset]) && $grid[$y + $d][$x - $offset] === $criteria) {
            $result[] = [$x - $offset, $y + $d];
        }
        if (!isset($grid[$y + $offset])) {
            return $result;
        }
        if (isset($grid[$y + $offset][$x - $d]) && $grid[$y + $offset][$x - $d] === $criteria) {
            $result[] = [$x - $d, $y + $offset];
        }
        if (isset($grid[$y + $offset][$x + $d]) && $grid[$y + $offset][$x + $d] === $criteria) {
            $result[] = [$x + $d, $y + $offset];
        }
    
        return $result;
    }
    
    /**
     * Calculates maximum offset needed to reach the edge of given grid for given starting point.
     *
     * Lowest possible offset is right in the middle of the greater grid dimension (width or height). Since grid starts
     * at zero, if the point is positioned past the middle, then offset will equal its position. Otherwise, it will equal
     * greater dimension minus its position increased by one (to compensate for the fact that dimensions are measured
     * starting at 1, but positions start at 0).
     */
    function calculateMaximumOffset(array $grid, int $x, int $y): int
    {
        $gridHeight = count($grid);
        $gridWidth = count($grid[0]);
        if ($gridHeight >= $gridWidth) {
            $relevantDimensionValue = $gridHeight;
            $relevantCoordinateValue = $y;
        } else {
            $relevantDimensionValue = $gridWidth;
            $relevantCoordinateValue = $x;
        }
        $half = floor($relevantDimensionValue / 2);
        if ($relevantCoordinateValue >= $half) {
            return $relevantCoordinateValue;
        }
        return $relevantDimensionValue - ($relevantCoordinateValue + 1);
    }
    

    这里的条件是幼稚的(我们正在匹配的一个简单字符串),实际上它可能最好作为回调,因此可以在每个网格元素上使用任何自定义逻辑。

    这个实现可能会被优化,但我会在以后解决它。我只做了一些非常基本的优化。

    整个东西都可以在线测试here。我使用了问题中心脏图像的数组表示,其中wr 代表白色和红色像素。

    【讨论】:

    • 哇,你的回答给我留下了深刻的印象!
    猜你喜欢
    • 2017-12-23
    • 2018-07-03
    • 1970-01-01
    • 2021-05-23
    • 2014-11-29
    • 2011-07-18
    • 2014-06-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多