OpencvSharp 算子学习教案之 - Cv2.Moments 重载1
2026/5/28 11:00:34 网站建设 项目流程

OpencvSharp 算子学习教案之 - Cv2.Moments 重载1

大家好,Opencv在很多工程项目中都会用到,而OpencvSharp则是以C#开发与实现的Opencv操作库,对.NET开发人员友好,但很多API的中文资料、应用场景及常见坑点等缺乏系统性归纳,因此这系列博客将给大家带来Cv2及Mat对象全系列算子学习教案,供大家参考学习。

Cv2.Moments

  • 教案版本:V1.0
  • 面向对象:OpenCvSharp 初学者
  • 所属模块:imgproc
  • 源码位置:OpenCvSharp/Cv2/Cv2_imgproc.cs:2557

摘要:本页演示Cv2.Moments(InputArray, bool)如何对单通道图像计算空间矩,并用binaryImage对比“灰度加权”和“非零计数”两种统计方式。

1. 函数名称(带参数签名)

publicstaticMomentsMoments(InputArrayarray,boolbinaryImage=false)

2. 函数用途

Cv2.Moments(InputArray, bool)用来对输入图像或输入数组计算矩对象Moments

它最常见的用途有:

  1. 计算目标区域的面积M00
  2. 计算目标区域的质心位置。
  3. 配合轮廓、遮罩或灰度图做形状分析。
  4. 为后续的HuMoments、目标跟踪或质心定位提供基础数据。

这个重载最适合“先有一张图,再去统计它的形状和权重”的场景。

3. 函数公式

对图像输入时,空间矩通常可以写成:

m p q = ∑ x ∑ y I ( x , y ) x p y q m_{pq} = \sum_x \sum_y I(x, y) x^p y^qmpq=xyI(x,y)xpyq

其中:

  1. I(x, y)是像素值。
  2. binaryImage = false时,像素值会作为权重参与计算。
  3. binaryImage = true时,所有非零像素都按1参与计算。

质心通常由下面的公式给出:

x ˉ = m 10 m 00 , y ˉ = m 01 m 00 \bar{x} = \frac{m_{10}}{m_{00}},\qquad \bar{y} = \frac{m_{01}}{m_{00}}xˉ=m00m10,yˉ=m00m01

中心矩和归一化中心矩则用于描述形状相对质心的分布:

μ p q = ∑ x ∑ y ( x − x ˉ ) p ( y − y ˉ ) q I ( x , y ) \mu_{pq} = \sum_x \sum_y (x - \bar{x})^p (y - \bar{y})^q I(x, y)μpq=xy(xxˉ)p(yyˉ)qI(x,y)

η p q = μ p q μ 00 1 + p + q 2 \eta_{pq} = \frac{\mu_{pq}}{\mu_{00}^{1 + \frac{p+q}{2}}}ηpq=μ001+2p+qμpq

4. 函数原理说明

InputArray是 OpenCvSharp 里非常通用的输入包装,它可以把MatUMat等输入统一传给底层实现。

对于这个重载,理解时可以分成三步:

  1. 先把输入对象交给 OpenCV 的矩计算逻辑。
  2. 再根据binaryImage决定“按灰度权重统计”还是“按非零像素统计”。
  3. 最后把计算结果写进Moments结构体。

对初学者来说,最重要的是记住:

  1. M00常常可以理解为“总质量”或“面积”。
  2. binaryImage=false时,亮一点的像素会贡献更多权重。
  3. binaryImage=true时,像素强度被忽略,只保留“有没有值”。
  4. 如果M000,就不能直接除以M00计算质心。

5. 参数含义解析

参数名类型必填含义
arrayInputArray输入图像或输入数据
binaryImagebool是否把非零像素统一按 1 处理

补充说明:

  1. 这个重载最适合单通道图像。
  2. 如果输入是彩色图,通常应该先转成灰度图,再计算矩。
  3. binaryImage=true常用于二值掩码、轮廓区域或阈值结果。
  4. binaryImage=false更适合灰度权重图、热力图或概率图。

6. 应用场景列表

