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

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

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

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

Cv2.DrawContours

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

摘要:本页演示 DrawContours(InputOutputArray, IEnumerable<IEnumerable>, int, Scalar, int, LineTypes, IEnumerable?, int, Point?) 如何按轮廓层级绘制指定子树,并说明 contourIdx 和 maxLevel 的配合方式。

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

publicstaticvoidDrawContours(InputOutputArrayimage,IEnumerable<IEnumerable<Point>>contours,intcontourIdx,Scalarcolor,intthickness=1,LineTypeslineType=LineTypes.Link8,IEnumerable<HierarchyIndex>?hierarchy=null,intmaxLevel=int.MaxValue,Point?offset=null)

2. 函数用途

Cv2.DrawContours(...)用来在图像上绘制一个或多个轮廓的边界,或者在thickness < 0时直接填充轮廓区域。这个重载直接接收点集和层级信息,最适合从“轮廓树”角度理解绘制范围。

它常用于:

  1. 轮廓可视化。
  2. 目标分组和子轮廓高亮。
  3. 带孔区域的边界说明。
  4. 从 FindContours 的结果里筛选某个轮廓子树。

3. 函数公式

如果把轮廓看成有父子关系的树,那么绘制范围可以理解成:

S ( contourIdx , maxLevel ) = { c ∣ c ∈ subtree ( contourIdx ) , depth ( c ) ≤ maxLevel } S(\text{contourIdx},\text{maxLevel})=\{c\mid c\in \text{subtree}(\text{contourIdx}),\;\text{depth}(c)\le \text{maxLevel}\}S(contourIdx,maxLevel)={ccsubtree(contourIdx),depth(c)maxLevel}

这个公式不是 OpenCV 的源码公式,而是帮助初学者理解contourIdxmaxLevel的抽象方式。contourIdx负责选根,maxLevel负责决定向下展开多少层。

4. 函数原理说明

这个函数会先读取轮廓集合,再根据contourIdx选择从哪一个轮廓开始绘制。如果传入了层级信息,maxLevel就会决定要不要继续画子轮廓、孙轮廓。

OpenCV 官方文档明确说明:

  1. thickness >= 0时绘制轮廓边界。
  2. thickness < 0时填充轮廓内部。
  3. maxLevel只在存在层级信息时才有意义。
  4. offset会把所有绘制出来的轮廓一起平移。

对初学者来说,最重要的是理解:DrawContours不是简单地“把数组画出来”,它会根据层级关系决定究竟画哪一部分。

5. 参数含义解析

参数名类型必填含义
imageInputOutputArray要被绘制的图像
contoursIEnumerable<IEnumerable>每条轮廓的点集
contourIdxint要从哪条轮廓开始绘制,负数表示全部绘制
colorScalar轮廓颜色
thicknessint线宽,负数表示填充
lineTypeLineTypes线型,默认Link8
hierarchyIEnumerable?轮廓层级信息
maxLevelint最大绘制层级,默认int.MaxValue
offsetPoint?所有轮廓的整体平移量

补充说明:

  1. hierarchy的每一项都对应一条轮廓。
  2. contourIdx=0并不代表“全部轮廓”,它只代表从第 1 条轮廓开始。
  3. maxLevel越大,递归展开得越深。

6. 应用场景列表

场景名场景说明典型用途
场景A:轮廓树可视化只画一个轮廓及其子树层级教学
场景B:孔洞轮廓看外轮廓和内轮廓的关系掩膜处理
场景C:局部高亮只强调某个目标轮廓调试展示
场景D:结果标注给提取结果加颜色视觉分析

7. 函数使用示例

说明:下面示例对应 WPF 场景 A。它用点集和层级信息绘制轮廓子树,并展示contourIdx=0maxLevel=2的效果。

usingOpenCvSharp;internalstaticclassProgram{privatestaticvoidMain(){// 准备一组有父子关系的轮廓:外轮廓、洞、洞中的小岛,以及右侧独立轮廓。varcontours=new[]{new[]{newPoint(52,50),newPoint(158,40),newPoint(196,98),newPoint(170,174),newPoint(88,186),newPoint(42,122),},new[]{newPoint(88,82),newPoint(138,78),newPoint(154,118),newPoint(126,144),newPoint(82,124),},new[]{newPoint(108,98),newPoint(126,94),newPoint(132,114),newPoint(112,122),},new[]{newPoint(244,62),newPoint(312,46),newPoint(346,94),newPoint(328,156),newPoint(260,146),newPoint(232,100),},};// 手工构造层级信息,和上面的四条轮廓一一对应。varhierarchy=new[]{newHierarchyIndex(3,-1,1,-1),newHierarchyIndex(-1,-1,2,0),newHierarchyIndex(-1,-1,-1,1),newHierarchyIndex(-1,0,-1,-1),};usingvarcanvas=newMat(260,400,MatType.CV_8UC3,Scalar.White);usingvartarget=InputOutputArray.Create(canvas);// contourIdx=0 表示从第 1 条轮廓开始,maxLevel=2 表示展开两层子轮廓。Cv2.DrawContours(target,contours,0,newScalar(53,114,223),3,LineTypes.AntiAlias,hierarchy,2,null);// 保存图片,检查轮廓子树是否按预期绘制。Cv2.ImWrite("drawcontours-point-enumerable.png",canvas);}}

8. 注意事项

  1. contourIdx小于 0 时会绘制所有轮廓。
  2. maxLevel只有在提供层级信息时才有效。
  3. thickness < 0会触发填充模式。
  4. offset只是整体平移,不会改变轮廓形状。

9. 调优建议

  1. 如果只想高亮某个轮廓家族,优先控制contourIdxmaxLevel
  2. 想让子轮廓关系更清楚,可以给不同层级使用不同颜色再叠加说明。
  3. 如果要表达“里面还有洞”,建议先画外轮廓再画内轮廓。
  4. 教学时可以把轮廓编号写在边界附近,帮助初学者对照数组顺序。

10. 进阶扩展

  1. 可以把FindContours的结果直接拿来做这个函数的输入。
  2. 可以结合ContourAreaBoundingRect一起讲轮廓几何。
  3. 可以用offset做拖拽式轮廓平移。
  4. 可以用thickness=-1配合层级信息做填充掩膜。

11. 常见错误排查

  1. 误以为contourIdx=0会画全部轮廓。
  2. 没有层级信息却期待maxLevel产生递归效果。
  3. thickness < 0和正数线宽混淆。
  4. 忽略轮廓点顺序,导致图形方向和层级理解混乱。

相关链接:

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