【python】OpenCV—Image Moments
时间:2025-06-24 12:31:15 来源:新华社
【字体:  

在这里插入图片描述

文章目录

  • 1、功能描述
  • 2、图像矩
  • 3、代码实现
  • 4、效果展示
  • 5、完整代码
  • 6、涉及到的库函数
    • cv2.moments
  • 7、参考

1、功能描述

计算图像的矩,以质心为例

2、图像矩

什么叫图像的矩,在数字图像处理中有什么作用? - 谢博琛的回答 - 知乎
https://www.zhihu.com/question/26803016/answer/888699124

在这里插入图片描述
0 阶矩 m 00 m_{ 00} m00:目标区域的面积(Area)

1 阶矩 m 01 , m 10 m_{ 01}, m_{ 10} m01,m10:目标区域的质心(Centroid)

2 阶矩 m 02 , m 20 , m 11 m_{ 02}, m_{ 20}, m_{ 11} m02,m20,m11:即惯性矩,可计算目标图像的方向

3 阶矩 m 03 , m 30 , m 12 , m 21 m_{ 03}, m_{ 30}, m_{ 12}, m_{ 21} m03,m30,m12,m21:目标区域的方位和斜度,反应目标的扭曲

Hu 矩:目标区域往往伴随着空间变换(平移,尺度,旋转),所以需要在普通矩的基础上构造出具备不变性的矩组

中心矩:构造平移不变性


一文弄懂图像的矩和相关应用

矩是统计学的一个概念(pencv中的图像矩(空间矩,中心矩,归一化中心矩,Hu矩))

图像矩(Image moments)是指图像的某些特定像素灰度的加权平均值(矩),或者是图像具有类似功能或意义的属性。

图像矩通常用来描述分割后的图像对象。可以通过图像的矩来获得图像的部分性质,包括面积(或总体亮度),以及有关几何中心方向的信息 。

在这里插入图片描述
例如工业缺陷检测中(实操教程|使用计算机视觉算法检测钢板中的焊接缺陷),使用图像矩测量缺陷严重性

3、代码实现

导入必要的库函数,固定随机种子,以保证绘制出来的图片色彩固定随机

from__future__ importprint_functionfrom__future__ importdivisionimportcv2 ascvimportnumpy asnpimportargparseimportrandom asrngrng.seed(12345)

读取图片,为空打印 could not open or find xxx

parser =argparse.ArgumentParser(description='Code for Image Moments tutorial.')parser.add_argument('--input',help='Path to input image.',default='1.jpg')args =parser.parse_args()src =cv.imread(cv.samples.findFile(args.input))ifsrc isNone:print('Could not open or find the image:',args.input)exit(0)

图片转化为灰度图,做模糊处理,并在窗口中显示

# Convert image to gray and blur itsrc_gray =cv.cvtColor(src,cv.COLOR_BGR2GRAY)src_gray =cv.blur(src_gray,(3,3))source_window ='Source'cv.namedWindow(source_window)cv.imshow(source_window,src)

调用 canny 算子边缘检测找出图片中需要计算矩的轮廓

把 canny 算子的 threshold 设置为滑动条的形式进行回调,默认为 100

max_thresh =255thresh =100# initial thresholdcv.createTrackbar('Canny Thresh:',source_window,thresh,max_thresh,thresh_callback)thresh_callback(thresh)cv.waitKey()

看看核心函数 thresh_callback

计算 canny 边缘检测,调用找轮廓函数,获取所有边缘轮廓

defthresh_callback(val):threshold =val    canny_output =cv.Canny(src_gray,threshold,threshold *2)contours,_ =cv.findContours(canny_output,cv.RETR_TREE,cv.CHAIN_APPROX_SIMPLE)

计算每个轮廓的矩

# Get the momentsmu =[None]*len(contours)fori inrange(len(contours)):mu[i]=cv.moments(contours[i])

计算每个轮廓的质心,图片中所有像素点的横坐标除以图片的像素点个数(面积),结果为质心的横坐标

同理可以计算出质心的纵坐标

# Get the mass centersmc =[None]*len(contours)fori inrange(len(contours)):# add 1e-5 to avoid division by zero# print(mu[i]['m00'], mu[i]['m10'], mu[i]['m01'])mc[i]=(mu[i]['m10']/(mu[i]['m00']+1e-5),mu[i]['m01']/(mu[i]['m00']+1e-5))

