Hier ist ein Weg, dies mit scipy.ndimage.distance_transform_cdt
tun können (oder scipy.ndimage.distance_transform_bf
):
import numpy as np
from scipy.ndimage import distance_transform_cdt
def distance_from_edge(x):
x = np.pad(x, 1, mode='constant')
dist = distance_transform_cdt(x, metric='taxicab')
return dist[1:-1, 1:-1]
Zum Beispiel:
In [327]: a
Out[327]:
array([[0, 0, 0, 0],
[0, 1, 1, 1],
[0, 1, 1, 1],
[0, 1, 1, 1]])
In [328]: distance_from_edge(a)
Out[328]:
array([[0, 0, 0, 0],
[0, 1, 1, 1],
[0, 1, 2, 1],
[0, 1, 1, 1]], dtype=int32)
In [329]: x
Out[329]:
array([[1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0],
[1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0],
[1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0],
[0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]])
In [330]: distance_from_edge(x)
Out[330]:
array([[1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
[1, 2, 2, 2, 2, 1, 0, 0, 0, 1, 0, 0],
[1, 2, 3, 3, 2, 1, 0, 0, 1, 2, 1, 0],
[1, 2, 3, 3, 2, 1, 0, 0, 0, 1, 0, 0],
[1, 2, 3, 3, 2, 1, 0, 0, 0, 0, 0, 0],
[1, 1, 2, 2, 2, 1, 0, 0, 0, 1, 1, 0],
[0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 2, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]], dtype=int32)
Wenn Sie mit Nullen nicht Pad, um das Array zu tun, erhalten Sie den Abstand zur nächstgelegenen 0 im Array:
In [335]: distance_transform_cdt(a, metric='taxicab')
Out[335]:
array([[0, 0, 0, 0],
[0, 1, 1, 1],
[0, 1, 2, 2],
[0, 1, 2, 3]], dtype=int32)
In [336]: distance_transform_cdt(x, metric='taxicab')
Out[336]:
array([[6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0],
[5, 5, 4, 3, 2, 1, 0, 0, 0, 1, 0, 0],
[4, 4, 4, 3, 2, 1, 0, 0, 1, 2, 1, 0],
[3, 3, 4, 3, 2, 1, 0, 0, 0, 1, 0, 0],
[2, 2, 3, 3, 2, 1, 0, 0, 0, 0, 0, 0],
[1, 1, 2, 2, 2, 1, 0, 0, 0, 1, 1, 0],
[0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 2, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2]], dtype=int32)
Hier eine andere Methode, die scipy.ndimage.binary_erosion
verwendet. Ich schrieb dies, bevor ich die Entfernungsumwandlungsfunktion entdeckte. Ich bin sicher, dass es viel effizientere Methoden gibt, aber das sollte für nicht allzu große Bilder einigermaßen gut funktionieren.
import numpy as np
from scipy.ndimage import binary_erosion
def distance_from_edge(x):
dist = np.zeros_like(x, dtype=int)
while np.count_nonzero(x) > 0:
dist += x # Assumes x is an array of 0s and 1s, or bools.
x = binary_erosion(x)
return dist
Zum Beispiel
In [291]: a
Out[291]:
array([[0, 0, 0, 0],
[0, 1, 1, 1],
[0, 1, 1, 1],
[0, 1, 1, 1]])
In [292]: distance_from_edge(a)
Out[292]:
array([[0, 0, 0, 0],
[0, 1, 1, 1],
[0, 1, 2, 1],
[0, 1, 1, 1]])
In [293]: x
Out[293]:
array([[1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0],
[1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0],
[1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0],
[0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]])
In [294]: distance_from_edge(x)
Out[294]:
array([[1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
[1, 2, 2, 2, 2, 1, 0, 0, 0, 1, 0, 0],
[1, 2, 3, 3, 2, 1, 0, 0, 1, 2, 1, 0],
[1, 2, 3, 3, 2, 1, 0, 0, 0, 1, 0, 0],
[1, 2, 3, 3, 2, 1, 0, 0, 0, 0, 0, 0],
[1, 1, 2, 2, 2, 1, 0, 0, 0, 1, 1, 0],
[0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 2, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]])
Ordentlich - und Spaß den Code evolve zu beobachten. :) –
@WarrenWeckesser Yup, tolle Ansätze haben wir, alle mit scipy! – Divakar