传统的确定线宽的方法是通过距离变换。这是一种计算前景像素到背景距离的算法。此距离在线的中心线上将达到最大,并且将是线宽的一半。
由于你的线近似垂直,我们只需沿每一行(或列)取距离变换的最大值。这些值的平均数将大约是平均宽度。
如果沿每一行(或列)取最大值不能很好地在线的中心线上采样,那么就需要更复杂的算法来提取这条中心线上的值。
此处我使用 DIPlib 进行演示,其他库也将拥有距离变换功能。[声明:我是 DIPlib 的作者之一。]
import diplib as dip
import numpy as np
img = dip.ImageRead('UVduo.png')
img = img > 128
dt = dip.EuclideanDistanceTransform(img)
width = dip.Maximum(dt, process=[True, False])
print(2 * np.mean(width))
这段代码输出 15.26
,表示线宽的平均像素值。
需要注意的是,width
是沿 y 轴线上每一位置的线宽。这是实际的线宽,而不是水平截距的长度。
由于线从图像顶部延伸到底部(即 width
中的每个值都有意义),我们可以简单地对 width
取平均值来得到平均宽度。同时,我们也可以直接读取 width
中的值,通过 np.asarray(width)
将其转换为 NumPy 数组。或者使用其他图像处理库,例如在 OpenCV 中可以使用 cv.distanceTransform
函数。
这里做了一个实验,与另一个答案中提到的 np.sum()
方法进行比较,该方法计算图像每一行的像素数量。目的是证明这种方法并不是计算图像每一行的像素数量,而是给出了沿整条线的实际宽度测量值。
代码将原始图像以 -0.3π 到 0.3π 之间的角度进行旋转。然后应用距离变换并取每一行的最大值。同时计算每一行的像素总和。这两个向量都被绘制出来。
import diplib as dip
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 2)
img = dip.ImageRead('UVduo.png')
for angle in [-0.3, -0.25, -0.2, -0.1, 0, 0.1, 0.2, 0.25, 0.3]:
angle *= np.pi
rot = dip.Rotation2D(img, angle, boundaryCondition="add zeros") > 128
dt = dip.EuclideanDistanceTransform(rot)
width = 2 * dip.Maximum(dt, process=[True, False])
ax[0].plot(width)
sum = np.sum(rot, axis=1)
ax[1].plot(sum)
plt.show()
生成的图表如下所示:

虽然这些图表看起来有些混乱,因为图像旋转会使其膨胀,而且在旋转后线并没有与很多行相交。但关键点是很明显的: