[英]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 |
+---+---+---+---+---+---+---+---+---+
為了避免在到達較遠的邊緣時必須處理大量元素,而不是擴展到所有邊緣元素,我們首先擴展到最近的像素,然后是下一個最近的像素,依此類推,遵循這些規則:
dx = 0 && dy = +/-2
和dx = +/-2 && dy = 0
的地方。dx = +/-1 && dy = +/-2
和dx = +/-2 && dy = +/-1
. 這種逐漸擴展的視覺幫助( 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);
}
這里的條件是幼稚的(我們正在匹配的一個簡單字符串),實際上它可能最好作為回調,因此可以在每個網格元素上使用任何自定義邏輯。
這個實現可能會被優化,但我會在以后解決它。 我只做了一些非常基本的優化。
整個事情可以在這里在線測試。 我使用了問題中心臟圖像的數組表示,其中w
和r
代表白色和紅色像素。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.