2016-07-26 139 views
3

给定一张带有一些不规则物体的图像,我想找到它们各自的直径。如何在Python中使用图像处理来查找对象的直径?

Thanks to this answer,我知道如何识别物体。 但是,是否可以测量图像中显示的物体的最大直径?

我查看了scipy-ndimage文档,但没有找到专门的功能。

代码为对象标识:

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

图片: enter image description here

+1

通常这不是一个小问题。 OpenCV(具有python绑定)具有'minEnclosingCircle',但我不知道ndimage中类似的东西。另请参阅https://en.wikipedia.org/wiki/Smallest-circle_problem – tom10

回答

5

你可以使用skimage.measure.regionprops确定图像的所有区域的边框。对于大致圆形的斑点,最小包围圆的直径可以用边界框的最大边来近似。要做到这一点,你只需要在你的脚本的末尾添加以下代码片段:

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

这是输出你:

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

夫妇的言论以更好地理解。 1)'N'或'bins'是否可以解释为图像中的对象数量?如果是这样,'N = nlabels'? 2)根据细胞的数量来解释“最大的一面”是否正确? – FaCoffee

+0

1)我简化了答案,'N'不再使用。 2)最大的一面以像素为单位进行测量 – Tonechas

1

我建议使用距离变换。所以一旦你有你的标签图像你做:

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

这给出了最大的内接圆(或三维球体)。 find_objects功能非常方便。它返回一个Python 片段对象的列表,您可以使用它来在包含blob的特定位置处将图像编入索引。这些切片当然可以用于索引到距离变换图像中。因此,切片内距离变换的最大值就是您要查找的半径。

上面的代码有一个潜在的缺陷:切片是正方形(或立方体)切片,因此如果它们靠得很近,可能会包含其他小块的小块。你可以解决这个问题有一个比较复杂一点的逻辑如下:

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

列表理解口罩的上述版本是应该由切片索引斑的距离变换,从而消除任何不必要的干扰来自相邻的斑点。

相关问题