场景名场景说明典型用途
场景A:灰度加权质心用不同亮度的图像区域计算质心目标中心定位
场景B:二值掩码面积用阈值结果或 mask 计算面积前景统计
场景C:热力图统计用权重图计算重心概率图分析
场景D:灰度轮廓前处理图像先转灰度后再统计轮廓分析入口

7. 函数使用示例(与 WPF 场景一一对应)

说明:下面的示例和 WPF 场景 A 对应。代码故意构造了一张“亮度不均匀”的灰度图,这样binaryImage=falsebinaryImage=true的差异会非常明显。

usingSystem;usingOpenCvSharp;internalstaticclassProgram{privatestaticvoidMain(){// 创建一张单通道灰度图,背景保持为 0,方便突出前景区域的矩统计。usingvarsource=CreateWeightedDemoImage();// binaryImage=false:像素值越大,参与统计时的权重越高。Momentsweighted=Cv2.Moments(source,false);// binaryImage=true:只要像素非零,就统一按 1 处理。Momentsbinary=Cv2.Moments(source,true);// 打印结果时先判断 M00,避免后面计算质心时出现除零。PrintMoments("binaryImage=false",weighted);PrintMoments("binaryImage=true",binary);}privatestaticMatCreateWeightedDemoImage(){// 先准备一张灰度画布,尺寸和 WPF 场景里的示例保持一致。varcanvas=newMat(260,360,MatType.CV_8UC1,Scalar.All(0));// 用一个大多边形作为基础前景,这样面积本身就有明确的形状。Cv2.FillPoly(canvas,new[]{new[]{newPoint(46,132),newPoint(102,64),newPoint(248,52),newPoint(314,114),newPoint(292,220),newPoint(188,248),newPoint(78,228),newPoint(40,168),}},newScalar(92));// 再叠加一个更亮的小圆,让加权质心向右上方偏移。Cv2.Circle(canvas,newPoint(258,92),46,newScalar(212),-1,LineTypes.AntiAlias);// 再加一个中等亮度的矩形和圆形,模拟“同一目标内部亮度不同”的情况。Cv2.Rectangle(canvas,newRect(94,166,90,48),newScalar(148),-1,LineTypes.AntiAlias);Cv2.Circle(canvas,newPoint(208,194),30,newScalar(180),-1,LineTypes.AntiAlias);returncanvas;}privatestaticvoidPrintMoments(stringname,Momentsmoments){// 当 M00 为 0 时,说明没有有效前景,不能直接用它做除法。varcentroidX=moments.M00==0?double.NaN:moments.M10/moments.M00;varcentroidY=moments.M00==0?double.NaN:moments.M01/moments.M00;Console.WriteLine($"{name}");Console.WriteLine($"M00 ={moments.M00:F3}");Console.WriteLine($"Centroid = ({centroidX:F2},{centroidY:F2})");Console.WriteLine($"Mu20 ={moments.Mu20:F3}");Console.WriteLine($"Mu11 ={moments.Mu11:F3}");Console.WriteLine($"Mu02 ={moments.Mu02:F3}");Console.WriteLine();}}

8. 常见错误与避坑

  1. 把彩色图直接传给矩函数,却没有先转成单通道灰度图。
  2. 忘记M00可能为 0,直接拿它做除法。
  3. binaryImage=truefalse混为一谈,以为结果不会变化。
  4. 以为矩只和轮廓边界有关,实际上图像输入时和所有像素权重都有关。

9. 进阶扩展

  1. 结合HuMoments()可以得到 7 个不变矩。
  2. 结合阈值分割可以稳定地计算前景质心。
  3. 结合ConnectedComponents可以批量统计多个目标的矩。
  4. 结合轮廓提取算法,可以做形状识别和姿态判断。

10. 小结

Cv2.Moments(InputArray, bool)是最通用的矩统计入口。只要记住下面三点就够了:

  1. 图像输入时,binaryImage决定是否按权重统计。
  2. M00常常代表面积或总质量。
  3. 质心坐标就是M10 / M00M01 / M00

11. 相关链接

  • WPF 教学控件:Cv2MomentsControl.xaml.cs
  • 样例实现:MomentsInputArraySample.cs
  • 官方文档源码位置:OpenCvSharp/Cv2/Cv2_imgproc.cs:2557

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询