我有一个嘈杂的二值化图像,其中大多数白点位于一个圆圈内。什么是拟合圆的好算法?该算法应该具有鲁棒性,能够处理圆圈中的各种缺陷。
这是一个示例图像:
以下是我迄今为止尝试过的一些方法:
编辑:在此处附加原始图像:
我对原始图像做了什么以获得上述二值图像:
这就是我的方法。最初的结果不是很好,还需要更多的工作。我将在最后详细说明后续步骤。
第1步:创建蒙版并准备图像
遮罩标识了粗黑圆圈内的区域,我猜这是容器。我们希望确保除此之外的任何事情都不会影响我们的结果。
import diplib as dip
# OP's original color image
img = dip.ImageRead("zDNIxW5n.jpg")
# We use the red channel, color information is not important and red has the best contrast
img = img(0)
# Find mask for analysis area by detecting the black circle (large dark areas)
approximate_radius = 400
circle = dip.AreaClosing(img, filterSize=10000)
gv = dip.Gradient(circle, 3)
circle = dip.OtsuThreshold(dip.Norm(gv))
x = dip.FindHoughCircles(circle, gv, range=[approximate_radius-100, approximate_radius+100], distance=100, fraction=0.9)
center = x[0][:2]
radius = x[0][2] - 20 # offset radius to ensure we clear out everything
mask = dip.DistanceToPoint(img.Sizes(), center, distance="square") < radius**2
# Prepare image
img = dip.Percentile(img, mask, percentile=95) - img
img.Mask(mask)
在帖子底部,您可以看到
img
现在的样子。
第 2 步:初步估计圆心
# Find centroid
center = dip.CenterOfMass(img)
这个中心并不精确,因为圆内的物质分布不均匀,而且圆外也有一些信号。
第 3 步:初步估计圆的半径
为此,我们根据到上面找到的质心的距离创建图像的总投影。
# Find radius, as the first point where the sum projection gets to half its maximum
proj = dip.RadialSum(img, center=center)
max_pos = dip.PositionMaximum(proj)[0][0]
max_val = proj[max_pos][0]
x = (proj < max_val / 2) & (dip.CreateXCoordinate(proj.Sizes(), mode={"corner"}) > max_pos)
radius = dip.PositionMaximum(x, mode="first")[0][0]
proj
函数如下所示:
随着到中心的距离增加,总和也增加。在圆圈之外,灰度值很少的地方,总和应该接近 0。
我们将最大值之后的点作为半径,该点的总和达到最大值的一半。
因为我们的初始中心不是实际的中心,所以此时的向下坡度并不是很陡。如果我们找到实际的中心,那么坡度会非常陡。
因此,此时我们可以进行迭代细化,即移动质心以使斜率尽可能陡。这涉及重复计算步骤 3,并评估
proj
处 max_pos
的陡度(例如,通过比较 max_pos - 2
和 max_pos + 2
处的值,或通过 max_pos
周围的几个点拟合一条直线)。
初始中心和半径如下所示:
import matplotlib as mpl
import matplotlib.pyplot as plt
img.Show()
c = mpl.patches.Circle(center, radius=radius, fill=False, edgecolor='r')
plt.gca().add_patch(c)
plt.show()
PS:我在 Python 中使用 DIPlib 来进行此演示。我是 DIPlib 的作者..