簡體   English   中英

如何遍歷二維坐標中的鄰居?

[英]How to iterate through neighbours in 2D coordinates?

在二維平面中,我想檢查相鄰點直到滿足條件。 例如,在此圖像中取一個紅色像素 ( 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的情況下構建循環。

您可以創建一個坐標偏移列表,按它們到基點的距離排序。 那么你的搜索 function 寫起來會簡單得多:

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

output:

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)

請注意,如果您要對矩陣中的每個 position 進行檢查,則使用鄰近優化算法創建距離矩陣會更有效。

通過偏移達到最大距離將跳過 75% 的結果位置。 如果您事先不知道最大大小,或者如果您想避免在偏移循環中跳過無用的 position,您可以保留一個大小位置字典,該字典映射到給定基數的有效位置:

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))

這只會循環通過有效位置,並將根據您實際遇到的尺寸和基本位置的需要動態創建和重用 position 地圖。

即使這個問題已經有一個可接受的答案,我想添加我的兩個位(和一個 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 個像素,無論離起點有多遠我們 go。 缺點是迭代非常繁重。 它有利於尋找接近的匹配。

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

這里的條件是幼稚的(我們正在匹配的一個簡單字符串),實際上它可能最好作為回調,因此可以在每個網格元素上使用任何自定義邏輯。

這個實現可能會被優化,但我會在以后解決它。 我只做了一些非常基本的優化。

整個事情可以在這里在線測試。 我使用了問題中心臟圖像的數組表示,其中wr代表白色和紅色像素。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM