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

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

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

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

Cv2.MorphologyEx

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

摘要:Cv2.MorphologyEx 是复合形态学操作的统一入口,Open、Close、Gradient 等常见操作都从这里进入。本文通过同一张二值图对比三种运算结果,帮助初学者理解这些复合操作其实都是膨胀和腐蚀的组合。

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

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

2. 函数用途

Cv2.MorphologyEx的作用,是执行更高级的形态学组合操作。

它常见的用途有:

  1. 开运算去噪。
  2. 闭运算补洞。
  3. 形态学梯度提取轮廓。
  4. 把常见形态学处理统一到一个入口里。

和单独调用DilateErode相比,MorphologyEx更适合表达“组合运算”的意图。

3. 函数公式

几种最常见的复合形态学操作可以写成:

Open(A)=(A⊖B)⊕B Open(A) = (A \ominus B) \oplus BOpen(A)=(AB)B

Close(A)=(A⊕B)⊖B Close(A) = (A \oplus B) \ominus BClose(A)=(AB)B

Gradient(A)=(A⊕B)−(A⊖B) Gradient(A) = (A \oplus B) - (A \ominus B)Gradient(A)=(AB)(AB)

其中AAA是输入图像,BBB是结构元素。

4. 函数原理说明

MorphologyEx 本质上不是一种全新的基础操作,而是把常见的膨胀和腐蚀组合起来。

  1. Open 通常先腐蚀,再膨胀,适合去除小白噪点。
  2. Close 通常先膨胀,再腐蚀,适合填补小黑洞。
  3. Gradient 会把边界提出来,结果通常更像轮廓带。
  4. 结构元素和迭代次数依旧会影响结果强弱。

当你理解了DilateErodeMorphologyEx就会变得很容易。

5. 参数含义解析

参数名类型必填含义
srcInputArray输入图像,通常是二值图或灰度图
dstOutputArray输出图像
opMorphTypes复合形态学操作类型
elementInputArray?结构元素
anchorPoint?锚点位置,默认在中心
iterationsint迭代次数
borderTypeBorderTypes边界外推方式
borderValueScalar?常量边界模式下的边界值

补充说明:

  1. MorphTypes.OpenMorphTypes.CloseMorphTypes.Gradient是最常见的教学入口。
  2. 如果你已经知道自己只是要腐蚀或膨胀,也可以直接用专门函数。
  3. MorphologyEx的优势是把“意图”说得更清楚。
  4. 教学时适合用同一张图对比不同op的效果。

6. 应用场景列表

场景名场景说明典型用途
场景A:Open 去噪去掉孤立白点教学入门
场景B:Close 补洞填补小黑洞图像修复
场景C:Gradient 看边界提取轮廓带轮廓教学
场景D:统一入口一次讲完多种形态学理论学习

7. 函数使用示例

下面的 Console 程序演示Cv2.MorphologyEx。示例会对同一张二值图分别执行OpenCloseGradient三种操作,帮助你建立整体直觉。

