当前位置: 首页 > news >正文

OpencvSharp 算子学习教案之 - Cv2.Dilate

OpencvSharp 算子学习教案之 - Cv2.Dilate

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

Cv2.Dilate

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

摘要:Cv2.Dilate 会让二值图或灰度图中的前景向外扩张,是理解形态学“膨胀”概念的起点。本文用两组结构元素和迭代次数对比,帮助初学者观察前景如何变粗、缝隙如何被填平,以及默认边界值为什么要配套理解。

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

publicstaticvoidDilate(InputArraysrc,OutputArraydst,InputArray?element,Point?anchor=null,intiterations=1,BorderTypesborderType=BorderTypes.Constant,Scalar?borderValue=null)

2. 函数用途

Cv2.Dilate的作用,是让前景区域向外扩张。

它常见的用途有:

  1. 连接断裂的前景区域。
  2. 填平细小缝隙。
  3. 让目标轮廓变粗,便于后续处理。
  4. ErodeMorphologyEx一起构成更复杂的形态学流程。

如果你想先建立“白色会变大”的直觉,先看 Dilate 最合适。

3. 函数公式

对二值或灰度图来说,膨胀可以理解成局部最大值操作:

dst(x,y)=max⁡(i,j)∈Bsrc(x−i,y−j) dst(x, y) = \max_{(i, j) \in B} src(x - i, y - j)dst(x,y)=(i,j)Bmaxsrc(xi,yj)

其中BBB是结构元素。直观地说,只要结构元素覆盖范围里有白色前景,结果就更容易变白。

4. 函数原理说明

Dilate 的教学理解可以分成四步:

  1. 选择一个结构元素。
  2. 把结构元素放到图像上的每个位置。
  3. 只要覆盖范围里有前景,结果就可能被“推亮”。
  4. 重复iterations次后,扩张效果会更明显。

它的视觉效果通常是:白色区域变大,黑色空洞变小,细碎缝隙更容易被填上。

5. 参数含义解析

参数名类型必填含义
srcInputArray输入图像,通常是二值图或灰度图
dstOutputArray输出图像,大小和类型与输入一致
elementInputArray?结构元素,决定膨胀的形状和范围
anchorPoint?锚点位置,默认是结构元素中心
iterationsint膨胀次数
borderTypeBorderTypes边界外推方式
borderValueScalar?常量边界模式下的边界值

补充说明:

  1. element越大,膨胀越明显。
  2. iterations越多,结果越“粗”。
  3. 教学时常用MorphShapes.RectMorphShapes.EllipseMorphShapes.Cross
  4. 常量边界模式下,通常配合Cv2.MorphologyDefaultBorderValue()使用。

6. 应用场景列表

场景名场景说明典型用途
场景A:前景扩张观察白色区域如何变大教学入门
场景B:缝隙连接把断裂边缘连起来二值修复
场景C:轮廓加粗让细轮廓更明显预处理
场景D:与腐蚀对照对比Erode的相反效果理论学习

7. 函数使用示例

下面的 Console 程序演示Cv2.Dilate。示例会对同一张二值图使用两组不同的结构元素和迭代次数,让你直接看到前景扩张的差异。

