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,boolbinaryImagefalse)2. 函数用途Cv2.Moments(InputArray, bool)用来对输入图像或输入数组计算矩对象Moments。它最常见的用途有计算目标区域的面积M00。计算目标区域的质心位置。配合轮廓、遮罩或灰度图做形状分析。为后续的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^qmpqx∑y∑I(x,y)xpyq其中I(x, y)是像素值。当binaryImage false时像素值会作为权重参与计算。当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)μpqx∑y∑(x−xˉ)p(y−yˉ)qI(x,y)η p q μ p q μ 00 1 p q 2 \eta_{pq} \frac{\mu_{pq}}{\mu_{00}^{1 \frac{pq}{2}}}ηpqμ0012pqμpq4. 函数原理说明InputArray是 OpenCvSharp 里非常通用的输入包装它可以把Mat、UMat等输入统一传给底层实现。对于这个重载理解时可以分成三步先把输入对象交给 OpenCV 的矩计算逻辑。再根据binaryImage决定“按灰度权重统计”还是“按非零像素统计”。最后把计算结果写进Moments结构体。对初学者来说最重要的是记住M00常常可以理解为“总质量”或“面积”。binaryImagefalse时亮一点的像素会贡献更多权重。binaryImagetrue时像素强度被忽略只保留“有没有值”。如果M00为0就不能直接除以M00计算质心。5. 参数含义解析参数名类型必填含义arrayInputArray是输入图像或输入数据binaryImagebool否是否把非零像素统一按 1 处理补充说明这个重载最适合单通道图像。如果输入是彩色图通常应该先转成灰度图再计算矩。binaryImagetrue常用于二值掩码、轮廓区域或阈值结果。binaryImagefalse更适合灰度权重图、热力图或概率图。6. 应用场景列表场景名场景说明典型用途场景A灰度加权质心用不同亮度的图像区域计算质心目标中心定位场景B二值掩码面积用阈值结果或 mask 计算面积前景统计场景C热力图统计用权重图计算重心概率图分析场景D灰度轮廓前处理图像先转灰度后再统计轮廓分析入口7. 函数使用示例与 WPF 场景一一对应说明下面的示例和 WPF 场景 A 对应。代码故意构造了一张“亮度不均匀”的灰度图这样binaryImagefalse和binaryImagetrue的差异会非常明显。usingSystem;usingOpenCvSharp;internalstaticclassProgram{privatestaticvoidMain(){// 创建一张单通道灰度图背景保持为 0方便突出前景区域的矩统计。usingvarsourceCreateWeightedDemoImage();// binaryImagefalse像素值越大参与统计时的权重越高。MomentsweightedCv2.Moments(source,false);// binaryImagetrue只要像素非零就统一按 1 处理。MomentsbinaryCv2.Moments(source,true);// 打印结果时先判断 M00避免后面计算质心时出现除零。PrintMoments(binaryImagefalse,weighted);PrintMoments(binaryImagetrue,binary);}privatestaticMatCreateWeightedDemoImage(){// 先准备一张灰度画布尺寸和 WPF 场景里的示例保持一致。varcanvasnewMat(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 时说明没有有效前景不能直接用它做除法。varcentroidXmoments.M000?double.NaN:moments.M10/moments.M00;varcentroidYmoments.M000?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. 常见错误与避坑把彩色图直接传给矩函数却没有先转成单通道灰度图。忘记M00可能为 0直接拿它做除法。把binaryImagetrue和false混为一谈以为结果不会变化。以为矩只和轮廓边界有关实际上图像输入时和所有像素权重都有关。9. 进阶扩展结合HuMoments()可以得到 7 个不变矩。结合阈值分割可以稳定地计算前景质心。结合ConnectedComponents可以批量统计多个目标的矩。结合轮廓提取算法可以做形状识别和姿态判断。10. 小结Cv2.Moments(InputArray, bool)是最通用的矩统计入口。只要记住下面三点就够了图像输入时binaryImage决定是否按权重统计。M00常常代表面积或总质量。质心坐标就是M10 / M00和M01 / M00。11. 相关链接WPF 教学控件Cv2MomentsControl.xaml.cs样例实现MomentsInputArraySample.cs官方文档源码位置OpenCvSharp/Cv2/Cv2_imgproc.cs:2557
OpencvSharp 算子学习教案之 - Cv2.Moments 重载1
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,boolbinaryImagefalse)2. 函数用途Cv2.Moments(InputArray, bool)用来对输入图像或输入数组计算矩对象Moments。它最常见的用途有计算目标区域的面积M00。计算目标区域的质心位置。配合轮廓、遮罩或灰度图做形状分析。为后续的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^qmpqx∑y∑I(x,y)xpyq其中I(x, y)是像素值。当binaryImage false时像素值会作为权重参与计算。当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)μpqx∑y∑(x−xˉ)p(y−yˉ)qI(x,y)η p q μ p q μ 00 1 p q 2 \eta_{pq} \frac{\mu_{pq}}{\mu_{00}^{1 \frac{pq}{2}}}ηpqμ0012pqμpq4. 函数原理说明InputArray是 OpenCvSharp 里非常通用的输入包装它可以把Mat、UMat等输入统一传给底层实现。对于这个重载理解时可以分成三步先把输入对象交给 OpenCV 的矩计算逻辑。再根据binaryImage决定“按灰度权重统计”还是“按非零像素统计”。最后把计算结果写进Moments结构体。对初学者来说最重要的是记住M00常常可以理解为“总质量”或“面积”。binaryImagefalse时亮一点的像素会贡献更多权重。binaryImagetrue时像素强度被忽略只保留“有没有值”。如果M00为0就不能直接除以M00计算质心。5. 参数含义解析参数名类型必填含义arrayInputArray是输入图像或输入数据binaryImagebool否是否把非零像素统一按 1 处理补充说明这个重载最适合单通道图像。如果输入是彩色图通常应该先转成灰度图再计算矩。binaryImagetrue常用于二值掩码、轮廓区域或阈值结果。binaryImagefalse更适合灰度权重图、热力图或概率图。6. 应用场景列表场景名场景说明典型用途场景A灰度加权质心用不同亮度的图像区域计算质心目标中心定位场景B二值掩码面积用阈值结果或 mask 计算面积前景统计场景C热力图统计用权重图计算重心概率图分析场景D灰度轮廓前处理图像先转灰度后再统计轮廓分析入口7. 函数使用示例与 WPF 场景一一对应说明下面的示例和 WPF 场景 A 对应。代码故意构造了一张“亮度不均匀”的灰度图这样binaryImagefalse和binaryImagetrue的差异会非常明显。usingSystem;usingOpenCvSharp;internalstaticclassProgram{privatestaticvoidMain(){// 创建一张单通道灰度图背景保持为 0方便突出前景区域的矩统计。usingvarsourceCreateWeightedDemoImage();// binaryImagefalse像素值越大参与统计时的权重越高。MomentsweightedCv2.Moments(source,false);// binaryImagetrue只要像素非零就统一按 1 处理。MomentsbinaryCv2.Moments(source,true);// 打印结果时先判断 M00避免后面计算质心时出现除零。PrintMoments(binaryImagefalse,weighted);PrintMoments(binaryImagetrue,binary);}privatestaticMatCreateWeightedDemoImage(){// 先准备一张灰度画布尺寸和 WPF 场景里的示例保持一致。varcanvasnewMat(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 时说明没有有效前景不能直接用它做除法。varcentroidXmoments.M000?double.NaN:moments.M10/moments.M00;varcentroidYmoments.M000?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. 常见错误与避坑把彩色图直接传给矩函数却没有先转成单通道灰度图。忘记M00可能为 0直接拿它做除法。把binaryImagetrue和false混为一谈以为结果不会变化。以为矩只和轮廓边界有关实际上图像输入时和所有像素权重都有关。9. 进阶扩展结合HuMoments()可以得到 7 个不变矩。结合阈值分割可以稳定地计算前景质心。结合ConnectedComponents可以批量统计多个目标的矩。结合轮廓提取算法可以做形状识别和姿态判断。10. 小结Cv2.Moments(InputArray, bool)是最通用的矩统计入口。只要记住下面三点就够了图像输入时binaryImage决定是否按权重统计。M00常常代表面积或总质量。质心坐标就是M10 / M00和M01 / M00。11. 相关链接WPF 教学控件Cv2MomentsControl.xaml.cs样例实现MomentsInputArraySample.cs官方文档源码位置OpenCvSharp/Cv2/Cv2_imgproc.cs:2557