usingSystem;usingSystem.Globalization;usingSystem.Linq;usingSystem.Text;usingOpenCvSharp;internalstaticclassProgram{/// <summary>/// 程序入口。/// </summary>privatestaticvoidMain(){// 先让控制台可以正确显示中文说明。Console.OutputEncoding=Encoding.UTF8;usingvarsource=CreateMorphologySourceImage();usingvarkernel=Cv2.GetStructuringElement(MorphShapes.Ellipse,newSize(5,5));usingvaropened=newMat();usingvarclosed=newMat();usingvargradient=newMat();// Open 先腐蚀再膨胀,通常用于去噪。Cv2.MorphologyEx(source,opened,MorphTypes.Open,kernel,iterations:1,borderType:BorderTypes.Constant,borderValue:Cv2.MorphologyDefaultBorderValue());// Close 先膨胀再腐蚀,通常用于补洞。Cv2.MorphologyEx(source,closed,MorphTypes.Close,kernel,iterations:1,borderType:BorderTypes.Constant,borderValue:Cv2.MorphologyDefaultBorderValue());// Gradient 会突出边界。Cv2.MorphologyEx(source,gradient,MorphTypes.Gradient,kernel,iterations:1,borderType:BorderTypes.Constant,borderValue:Cv2.MorphologyDefaultBorderValue());Cv2.ImWrite("morphologyex-source.png",source);Cv2.ImWrite("morphologyex-open.png",opened);Cv2.ImWrite("morphologyex-close.png",closed);Cv2.ImWrite("morphologyex-gradient.png",gradient);Cv2.ImWrite("morphologyex-kernel.png",CreateKernelPreview(kernel));Console.WriteLine("场景A:MorphologyEx(InputArray src, OutputArray dst, MorphTypes op, InputArray? element, Point? anchor = null, int iterations = 1, BorderTypes borderType = BorderTypes.Constant, Scalar? borderValue = null)");Console.WriteLine("MorphologyEx 是复合形态学的统一入口。\n");Console.WriteLine($"源图:{DescribeBinaryMat(source)}");Console.WriteLine($"5x5 Ellipse 核:{DescribeKernel(kernel)}");Console.WriteLine($"结构元素矩阵:\n{FormatKernelMatrix(kernel)}");Console.WriteLine();AppendCaseReport("结果A:Open",source,opened,"开运算通常用来去除孤立白噪点,并保留较大的主体结构。");Console.WriteLine();AppendCaseReport("结果B:Close",source,closed,"闭运算通常用来填补小黑洞,并让断裂的前景更容易连起来。");Console.WriteLine();AppendCaseReport("结果C:Gradient",source,gradient,"形态学梯度会把边界提出来,结果通常更像一条轮廓带。");}/// <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>privatestaticstringFormatKernelMatrix(Matkernel){varvalues=ReadMatAsDoubleMatrix(kernel);varsb=newStringBuilder();for(varrow=0;row<values.GetLength(0);row++){sb.Append("[");for(varcol=0;col<values.GetLength(1);col++){sb.Append(values[row,col].ToString("F0",CultureInfo.InvariantCulture));if(col<values.GetLength(1)-1){sb.Append(", ");}}sb.AppendLine("]");}returnsb.ToString();}/// <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. MorphologyEx不是一种全新的基础运算,而是膨胀和腐蚀的组合。
  2. OpenCloseGradient分别对应不同的教学目标。
  3. 结构元素和迭代次数依旧会影响最终结果。
  4. 如果你理解了DilateErode,再看MorphologyEx会轻松很多。

9. 调优建议

  1. 教学时先固定结构元素,只观察op的变化。
  2. 如果你在做去噪,先看Open
  3. 如果你在做补洞,先看Close
  4. 如果你想看轮廓,先看Gradient

10. 运行说明

  1. 如果你在控制台工程里运行本文示例,直接把代码放到Program.cs即可。
  2. 如果你在本仓库里学习,请直接打开 WPF 控件Cv2.MorphologyEx,点击“运行场景A”。
  3. WPF 示例会同时展示 Open、Close 和 Gradient 的结果,便于你整体理解复合形态学。

11. 常见错误排查

  1. MorphologyEx当成独立于膨胀和腐蚀的新东西。
  2. 不区分 Open、Close 和 Gradient 的用途。
  3. 忘记结构元素对结果的影响。
  4. 只看结果图,不看操作背后的组合逻辑。
http://www.jsqmd.com/news/678930/

相关文章:

  • nli-MiniLM2-L6-H768参数详解:Cross-Encoder vs Bi-Encoder在NLI任务中的选型建议
  • 高并发系统重构迫在眉睫?Java 25虚拟线程上线72小时:GC停顿降86%,连接池告警归零,》
  • 2026年厕所隔断服务机构top5排行:卫生间隔断板材/厕所隔断/洗手间隔断/卫生间隔断/选择指南 - 优质品牌商家
  • RWKV7-1.5B-g1a部署案例:CSDN平台外网服务(7860端口)完整调试与日志排障指南
  • Prompt工程进阶2026:从基础提示到企业级提示系统设计
  • C语言新手必看:用代码实现人民币大写转换,搞定这道经典编程题
  • 别再死记硬背模型了!用SUMO的Krauss跟驰模型,手把手教你复现一次真实堵车
  • FPGA间高速数据搬运工:SRIO NWRITE协议在图像处理系统中的实战优化
  • GNU Radio之「模块」—— QT GUI Time Sink
  • ESP32-C3 SPI避坑指南:从模式选择到时钟配置,新手必看的5个常见错误
  • 推荐几款内存占用小的监控Agent:2026年企业级智能体与轻量化监控选型全景盘点
  • 浙江大学毕业论文LaTeX模板:告别格式烦恼,专注学术创作的终极解决方案
  • Windows下用Python写后台服务或开机自启?那你必须搞懂Pythonw.exe
  • 保姆级教程:为你的ROS2机器人打造稳定IMU数据流(基于幻尔CMP10A传感器与Humble版本)
  • Phi-3.5-mini-instruct实际应用:法律文书初稿辅助撰写(通用层)
  • 零基础学网络安全:Kali Linux渗透测试系统入门指南(建议收藏,附常用命令详解)
  • OpenClaw 一键安装包|一键部署,告别复杂环境配置
  • 手把手教你用Java代码实现EMQX免费版到Kafka的数据桥接(附完整源码)
  • AIGlasses_for_navigation效果对比:不同YOLO版本(v5/v8/v10)在盲道任务表现
  • 用MobileNet搞定垃圾分类:基于TensorFlow2.3,从数据清洗到GUI部署的完整实战
  • AngularJS Select(选择框)
  • Tang Nano 9k FPGA扩展板设计与应用指南
  • 服务器挂了才发现,怎么做到事前预警?——2026企业级智能体监控与AIOps全景选型指南
  • 保姆级教程:用WoLF PSORT、YLoc和DeepLoc 2.0搞定蛋白质亚细胞定位预测(附结果解读)
  • 169.254.x.x:当你的HP打印机决定‘单飞’时,它在想什么?(聊聊APIPA协议与局域网那些事儿)
  • 别再为PyTorch数据不平衡发愁了!手把手教你用WeightedRandomSampler搞定猫狗分类
  • 关于苹果官宣库克卸任CEO 属于他的时代结束了
  • 用STC8H给DS3231模块(ZS-042)做个时间管家:I2C读写、闹钟设置与电池改造全攻略
  • FPGA在电池管理系统中的优势与应用
  • Parsec VDD终极指南:如何在Windows上创建16个虚拟显示器实现游戏直播与远程办公