usingSystem;usingSystem.Globalization;usingSystem.Linq;usingSystem.Text;usingOpenCvSharp;internalstaticclassProgram{/// <summary>/// 程序入口。/// </summary>privatestaticvoidMain(){// 先让控制台可以正确显示中文说明。Console.OutputEncoding=Encoding.UTF8;usingvarsource=CreateMorphologySourceImage();usingvarrectKernel=Cv2.GetStructuringElement(MorphShapes.Rect,newSize(3,3));usingvarellipseKernel=Cv2.GetStructuringElement(MorphShapes.Ellipse,newSize(7,7));usingvarrectDilated=newMat();usingvarellipseDilated=newMat();// 膨胀会把白色前景向外扩张,所以我们用两组参数做对比。Cv2.Dilate(source,rectDilated,rectKernel,iterations:1,borderType:BorderTypes.Constant,borderValue:Cv2.MorphologyDefaultBorderValue());Cv2.Dilate(source,ellipseDilated,ellipseKernel,iterations:2,borderType:BorderTypes.Constant,borderValue:Cv2.MorphologyDefaultBorderValue());Cv2.ImWrite("dilate-source.png",source);Cv2.ImWrite("dilate-rect.png",rectDilated);Cv2.ImWrite("dilate-ellipse.png",ellipseDilated);Cv2.ImWrite("dilate-ellipse-kernel.png",CreateKernelPreview(ellipseKernel));Console.WriteLine("场景A:Dilate(InputArray src, OutputArray dst, InputArray? element, Point? anchor = null, int iterations = 1, BorderTypes borderType = BorderTypes.Constant, Scalar? borderValue = null)");Console.WriteLine("Dilate 会让白色前景向外扩张。\n");Console.WriteLine($"源图:{DescribeBinaryMat(source)}");Console.WriteLine($"3x3 Rect 核:{DescribeKernel(rectKernel)}");Console.WriteLine($"7x7 Ellipse 核:{DescribeKernel(ellipseKernel)}");Console.WriteLine();AppendCaseReport("结果A:3x3 Rect + 1 次",source,rectDilated,"这组参数比较温和,适合先观察前景边界如何变厚。");Console.WriteLine();AppendCaseReport("结果B:7x7 Ellipse + 2 次",source,ellipseDilated,"这组参数更激进,前景会明显向外扩张。");}/// <summary>/// 创建一张适合形态学演示的二值图。/// </summary>privatestaticMatCreateMorphologySourceImage(){varcanvas=newMat(480,640,MatType.CV_8UC1,newScalar(0));// 这张图里包含大块前景、孔洞、细线和孤立区域,适合观察膨胀的变化。Cv2.Rectangle(canvas,newRect(44,44,184,132),newScalar(255),-1,LineTypes.Link8);Cv2.Rectangle(canvas,newRect(84,82,34,34),newScalar(0),-1,LineTypes.Link8);Cv2.Circle(canvas,newPoint(178,132),14,newScalar(0),-1,LineTypes.Link8);Cv2.Circle(canvas,newPoint(348,118),58,newScalar(255),-1,LineTypes.Link8);Cv2.Circle(canvas,newPoint(348,118),18,newScalar(0),-1,LineTypes.Link8);Cv2.Line(canvas,newPoint(62,282),newPoint(568,282),newScalar(255),6,LineTypes.Link8);Cv2.Line(canvas,newPoint(390,194),newPoint(514,92),newScalar(255),4,LineTypes.Link8);Cv2.Rectangle(canvas,newRect(68,248,124,58),newScalar(255),-1,LineTypes.Link8);Cv2.Rectangle(canvas,newRect(102,264,28,18),newScalar(0),-1,LineTypes.Link8);Cv2.Ellipse(canvas,newPoint(218,320),newSize(92,60),18,0,360,newScalar(255),-1,LineTypes.Link8);Cv2.Circle(canvas,newPoint(488,314),44,newScalar(255),-1,LineTypes.Link8);Cv2.Circle(canvas,newPoint(488,314),10,newScalar(0),-1,LineTypes.Link8);returnAddSaltAndPepperNoise(canvas,0.008,2026);}/// <summary>/// 给二值图叠加可重复的椒盐噪声。/// </summary>privatestaticMatAddSaltAndPepperNoise(Matsource,doublenoiseRatio,intseed){varrng=newRandom(seed);varnoisy=source.Clone();varflipCount=Math.Max(1,(int)Math.Round(noisy.Rows*noisy.Cols*noiseRatio));// 随机翻转像素值,会同时产生白点和黑洞。for(varindex=0;index<flipCount;index++){varrow=rng.Next(noisy.Rows);varcol=rng.Next(noisy.Cols);noisy.At<byte>(row,col)=noisy.At<byte>(row,col)==0?(byte)255:(byte)0;}returnnoisy;}/// <summary>/// 把结构元素缩放成便于观察的预览图。/// </summary>privatestaticMatCreateKernelPreview(Matkernel,intscale=28){usingvarkernel64=newMat();usingvarnormalized=newMat();usingvarenlarged=newMat();kernel.ConvertTo(kernel64,MatType.CV_64FC1);Cv2.Normalize(kernel64,normalized,0,255,NormTypes.MinMax,(int)MatType.CV_8UC1);Cv2.Resize(normalized,enlarged,newSize(kernel.Cols*scale,kernel.Rows*scale),0,0,InterpolationFlags.Nearest);returnenlarged;}/// <summary>/// 创建带标签的预览图。/// </summary>privatestaticMatCreateLabeledPreview(Matimage,stringlabel){varpreview=newMat();if(image.Channels()==1){Cv2.CvtColor(image,preview,ColorConversionCodes.GRAY2BGR);}else{preview=image.Clone();}Cv2.PutText(preview,label,newPoint(18,34),HersheyFonts.HersheySimplex,0.9,newScalar(255,255,255),3,LineTypes.AntiAlias);Cv2.PutText(preview,label,newPoint(18,34),HersheyFonts.HersheySimplex,0.9,newScalar(35,35,35),1,LineTypes.AntiAlias);returnpreview;}/// <summary>/// 把二值图格式化成便于教学阅读的摘要。/// </summary>privatestaticstringDescribeBinaryMat(Matimage){Cv2.MinMaxLoc(image,outvarminVal,outvarmaxVal);varactivePixels=Cv2.CountNonZero(image);varratio=activePixels*100.0/(image.Rows*image.Cols);return$"Size={image.Width}x{image.Height}, ForegroundPixels={activePixels}, ForegroundRatio={ratio.ToString("F2",CultureInfo.InvariantCulture)}%, Min={minVal.ToString("F0",CultureInfo.InvariantCulture)}, Max={maxVal.ToString("F0",CultureInfo.InvariantCulture)}";}/// <summary>/// 描述结构元素的核心信息。/// </summary>privatestaticstringDescribeKernel(Matkernel){Cv2.MinMaxLoc(kernel,outvarminVal,outvarmaxVal);varactiveCells=Cv2.CountNonZero(kernel);return$"Size={kernel.Width}x{kernel.Height}, ActiveCells={activeCells}, Min={minVal.ToString("F0",CultureInfo.InvariantCulture)}, Max={maxVal.ToString("F0",CultureInfo.InvariantCulture)}";}/// <summary>/// 追加一个完整的案例报告块。/// </summary>privatestaticvoidAppendCaseReport(stringtitle,Matsource,Matresult,stringcomment){Console.WriteLine(title);Console.WriteLine(comment);Console.WriteLine($"源图摘要:{DescribeBinaryMat(source)}");Console.WriteLine($"结果摘要:{DescribeBinaryMat(result)}");Console.WriteLine($"前景像素变化:{Cv2.CountNonZero(result)-Cv2.CountNonZero(source):+0;-0;0}");usingvardiff=newMat();Cv2.Absdiff(source,result,diff);Console.WriteLine($"实际被改动的像素数:{Cv2.CountNonZero(diff)}");}}

8. 注意事项

  1. 膨胀会让前景变大,不要把它和腐蚀的效果混淆。
  2. element的形状会直接影响膨胀后的轮廓。
  3. iterations越多,前景越容易被推得过大。
  4. 如果你不理解边界行为,先默认使用Cv2.MorphologyDefaultBorderValue()

9. 调优建议

  1. 教学时先从小结构元素开始,比如3x3 Rect
  2. 如果只是想“轻微变粗”,不要把iterations设得太大。
  3. 如果希望保留更多圆角,可以试试Ellipse
  4. 如果前景已经很大,先观察扩张后是否还保留了你真正想要的结构。

10. 运行说明

  1. 如果你在控制台工程里运行本文示例,直接把代码放到Program.cs即可。
  2. 如果你在本仓库里学习,请直接打开 WPF 控件Cv2.Dilate,点击“运行场景A”。
  3. WPF 示例会同时展示源图、结果图和结构元素预览,方便你直观看到膨胀效果。

11. 常见错误排查

  1. 输入图不是二值图,却期待它像二值图那样膨胀。
  2. 结构元素太大,导致细节直接被吞掉。
  3. 只盯着结果大小变化,却忘了观察轮廓形状变化。
  4. 没有把膨胀和腐蚀成对理解。
http://www.jsqmd.com/news/679160/

相关文章:

  • 3D高斯泼溅技术:实时渲染与SLAM系统革新
  • 离开一个不爱你的人,不是损失,而是幸运
  • mysql如何使用INNER JOIN内连接_mysql等值连接实现方式
  • Proxmox VE 8 入门上手系列(7总结篇) 从规划到落地的完整方案
  • 盛合晶微科创板上市,开盘市值近1858亿,无锡国资投资回报率超600%
  • 明日方舟MAA助手终极指南:如何一键解放你的游戏时间?[特殊字符]
  • 为什么92%的边缘项目在Docker 27升级后失败?资深SRE披露3个被官方文档隐藏的systemd-cgroups兼容陷阱
  • NomNom存档编辑器:解锁《无人深空》无限可能的终极解决方案
  • 告别“黑盒”:用Vector Davinci工具链手把手配置你的第一个AUTOSAR SWC
  • 用Python和MATLAB搞定数学建模:从报童问题到轧钢浪费,手把手教你搭建概率模型
  • 别再乱选TVS管了!手把手教你根据USB 3.0 Type-C接口特性搞定选型(附参数对照表)
  • 零成本构建移动服务器:基于Termux的安卓Web服务实战
  • 2026年4月新发布:五大电磁先导头非标定制服务商深度评估与选型指南 - 2026年企业推荐榜
  • AI推理卡在GC上?.NET 11 GC第7代改进与Span<T>-First内存策略(附3个内存泄漏检测脚本)
  • RK3308B开发板WiFi+蓝牙一体模组RTL8821CS驱动移植保姆级教程(含DTS配置与功能验证)
  • 【Java Loom响应式转型终极指南】:20年架构师亲测的5大避坑法则与性能跃迁实录
  • 京东茅台抢购脚本终极指南:三步实现全自动精准定时抢购
  • 家长参考|在家辅导孩子科学课,3款实用学习APP分享 - 品牌测评鉴赏家
  • 基于 RRT * 的多无人机编队动态路径规划与避障仿真研究(Matlab代码实现)
  • Windows Cleaner:终极免费解决方案,彻底告别C盘爆红!
  • 孩子科学知识点记不牢?5个归纳类学习平台推荐 - 品牌测评鉴赏家
  • 5分钟快速上手:xrdp开源远程桌面服务器完整配置指南
  • amdgpu 架构
  • 从老式工控机到树莓派:一文理清RS-232、RS-485和TTL电平的‘前世今生’与适用场景
  • 一张“网”如何拯救生命?浅谈医疗系统集成平台iPaaS
  • 苹果15年来首次换帅,新CEO能否带领苹果打赢AI硬件之战?
  • WinMerge文件对比合并工具
  • 别再乱卸载补丁了!Win10共享打印机0x00000709/11b错误,用这个官方修复补丁KB5007253一键搞定
  • PM4 / AQL 命令包
  • 2026年Q2高性价比尼龙布厂家排行:结构粘接胶膜、绝缘与屏蔽膜、自修复车衣、航空级尼龙布、航空阻燃标准尼龙布选择指南 - 优质品牌商家