看看计算出来的矩的所有信息

mu[0]

output

{ 'm00':1.0,'m10':668.0,'m01':876.3333333333333,'m20':446224.1666666666,'m11':585390.6666666666,'m02':767960.1666666666,'m30':298077966.0,'m21':391041111.3666667,'m12':512997391.3333333,'m03':672989190.1,'mu20':0.16666666662786156,'mu11':0.0,'mu02':0.055555555620230734,'mu30':5.960464477539063e-08,'mu21':-0.022222160400502844,'mu12':9.778887033462524e-09,'mu03':0.007407426834106445,'nu20':0.16666666662786156,'nu11':0.0,'nu02':0.055555555620230734,'nu30':5.960464477539063e-08,'nu21':-0.022222160400502844,'nu12':9.778887033462524e-09,'nu03':0.007407426834106445}

绘制轮廓,绘制每个轮廓的质心,保存结果

# Draw contoursdrawing =np.zeros((canny_output.shape[0],canny_output.shape[1],3),dtype=np.uint8)fori inrange(len(contours)):color =(rng.randint(0,256),rng.randint(0,256),rng.randint(0,256))cv.drawContours(drawing,contours,i,color,2)cv.circle(drawing,(int(mc[i][0]),int(mc[i][1])),4,color,-1)cv.imshow('Contours',drawing)cv.imwrite("result.jpg",drawing)

对比下我们通过图片矩计算出来的面积和直接调用 opencv 接口的轮廓面积

# Calculate the area with the moments 00 and compare with the result of the OpenCV functionfori inrange(len(contours)):print(' * Contour[%d] - Area (M_00) = %.2f - Area OpenCV: %.2f - Length: %.2f'%(i,mu[i]['m00'],cv.contourArea(contours[i]),cv.arcLength(contours[i],True)))

output

*Contour[0]-Area (M_00)=1.00-Area OpenCV:1.00-Length:17.66*Contour[1]-Area (M_00)=0.00-Area OpenCV:0.00-Length:426.14*Contour[2]-Area (M_00)=1.50-Area OpenCV:1.50-Length:67.90......*Contour[539]-Area (M_00)=92.00-Area OpenCV:92.00-Length:541.41*Contour[540]-Area (M_00)=21.50-Area OpenCV:21.50-Length:84.47*Contour[541]-Area (M_00)=81.50-Area OpenCV:81.50-Length:289.49*Contour[542]-Area (M_00)=61.00-Area OpenCV:61.00-Length:452.42

可以看到结果是一致的

4、效果展示

输入图片

在这里插入图片描述

threshold = 10

在这里插入图片描述

threshold = 60
在这里插入图片描述
threshold = 112
在这里插入图片描述

threshold = 163

在这里插入图片描述
threshold = 214

在这里插入图片描述

只绘制矩,不绘制轮廓

eg,threshold = 125

在这里插入图片描述

输入图片

在这里插入图片描述
threshold = 64
在这里插入图片描述

输入图片

在这里插入图片描述

threshold = 64

在这里插入图片描述

输入图片

在这里插入图片描述
threshold = 64
在这里插入图片描述

输入图片

在这里插入图片描述

threshold = 64

在这里插入图片描述

输入图片

在这里插入图片描述

threshold = 64

在这里插入图片描述

输入图片

在这里插入图片描述

threshold = 64

在这里插入图片描述

5、完整代码

