「这是我参与2022首次更文挑战的第26天,活动详情查看:2022首次更文挑战」
Hu 不变矩
Hu 不变矩可以保持平移、缩放和旋转不变,同时,所有的矩(第 7 个矩除外)对于反射都是不变的。第 7 个矩因反射而改变,从而使其能够区分镜像图片。 OpenCV 提供 cv2.HuMoments() 来计算 7 个Hu 不变矩,使用方法如下:
cv2.HuMoments(m[, hu]) → hu
这里,m 对应于用 cv2.moments() 计算的矩,输出 hu 对应于 7 个 Hu 不变矩。
7 个 Hu 不变矩定义如下:
接下来编写程序计算 7 个 Hu 不变矩,为了计算不变矩,必须首先使用 cv2.moments() 计算矩。计算图像矩时,可以使用矢量形状或图像,如果 binaryImage 参数为真(仅用于图像),则输入图像中的所有非零像素将被视为1。计算使用矢量形状和图像的图像矩后,根据计算的矩,计算 Hu 不变矩。
# 加载图像并将其转化为灰度图像
image = cv2.imread("example.png")
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 获取二值图像
ret, thresh = cv2.threshold(gray_image, 70, 255, cv2.THRESH_BINARY)
# 计算图像矩,传递参数为图像
M = cv2.moments(thresh, True)
print("moments: '{}'".format(M))
def centroid(moments):
"""根据图像矩计算质心"""
x_centroid = round(moments['m10'] / moments['m00'])
y_centroid = round(moments['m01'] / moments['m00'])
return x_centroid, y_centroid
# 计算质心
x, y = centroid(M)
# 计算 Hu 矩并打印
HuM = cv2.HuMoments(M)
print("Hu moments: '{}'".format(HuM))
# 计算图像矩时传递轮廓,重复以上计算过程
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
M2 = cv2.moments(contours[0])
print("moments: '{}'".format(M2))
x2, y2 = centroid(M2)
# 绘制轮廓
draw_contour_outline(image, contours, (255, 0, 0), 10)
# 绘制质心
cv2.circle(image, (x, y), 25, (255, 255, 0), -1)
cv2.circle(image, (x2, y2), 25, (0, 0, 255), -1)
# 打印质心
print("('x','y'): ('{}','{}')".format(x, y))
print("('x2','y2'): ('{}','{}')".format(x2, y2))
# 可视化,show_img_with_matplotlib()函数与前述示例类似,不再赘述
def show_thresh_with_matplotlib(thresh, title, pos):
ax = plt.subplot(1, 2, pos)
plt.imshow(thresh, cmap='gray')
plt.title(title, fontsize=8)
plt.axis('off')
show_img_with_matplotlib(image, "detected contour and centroid", 1)
show_thresh_with_matplotlib(thresh, 'thresh', 2)
plt.show()
观察所计算的矩,Hu 不变矩,以及质心,可以发现使用矢量形状和图像的结果是相似的,但稍有不同,例如,获得的质心:
('x','y'): ('1124','1713')
('x2','y2'): ('1157','1636')
其坐标相差一些像素,原因是栅格化的图像分辨率有限。对于轮廓估计的矩与针对栅格化后的轮廓计算的矩稍有不同。上图中可以看到程序的输出,其中使用不同颜色显示了两个质心,以便观察它们之前的差异。
接下来,为了对比 Hu 不变矩,我们使用三个图像。第一个是原始图像,第二个将原始图像旋转180度,第三个将原始图像水平翻转,计算上述图像的 Hu 不变矩。
程序的第一步是使用 cv2.imread() 加载图像,并通过使用 cv2.cvtColor() 将它们转换为灰度图像。第二步是应用 cv2.threshold() 获取二进制图像。最后,使用 cv2.humoments() 计算Hu不变矩:
# 加载图像并进行转换
images = [image_1, image_2, image_3]
des = ['original', 'rotation', 'reflection']
for i in range(len(images)):
image = images[i]
# 转换为灰度图像
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 图像二值化
ret, thresh = cv2.threshold(gray_image, 100, 255, cv2.THRESH_BINARY)
# 计算 Hu 不变矩
HuM_1 = cv2.HuMoments(cv2.moments(thresh, True)).flatten()
# 打印 Hu 不变矩
print("Hu moments ({}): '{}'".format(des[i], HuM_1))
# 可视化
show_img_with_matplotlib(image, "original", 1+i )
plt.show()
查看计算的 Hu 不变矩结果:
Hu moments (original): '[ 3.01270761e-01 2.85277848e-02 6.91011783e-03 3.83970453e-04 -3.46840290e-07 -3.85059443e-05 5.20465006e-07]'
Hu moments (rotation): '[ 3.01270761e-01 2.85277848e-02 6.91011783e-03 3.83970453e-04 -3.46840290e-07 -3.85059443e-05 5.20465006e-07]'
Hu moments (reflection): '[ 3.01270761e-01 2.85277848e-02 6.91011783e-03 3.83970453e-04 -3.46840290e-07 -3.85059443e-05 -5.20465006e-07]'
可以看到,除了第七个矩外,计算的 Hu 不变矩在这三种情况下是相同的。