OpenCV Hu不变矩详解

1,538 阅读3分钟

「这是我参与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 不变矩定义如下:

hu[0]=nu20+nu02hu[1]=(nu20nu02)2+4nu112hu[2]=(nu303nu12)2+(3nu21nu03)2hu[3]=(nu30+nu12)2+(nu21+nu03)2hu[4]=(nu303nu12)(nu30+nu12)[(nu30+nu12)23(nu21+nu03)2]+(3nu21nu03)(nu21+nu03)[3(nu30+nu12)2(nu21+nu03)2]hu[5]=(nu20nu02)[(nu30+nu12)2(nu21+nu03)2]+4(nu30+nu12)(nu21+nu03)hu[6]=(3nu21nu03)(nu21+nu03)[3(nu30+nu12)2(nu21+nu03)2](nu303nu12)(nu21+nu03)[3(nu30+nu12)2(nu21+nu03)2]\begin{aligned} hu[0]&=nu_{20}+nu_{02} \\ hu[1]&=(nu_{20}-nu_{02})^2+4\cdot nu_{11}^2 \\ hu[2]&=(nu_{30}-3\cdot nu_{12})^2+(3\cdot nu_{21}- nu_{03})^2 \\ hu[3]&=(nu_{30}+nu_{12})^2+(nu_{21}+ nu_{03})^2 \\ hu[4]&=(nu_{30}-3\cdot nu_{12})(nu_{30}+nu_{12})[(nu_{30}+nu_{12})^2-3\cdot(nu_{21}+nu_{03})^2]+(3\cdot nu_{21}- nu_{03})(nu_{21}+ nu_{03})[3\cdot (nu_{30}+nu_{12})^2-(nu_{21}+ nu_{03})^2]\\ hu[5]&=(nu_{20}-nu_{02})[(nu_{30}+nu_{12})^2-(nu_{21}+nu_{03})^2]+4\cdot(nu_{30}+nu_{12})(nu_{21}+ nu_{03})\\ hu[6]&=(3\cdot nu_{21}- nu_{03})(nu_{21}+ nu_{03})[3\cdot (nu_{30}+nu_{12})^2-(nu_{21}+nu_{03})^2]-(nu_{30}-3\cdot nu_{12})(nu_{21}+ nu_{03})[3\cdot (nu_{30}+nu_{12})^2-(nu_{21}+ nu_{03})^2] \end{aligned}

接下来编写程序计算 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 不变矩

观察所计算的矩,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 不变矩结果:

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 不变矩在这三种情况下是相同的。

相关链接

OpenCV轮廓检测详解

OpenCV图像矩详解