OpencvSharp 算子学习教案之 - Cv2.Moments 重载4
OpencvSharp 算子学习教案之 - Cv2.Moments 重载4大家好Opencv在很多工程项目中都会用到而OpencvSharp则是以C#开发与实现的Opencv操作库对.NET开发人员友好但很多API的中文资料、应用场景及常见坑点等缺乏系统性归纳因此这系列博客将给大家带来Cv2及Mat对象全系列算子学习教案供大家参考学习。Cv2.Moments教案版本V1.0面向对象OpenCvSharp 初学者所属模块imgproc源码位置OpenCvSharp/Cv2/Cv2_imgproc.cs:2593摘要本页演示Cv2.Moments(IEnumerablePoint, bool)如何对整数轮廓点集求矩并强调点集输入更接近“轮廓面积”而不是“像素逐点加权”。1. 函数名称带参数签名publicstaticMomentsMoments(IEnumerablePointarray,boolbinaryImagefalse)2. 函数用途Cv2.Moments(IEnumerablePoint, bool)用来对整数轮廓点集计算矩。它最常见的用途有计算多边形轮廓的面积。计算轮廓的质心。用轮廓矩描述物体形状。为后续的形状匹配、筛选和分析提供输入。这个重载最适合“已经拿到轮廓点”的场景。3. 函数公式对于点集输入OpenCV 不是简单地把点一个个当像素去求和而是按轮廓几何意义来计算矩。直观地说它更接近多边形面积和边界积分。空间矩、中心矩和归一化中心矩仍然可以用同样的记号理解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)xpyqx ˉ 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)对轮廓输入来说最重要的补充是这里的计算更接近 Green 公式。结果可能和“把轮廓先栅格化以后再算矩”略有差异。如果轮廓退化成一条线或一个点M00可能为 0。4. 函数原理说明点集版本更像“几何轮廓分析”而不是“像素权重统计”。对初学者来说可以这样理解Point代表一个整数顶点。OpenCV 会把这些点连起来看成一条闭合轮廓。矩的计算结果反映的是这个轮廓包围的形状。这里的binaryImage参数通常只是兼容参数对点集来说它通常不会改变你想表达的几何意义。教学里可以把它理解为“保留默认即可”。真正要关注的是轮廓点的顺序、闭合性和是否退化。5. 参数含义解析参数名类型必填含义arrayIEnumerable是整数轮廓点集binaryImagebool否兼容参数通常保持默认即可补充说明点集必须能表示一个合理的轮廓。点的顺序通常要沿着轮廓边界排列。闭合轮廓会更符合面积矩的语义。如果轮廓退化质心计算可能没有意义。6. 应用场景列表场景名场景说明典型用途场景A多边形面积对闭合多边形求矩面积统计场景B目标轮廓质心对分割轮廓求中心目标定位场景C形状筛选用矩过滤过小或过大的轮廓目标预筛选场景D轮廓比较用矩特征比较两个对象形状分析7. 函数使用示例与 WPF 场景一一对应说明下面示例对应 WPF 场景 D。它用一组整数点构造一个多边形轮廓再计算轮廓矩和质心。usingSystem;usingSystem.Collections.Generic;usingOpenCvSharp;internalstaticclassProgram{privatestaticvoidMain(){// 用整数点直接描述一个闭合轮廓点的顺序要沿着边界排列。varcontournewListPoint{newPoint(46,132),newPoint(102,64),newPoint(248,52),newPoint(314,114),newPoint(292,220),newPoint(188,248),newPoint(78,228),newPoint(40,168),};// 对点集输入来说binaryImage 参数通常不会改变我们想表达的几何语义。MomentscontourMomentsCv2.Moments(contour,false);MomentscontourMomentsBinaryCv2.Moments(contour,true);PrintMoments(binaryImagefalse,contourMoments);PrintMoments(binaryImagetrue,contourMomentsBinary);}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. 常见错误与避坑点集没有按轮廓顺序排列导致几何意义不稳定。轮廓不闭合或者最后一个点没有和第一个点形成完整边界。误以为binaryImage在点集输入里会像图像输入那样改变权重。忘记M00 0时不能直接计算质心。9. 进阶扩展可以把轮廓矩和ApproxPolyDP结合做更稳定的形状简化。可以把矩和HuMoments()结合做基础形状识别。可以把轮廓矩和FindContours结合批量分析多个目标。可以把轮廓矩和连通域分析结合筛掉噪声轮廓。10. 小结Cv2.Moments(IEnumerablePoint, bool)最适合轮廓输入。只要记住下面三点就够了它更像轮廓几何统计不是像素加权统计。binaryImage对点集输入通常不是重点。轮廓退化时要特别注意M00是否为 0。11. 相关链接WPF 教学控件Cv2MomentsControl.xaml.cs样例实现MomentsPointEnumerableSample.cs官方文档源码位置OpenCvSharp/Cv2/Cv2_imgproc.cs:2593