Python 如何使用图像处理求物体的直径?
How to find the diameter of objects using image processing in Python?
给定一张包含一些不规则物体的图像,我想求出它们各自的直径。
,我知道如何识别对象了。 但是,是否可以测量图像中显示的物体的最大直径?
我查看了 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()
图片:
您可以使用 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
我建议使用距离变换。因此,一旦您获得了带标签的图像,您就可以:
dt = ndimage.distance_transform_edt(blobs)
slices = ndimage.find_objects(input=labels)
radii = [np.amax(dt[s]) for s in slices]
这给出了最大的内切圆(或 3D 球体)。 find_objects
函数非常方便。它 returns 一个 Python slice 对象的列表,您可以使用它在包含斑点的特定位置对图像进行索引。这些切片当然可以用于对距离变换图像进行索引。因此,切片内距离变换的最大值就是您要查找的半径。
上面的代码有一个潜在的问题:切片是一个正方形(或立方体)部分,因此如果其他 blob 靠近在一起,可能会包含小块。您可以使用更复杂的逻辑来解决这个问题,如下所示:
radii = [np.amax(dt[slices[i]]*(labels[slices[i]] == (i+1))) for i in range(nlabels)]
上述版本的列表理解用应该由切片索引的 blob 掩盖了距离变换,从而消除了来自相邻 blob 的任何不需要的干扰。
给定一张包含一些不规则物体的图像,我想求出它们各自的直径。
我查看了 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()
图片:
您可以使用 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
我建议使用距离变换。因此,一旦您获得了带标签的图像,您就可以:
dt = ndimage.distance_transform_edt(blobs)
slices = ndimage.find_objects(input=labels)
radii = [np.amax(dt[s]) for s in slices]
这给出了最大的内切圆(或 3D 球体)。 find_objects
函数非常方便。它 returns 一个 Python slice 对象的列表,您可以使用它在包含斑点的特定位置对图像进行索引。这些切片当然可以用于对距离变换图像进行索引。因此,切片内距离变换的最大值就是您要查找的半径。
上面的代码有一个潜在的问题:切片是一个正方形(或立方体)部分,因此如果其他 blob 靠近在一起,可能会包含小块。您可以使用更复杂的逻辑来解决这个问题,如下所示:
radii = [np.amax(dt[slices[i]]*(labels[slices[i]] == (i+1))) for i in range(nlabels)]
上述版本的列表理解用应该由切片索引的 blob 掩盖了距离变换,从而消除了来自相邻 blob 的任何不需要的干扰。