OpencvSharp 算子学习教案之 - Cv2.ConnectedComponents 重载3
大家好,Opencv在很多工程项目中都会用到,而OpencvSharp则是以C#开发与实现的Opencv操作库,对.NET开发人员友好,但很多API的中文资料、应用场景及常见坑点等缺乏系统性归纳,因此这系列博客将给大家带来Cv2及Mat对象全系列算子学习教案,供大家参考学习。
Cv2.ConnectedComponents
- 教案版本:V1.0
- 面向对象:OpenCvSharp 初学者
- 所属模块:imgproc
- 源码位置:OpenCvSharp/Cv2/Cv2_imgproc.cs:2745
摘要:本页演示Cv2.ConnectedComponents(InputArray, out int[,], PixelConnectivity)如何直接返回二维整数标签数组。这个重载很适合想在纯 C# 里继续遍历、筛选或持久化标签结果的场景。
1. 函数名称(带参数签名)
publicstaticintConnectedComponents(InputArrayimage,outint[,]labels,PixelConnectivityconnectivity)2. 函数用途
Cv2.ConnectedComponents(..., out int[,], ...)用来把连通域结果直接返回成二维int数组。
它最常见的用途有:
- 想在纯 C# 里继续处理标签图。
- 想把标签结果做序列化、存档或传输。
- 想在不依赖
Mat的情况下做后续逻辑。 - 想让教学代码更像“普通数组处理”,降低入门门槛。
这个重载特别适合初学者,因为它把结果直接变成了 C# 里的二维数组。
3. 函数公式
这个重载的数学意义和默认重载一致,只是输出容器变成了int[,]:
L ( p ) = k L(p) = kL(p)=k
其中:
L(p)是像素p的标签编号。k = 0表示背景。k > 0表示第k个前景连通块。
前景块数仍然可以写成:
F o r e g r o u n d C o u n t = L a b e l C o u n t − 1 ForegroundCount = LabelCount - 1ForegroundCount=LabelCount−1
4. 函数原理说明
和输出Mat的版本相比,这个重载的区别只在于“结果怎么装起来”。
对初学者来说,可以这样理解:
- OpenCV 先完成连通域标记。
- OpenCvSharp 再把标签图转换成二维
int数组。 - 这样你就可以直接在 C# 中用
labels[row, col]访问每个像素所属的连通块。
这个重载最大的优点是简单直观,最大的教学价值是让你更快理解“标签图其实就是一个二维编号表”。
我们在示例里故意使用 4 连通,这样对角桥上的像素会被拆成几个独立标签,差异会更明显。
5. 参数含义解析
| 参数名 | 类型 | 必填 | 含义 |
|---|---|---|---|
| image | InputArray | 是 | 8 位单通道二值图 |
| labels | int[,] | 是 | 输出二维整数标签数组 |
| connectivity | PixelConnectivity | 是 | 4 连通或 8 连通 |
补充说明:
labels的行列和输入图完全对应。- 0 号标签仍然表示背景。
- 4 连通会比 8 连通更严格。
- 如果你只想在 C# 中做遍历,这个重载非常方便。
6. 应用场景列表
| 场景名 | 场景说明 | 典型用途 |
|---|---|---|
| 场景A:数组遍历 | 直接遍历labels[row, col] | 纯 C# 处理 |
| 场景B:结果存档 | 把二维数组保存到文件 | 数据持久化 |
| 场景C:规则筛选 | 对特定标签做额外处理 | 后续过滤 |
| 场景D:教学入门 | 让初学者把标签图理解成编号表 | 入门学习 |
7. 函数使用示例(与 WPF 场景一一对应)
说明:下面示例对应 WPF 场景 C。它会直接拿到
int[,],再把数组转回Mat做热力图显示。
usingSystem;usingOpenCvSharp;internalstaticclassProgram{privatestaticvoidMain(){// 先准备一张二值图,前景块的数量和位置都尽量简单、稳定,方便观察。usingvarsource=CreateDemoImage();// 这个重载会直接把结果写成二维 int 数组,很适合在纯 C# 里继续处理。varlabelCount=Cv2.ConnectedComponents(source,outint[,]labels,PixelConnectivity.Connectivity4);// 打印数组信息时,先确认它的行列数和原图一致。Console.WriteLine($"LabelCount ={labelCount}");Console.WriteLine($"ForegroundCount ={labelCount-1}");Console.WriteLine($"ArraySize ={labels.GetLength(0)}x{labels.GetLength(1)}");// 把二维数组转回 Mat,再映射成热力图,初学者更容易看见标签分布。usingvarlabelMat=Mat.FromPixelData(labels.GetLength(0),labels.GetLength(1),MatType.CV_32SC1,labels);SaveLabelHeatmap(labelMat,"connected_int_array.png");}privatestaticMatCreateDemoImage(){// 背景保持为 0,前景统一填 255,满足连通域函数对二值图的要求。varcanvas=newMat(260,420,MatType.CV_8UC1,Scalar.All(0));// 两个大块通过对角像素连接,便于对比 4 连通和 8 连通。Cv2.Rectangle(canvas,newRect(32,38,64,52),Scalar.All(255),-1,LineTypes.AntiAlias);Cv2.Rectangle(canvas,newRect(100,94,64,48),Scalar.All(255),-1,LineTypes.AntiAlias);for(varoffset=0;offset<4;offset++){Cv2.Rectangle(canvas,newRect(96+offset,90+offset,1,1),Scalar.All(255),-1);}// 再补几个孤立块,让数组里的标签编号更有层次。Cv2.Circle(canvas,newPoint(314,64),26,Scalar.All(255),-1,LineTypes.AntiAlias);Cv2.Ellipse(canvas,newPoint(296,186),newSize(36,26),12,0,360,Scalar.All(255),-1,LineTypes.AntiAlias);Cv2.Rectangle(canvas,newRect(44,176,54,38),Scalar.All(255),-1,LineTypes.AntiAlias);returncanvas;}privatestaticvoidSaveLabelHeatmap(Matlabels,stringfileName){// 标签编号本身只是整数,所以先拉伸到 0~255 再套颜色映射最容易观察。usingvarlabels64=newMat();labels.ConvertTo(labels64,MatType.CV_64FC1);usingvarnormalized=newMat();Cv2.Normalize(labels64,normalized,0,255,NormTypes.MinMax,MatType.CV_8UC1);usingvarheatmap=newMat();Cv2.ApplyColorMap(normalized,heatmap,ColormapTypes.Turbo);Cv2.ImWrite(fileName,heatmap);}}8. 常见错误与避坑
- 误以为
int[,]只适合小数据,其实它只是更方便教学和纯 C# 处理。 - 忘记数组的第一维是行,第二维是列。
- 以为 4 连通和 8 连通只是名字不同。
- 直接把数组当成普通图像像素值去显示,结果看不出标签编号。
9. 进阶扩展
- 可以把
int[,]直接写入 CSV,再交给其他程序分析。 - 可以把数组转换回
Mat后做颜色映射或绘图。 - 可以把标签数组和
stats表一起存下来,形成完整的连通域分析结果。 - 可以把 4 连通和 8 连通的数组结果并排比较。
10. 小结
Cv2.ConnectedComponents(..., out int[,], ...)的核心价值在于简单:
- 它直接给你一个二维整数数组。
- 它很适合 C# 教学和后续遍历。
- 它和
Mat版本的数学含义完全一致。
11. 相关链接
- WPF 教学控件:Cv2ConnectedComponentsControl.xaml.cs
- 样例实现:ConnectedComponentsIntArraySample.cs
- 官方文档源码位置:OpenCvSharp/Cv2/Cv2_imgproc.cs:2745