2016-11-08 8 views
1

Ich habe eine numpy Array mit 1 s & 0 s (oder bools wenn das einfacher ist)Suche ‚Abstand vom Rand‘ eines numpy Array

Ich möchte den Abstand von jedem 1 seinen engsten ‚Rand finden '(Eine Kante ist, wo eine 1 eine 0 erfüllt).

Toy Beispiel:

Original-Array:

array([[0, 0, 0, 0], 
     [0, 1, 1, 1], 
     [0, 1, 1, 1], 
     [0, 1, 1, 1]]) 

Ergebnis:

array([[0, 0, 0, 0], 
     [0, 1, 1, 1], 
     [0, 1, 2, 1], 
     [0, 1, 1, 1]]) 

Wenn möglich, würde Ich mag die 'cityblock' Abstand verwenden, aber das ist eine niedrigere Priorität

Danke!

Antwort

2

hier ein vektorisiert Ansatz binary_erosion & cdist(..'cityblock') -

from scipy.ndimage.morphology import binary_erosion 
from scipy.spatial.distance import cdist 

def dist_from_edge(img): 
    I = binary_erosion(img) # Interior mask 
    C = img - I    # Contour mask 
    out = C.astype(int)  # Setup o/p and assign cityblock distances 
    out[I] = cdist(np.argwhere(C), np.argwhere(I), 'cityblock').min(0) + 1 
    return out 

Probelauf -

In [188]: img.astype(int) 
Out[188]: 
array([[0, 0, 0, 0, 1, 0, 0], 
     [0, 1, 1, 1, 1, 1, 0], 
     [0, 1, 1, 1, 1, 1, 1], 
     [0, 1, 1, 1, 1, 1, 1], 
     [0, 0, 1, 1, 1, 1, 1], 
     [0, 0, 0, 1, 0, 0, 0]]) 

In [189]: dist_from_edge(img) 
Out[189]: 
array([[0, 0, 0, 0, 1, 0, 0], 
     [0, 1, 1, 1, 2, 1, 0], 
     [0, 1, 2, 2, 3, 2, 1], 
     [0, 1, 2, 3, 2, 2, 1], 
     [0, 0, 1, 2, 1, 1, 1], 
     [0, 0, 0, 1, 0, 0, 0]]) 

Hier ist ein Eingang, Ausgang auf einem menschlichen blob -

enter image description hereenter image description here

+0

Ordentlich - und Spaß den Code evolve zu beobachten. :) –

+0

@WarrenWeckesser Yup, tolle Ansätze haben wir, alle mit scipy! – Divakar

2

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