在数字图像处理、计算机视觉与相关领域中,图像矩(Image moments)是指图像的某些特定像素灰度的加权平均值(矩),或者是图像具有类似功能或意义的属性。
图像矩通常用来描述 分割 后的图像对象。可以通过图像的矩来获得图像的部分性质,包括面积(或总体亮度),以及有关几何中心和方向的信息 。
对于灰度图像f,其(p+q)阶矩(有时称为"原始矩"),定义如下:
m p q = ∑ x ∑ y x p y q f ( x , y ) m_{pq}= \sum\limits_{x}\sum\limits_{y} x^py^qf(x,y) mpq=x∑y∑xpyqf(x,y)
原始矩中包含以下有关原始图像属性的信息:
中心矩是以质心为中心的矩,相比原始矩,只要添加一个平移即可,中心矩的定义如下:
u p q = ∑ x ∑ y ( x − c x ) p ( y − c y ) q f ( x , y ) u_{pq}=\sum\limits_{x}\sum\limits_{y}(x-c_x)^p(y-c_y)^qf(x,y) upq=x∑y∑(x−cx)p(y−cy)qf(x,y)
上述公式中的 ( c x , c y ) (c_x,c_y) (cx,cy)为图像的质心.
有了中心矩的定义,我们可以得到图像f的协方差矩阵,其定义为:
C o n v ( f ) = 1 u 00 ( u 20 u 11 u 11 u 02 ) Conv(f)=\frac{1}{u_{00}} \left( \begin{array}{l} u_{20} &u_{11} \\ u_{11} & u_{02} \end{array} \right) Conv(f)=u001(u20u11u11u02)
接着我们可以计算该协方差矩阵的特征向量,最大特征值对应的特征向量就是物体长轴的角度,我们可以将其定义为物体的方向.该角度计算公式如下:
θ = 1 2 a r c t a n ( 2 u 11 u 20 − u 02 ) \theta=\frac{1}{2}arctan( \frac{2u_{11}} {u_{20}-u_{02}}) θ=21arctan(u20−u022u11)
接下来我们推导一下阶数较低的中心矩的性质:
u 00 = m 00 u_{00}=m_{00} u00=m00
u 10 = ∑ x ∑ y ( x − c x ) f ( x , y ) = m 10 − c x m 00 = 0 \begin{aligned} u_{10}&=\sum\limits_{x}\sum\limits_{y}(x-c_{x})f(x,y) \\ &=m_{10}-c_{x}m_{00} \\ &=0 \end{aligned} u10=x∑y∑(x−cx)f(x,y)=m10−cxm00=0
u 01 = ∑ x ∑ y ( y − c y ) f ( x , y ) = m 01 − c y m 00 = 0 \begin{aligned} u_{01}&=\sum\limits_{x}\sum\limits_{y}(y-c_{y})f(x,y) \\ &=m_{01}-c_{y}m_{00} \\ &=0 \end{aligned} u01=x∑y∑(y−cy)f(x,y)=m01−cym00=0
u 11 = ∑ x ∑ y ( x − c x ) ( y − c y ) f ( x , y ) = m 11 − c y m 10 − c x m 01 + c x c y m 00 = m 11 − m 10 m 01 m 00 = m 11 − c x m 01 = m 11 − c y m 10 \begin{aligned} u_{11} &=\sum\limits_{x}\sum\limits_{y}(x-c_{x})(y-c_{y})f(x,y) \\ &=m_{11}-c_{y}m_{10}-c_{x}m_{01}+c_{x}c_{y}m_{00} \\ &=m_{11}- \frac{m_{10}m_{01}}{m_{00}} \\ &=m_{11}- {c_{x}m_{01}} \\ &=m_{11}- {c_{y}m_{10}} \end{aligned} u11=x∑y∑(x−cx)(y−cy)f(x,y)=m11−cym10−cxm01+cxcym00=m11−m00m10m01=m11−cxm01=m11−cym10
u 20 = ∑ x ∑ y ( x − c x ) 2 f ( x , y ) = m 20 − 2 c x m 10 + c x 2 m 00 = m 20 − c x m 10 \begin{aligned} u_{20} &=\sum\limits_{x}\sum\limits_{y}(x-c_{x})^2f(x,y) \\ &=m_{20}-2c_{x}m_{10}+c_{x}^2m_{00} \\ &=m_{20}- {c_{x}m_{10}} \end{aligned} u20=x∑y∑(x−cx)2f(x,y)=m20−2cxm10+cx2m00=m20−cxm10
u 02 = ∑ x ∑ y ( y − c y ) 2 f ( x , y ) = m 02 − 2 c y m 01 + c y 2 m 00 = m 02 − c y m 01 \begin{aligned} u_{02} &=\sum\limits_{x}\sum\limits_{y}(y-c_{y})^2f(x,y) \\ &=m_{02}-2c_{y}m_{01}+c_{y}^2m_{00} \\ &=m_{02}- {c_{y}m_{01}} \end{aligned} u02=x∑y∑(y−cy)2f(x,y)=m02−2cym01+cy2m00=m02−cym01
推到了一堆公式,不知道小伙伴有没有觉得很枯燥,好的吧,接下来我们来举个栗子,进入轻松的写代码实战环节.本节的目的在于利用矩求出前景物体的中心点以及长轴的角度.
我们这里以一群飞翔的大雁组成的图像为例来进行讲解,首先我们读入图像并执行灰度化,代码如下:
img_file = "./images/bird_swarm.jpg"
img = cv2.imread(img_file,cv2.IMREAD_COLOR)
gray_img = cv2.cvtColor(img,cv2.COLOR_RGB2GRAY)
效果如下:
上图中,左侧为原始输入图,右侧为灰度化后的图像.
通过第二章节我们知道,二值图像的原始矩就是所有像素值之和,所以我们可以先进行二值化来提取我们的前景物体,二值化代码如下:
retval, bin_img = cv2.threshold(gray_img, 50, 1, cv2.THRESH_BINARY)
show_img = bin_img * 255
可以看出,我们使用较低的阈值,将前景大雁(白色部分)通过二值化提取出来.
接下来我们来计算图像矩,我们得到的二值化图中,对于前景大雁来说 f ( x , y ) = 1 f(x,y)=1 f(x,y)=1,对于背景黑色像素值来说 f ( x , y ) = 0 f(x,y)=0 f(x,y)=0.根据上文矩的定义,编写代码如下:
m00 = m01 = m10 = m11 = m20 = m02 = m21 = m12 = 0
height, width = bin_img.shape
for y in range(height):
for x in range(width):
m00 += bin_img[y, x]
m10 += x * bin_img[y, x]
m01 += y * bin_img[y, x]
m11 += x * y * bin_img[y, x]
m20 += x * x * bin_img[y, x]
m02 += y * y * bin_img[y, x]
m21 += x * x * y * bin_img[y, x]
m12 += x * y * y * bin_img[y, x]
进而利用以下代码可以得到中心点的位置坐标:
cx = m10/m00
cy = m01/m00
print('Centriod: ({0:.2f}, {1:.2f})'.format(cx, cy))
输出如下:
Centriod: (1039.64, 660.24)
上图中的红色小圆圈即为计算出来的大雁群的中心点.
根据上述中心矩的定义和物体长轴角度的定义,我们可以计算出大雁群长轴和水平线的夹角,代码如下:
mu00 = m00
mu11 = m11 - cx*m01
mu20 = m20 - cx*m10
mu02 = m02 - cy*m01
theta = 1/2*np.arctan2(2*mu11/mu00, (mu20 - mu02)/mu00)
print('Angle {0:.2f}'.format(theta*180/np.pi))
输出如下:
Angle 37.96
虽然通过上述代码,我们可以计算出大雁群的质心和长轴角度,但是只有数值不太直观,那么我们接下来来可视化上述输出.
我们现在在原始图像上绘制长轴和短轴(这里设置默认长度)以可视化说明质心和角度的位置。
代码如下:
# visual
rho = 800
dx_major = rho * np.cos(theta)
dy_major = rho * np.sin(theta)
dx_minor = 0.3 * rho * np.cos(theta - np.pi / 2)
dy_minor = 0.3 * rho * np.sin(theta - np.pi / 2)
# short
short_axis=[(int(cx-dx_minor),int(cy-dy_minor)),(int(cx),int(cy)),(int(cx+dx_minor),int(cy+dy_minor))]
for i in range(len(short_axis)-1):
cv2.line(img,short_axis[i],short_axis[i+1],color=(255,0,0),thickness=2)
for pt in short_axis:
cv2.circle(img,pt,radius=5,color=(255,0,0),thickness=3,lineType=-1)
# long
long_axis = [(int(cx - dx_major), int(cy - dy_major)), (int(cx), int(cy)), (int(cx + dx_major), int(cy + dy_major))]
for i in range(len(long_axis) - 1):
cv2.line(img, long_axis[i], long_axis[i + 1], color=(0, 0, 255), thickness=2)
for pt in long_axis:
cv2.circle(img,pt,radius=5,color=(0,0,255),thickness=3,lineType=-1)
# center
cv2.circle(img, (int(cx),int(cy)), radius=5, color=(0, 255, 0), thickness=3, lineType=-1)
# show
cv2.imshow("img",img)
cv2.waitKey(0)
上图中,蓝色边为大雁群的短轴,红边为大雁群的长轴,绿色圆圈为大雁群的几何中心.
参考链接
本文介绍了图像矩和中心矩的相关定义和一些基本性质,并举例来实现对图像矩的求解和可视化得到的结果.
您学废了吗?
关注公众号《AI算法之道》,获取更多AI算法资讯。
注: 关注公众号,后台回复 矩 , 可获取完整代码