from__future__ importprint_functionfrom__future__ importdivisionimportcv2 ascvimportnumpy asnpimportargparseimportrandom asrngrng.seed(12345)defthresh_callback(val):threshold =val    canny_output =cv.Canny(src_gray,threshold,threshold *2)contours,_ =cv.findContours(canny_output,cv.RETR_TREE,cv.CHAIN_APPROX_SIMPLE)# Get the momentsmu =[None]*len(contours)fori inrange(len(contours)):mu[i]=cv.moments(contours[i])# Get the mass centersmc =[None]*len(contours)fori inrange(len(contours)):# add 1e-5 to avoid division by zero# print(mu[i]['m00'], mu[i]['m10'], mu[i]['m01'])mc[i]=(mu[i]['m10']/(mu[i]['m00']+1e-5),mu[i]['m01']/(mu[i]['m00']+1e-5))# Draw contoursdrawing =np.zeros((canny_output.shape[0],canny_output.shape[1],3),dtype=np.uint8)fori inrange(len(contours)):color =(rng.randint(0,256),rng.randint(0,256),rng.randint(0,256))cv.drawContours(drawing,contours,i,color,2)cv.circle(drawing,(int(mc[i][0]),int(mc[i][1])),4,color,-1)cv.imshow('Contours',drawing)cv.imwrite("result.jpg",drawing)# Calculate the area with the moments 00 and compare with the result of the OpenCV functionfori inrange(len(contours)):print(' * Contour[%d] - Area (M_00) = %.2f - Area OpenCV: %.2f - Length: %.2f'%(i,mu[i]['m00'],cv.contourArea(contours[i]),cv.arcLength(contours[i],True)))if__name__ =="__main__":parser =argparse.ArgumentParser(description='Code for Image Moments tutorial.')parser.add_argument('--input',help='Path to input image.',default='1.jpg')args =parser.parse_args()src =cv.imread(cv.samples.findFile(args.input))ifsrc isNone:print('Could not open or find the image:',args.input)exit(0)# Convert image to gray and blur itsrc_gray =cv.cvtColor(src,cv.COLOR_BGR2GRAY)src_gray =cv.blur(src_gray,(3,3))source_window ='Source'cv.namedWindow(source_window)cv.imshow(source_window,src)max_thresh =255thresh =100# initial thresholdcv.createTrackbar('Canny Thresh:',source_window,thresh,max_thresh,thresh_callback)thresh_callback(thresh)cv.waitKey()

6、涉及到的库函数

cv2.moments

cv2.moments是 OpenCV 库中的一个函数,用于计算图像轮廓的矩。

一、函数原型

retval =cv2.moments(array[,binaryImage])

二、参数说明

  • array:表示轮廓的数组,可以是点集、灰度图像或二值图像。当 array 是点集时,函数会将这些点集当成轮廓中的顶点,把整个点集作为一条轮廓来处理,而不是将它们当成独立的点来看待。

  • binaryImage:可选参数,布尔值。当该参数为 True 时,array 内所有的非零值都被处理为 1。

三、返回值

  • retval:一个字典对象,包含了轮廓的各种矩信息。这些矩可以用于进一步分析,如计算轮廓的重心、长宽比、旋转角度等。

四、矩的类型和含义

  • 零阶矩(m00):表示轮廓的面积。这是一个比较直观的含义,可以通过 M[“m00”] 来访问。
  • 一阶矩(m10, m01):与轮廓的质心位置有关。其中,m10 表示关于 x 轴的亮度加权平均值(即质心的 x 坐标),m01 表示关于 y 轴的亮度加权平均值(即质心的 y 坐标)。
  • 二阶矩及更高阶矩:提供了关于图像形状相对于其中心的分布信息,以及更复杂的形状特征。

五、使用示例

importcv2importnumpy asnp # 读取图像img =cv2.imread("image.jpg")# 转换为灰度图像并二值化gray =cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)ret,binary =cv2.threshold(gray,127,255,cv2.THRESH_BINARY)# 查找轮廓contours,_ =cv2.findContours(binary,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)# 计算每个轮廓的矩forcontour incontours:M =cv2.moments(contour)print("轮廓的矩:",M)print("轮廓的面积:",M["m00"])

六、注意事项

  • 在使用 cv2.moments 函数之前,需要确保已经正确读取了图像并找到了轮廓。
  • 返回值是一个字典对象,可以通过键来访问不同类型的矩值。
  • 矩值可以用于后续的形状分析和特征提取任务中。

综上所述,cv2.moments 函数是 OpenCV 库中用于计算图像轮廓矩的重要工具,它提供了关于图像形状的重要信息,并广泛应用于形状分析、特征提取和形状识别等任务中。

7、参考

  • https://docs.opencv.org/5.x/d0/d49/tutorial_moments.html

[责任编辑:百度一下]
检察日报数字报 | 正义网 |
Copyrights©最高人民检察院 All Rights Reserved.