OpencvSharp 算子学习教案之 - Cv2.MorphologyEx
2026/4/21 23:05:24 网站建设 项目流程

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. 只看结果图,不看操作背后的组合逻辑。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询