2016-07-26 37 views
3

Angesichts eines Bildes mit einigen unregelmäßigen Objekten möchte ich ihren individuellen Durchmesser finden.Wie findet man den Durchmesser von Objekten mit der Bildverarbeitung in Python?

Thanks to this answer kann ich die Objekte identifizieren. Ist es jedoch möglich, den maximalen Durchmesser der im Bild gezeigten Objekte zu messen?

Ich habe in die scipy-ndimage Dokumentation geschaut und habe keine dedizierte Funktion gefunden.

-Code für Objektidentifikation:

import numpy as np 
from scipy import ndimage 
from matplotlib import pyplot as plt 

# generate some lowpass-filtered noise as a test image 
gen = np.random.RandomState(0) 
img = gen.poisson(2, size=(512, 512)) 
img = ndimage.gaussian_filter(img.astype(np.double), (30, 30)) 
img -= img.min() 
img /= img.max() 

# use a boolean condition to find where pixel values are > 0.75 
blobs = img > 0.75 

# label connected regions that satisfy this condition 
labels, nlabels = ndimage.label(blobs) 

# find their centres of mass. in this case I'm weighting by the pixel values in 
# `img`, but you could also pass the boolean values in `blobs` to compute the 
# unweighted centroids. 
r, c = np.vstack(ndimage.center_of_mass(img, labels, np.arange(nlabels) + 1)).T 

# find their distances from the top-left corner 
d = np.sqrt(r*r + c*c) 

# plot 
fig, ax = plt.subplots(1, 2, sharex=True, sharey=True, figsize=(10, 5)) 
ax[0].imshow(img) 
ax[1].hold(True) 
ax[1].imshow(np.ma.masked_array(labels, ~blobs), cmap=plt.cm.rainbow) 
for ri, ci, di in zip(r, c, d): 
    ax[1].annotate('', xy=(0, 0), xytext=(ci, ri), 
        arrowprops={'arrowstyle':'<-', 'shrinkA':0}) 
    ax[1].annotate('d=%.1f' % di, xy=(ci, ri), xytext=(0, -5), 
        textcoords='offset points', ha='center', va='top', 
        fontsize='x-large') 
for aa in ax.flat: 
    aa.set_axis_off() 
fig.tight_layout() 
plt.show() 

Image: enter image description here

+1

Im Allgemeinen ist es kein triviales Problem. OpenCV (mit Python-Bindings) hat 'minEnclosingCircle', aber ich weiß nichts Ähnliches in ndimage. Siehe auch beispielsweise https://en.wikipedia.org/wiki/Smallest-circle_problem – tom10

Antwort

5

Sie skimage.measure.regionprops verwenden könnte den Begrenzungsrahmen für alle Regionen in Ihrem Bild zu bestimmen. Für grob kreisförmige Blobs kann der Durchmesser des kleinsten umschließenden Kreises durch die größte Seite der Bounding Box approximiert werden. Um dies zu tun müssen Sie nur das folgende Snippet am Ende des Skripts hinzuzufügen:

from skimage.measure import regionprops 

properties = regionprops(labels) 
print 'Label \tLargest side' 
for p in properties: 
    min_row, min_col, max_row, max_col = p.bbox 
    print '%5d %14.3f' % (p.label, max(max_row - min_row, max_col - min_col)) 

fig = plt.figure() 
ax = fig.add_subplot(111)  
ax.imshow(np.ma.masked_array(labels, ~blobs), cmap=plt.cm.gist_rainbow) 
ax.set_title('Labeled objects') 
plt.xticks([]) 
plt.yticks([]) 
for ri, ci, li in zip(r, c, range(1, nlabels+1)): 
    ax.annotate(li, xy=(ci, ri), fontsize=24) 
plt.show() 

Und dies ist die Ausgabe, die Sie erhalten:

Label Largest side 
    1  106.000 
    2   75.000 
    3   79.000 
    4   56.000 
    5  161.000 
    6   35.000 
    7   47.000 

Labeled objects

+0

Paar Anmerkungen, um besser zu verstehen. 1) Können 'N' oder' Bins' als die Anzahl der Objekte im Bild interpretiert werden? Wenn ja, "N = nLabels"? 2) Ist es richtig, die "größte Seite" in Bezug auf die Anzahl der Zellen zu interpretieren? – FaCoffee

+0

1) Ich habe meine Antwort vereinfacht und 'N' wird nicht mehr verwendet. 2) Die größte Seite wird in Pixeln gemessen – Tonechas

1

Ich würde vorschlagen, eine mit Entfernung transformieren. Also, sobald Sie Ihr etikettiertes Bild haben, tun Sie:

dt = ndimage.distance_transform_edt(blobs) 
slices = ndimage.find_objects(input=labels) 
radii = [np.amax(dt[s]) for s in slices] 

Dies ergibt den größten Inkreis (oder Kugel in 3D). Die find_objects Funktion ist ziemlich praktisch. Es gibt eine Liste von Python Slice Objekten zurück, die Sie verwenden können, um in das Bild an den spezifischen Positionen zu indizieren, die die Blobs enthalten. Diese Schnitte können natürlich zum Indexieren in das Distanztransformationsbild verwendet werden. Daher ist der größte Wert der Entfernung innerhalb der Scheibe der Radius, nach dem Sie suchen.

Es gibt eine mögliche gotcha des obigen Codes: die Scheibe ist ein quadratischer (oder kubischer) Schnitt, der kleine Stücke anderer Blobs enthalten könnte, wenn sie dicht beieinander liegen. Sie können mit ein bisschen komplizierter Logik dieses Problem umgehen, wie folgt:

radii = [np.amax(dt[slices[i]]*(labels[slices[i]] == (i+1))) for i in range(nlabels)] 

Die obige Version der Liste Verständnis Masken der Abstand mit dem Blob-Transformation, die durch die Scheibe indiziert werden soll, wodurch jegliche unerwünschte Störungen entfernt aus benachbarten Blobs.