OpencvSharp 算子学习教案之 - Cv2.Blur
OpencvSharp 算子学习教案之 - Cv2.Blur大家好Opencv在很多工程项目中都会用到而OpencvSharp则是以C#开发与实现的Opencv操作库对.NET开发人员友好但很多API的中文资料、应用场景及常见坑点等缺乏系统性归纳因此这系列博客将给大家带来Cv2及Mat对象全系列算子学习教案供大家参考学习。Cv2.Blur教案版本V1.0面向对象OpenCvSharp 初学者所属模块imgproc源码位置OpenCvSharp/Cv2/Cv2_imgproc.cs:285摘要Blur 是最直接的平均模糊接口它本质上等价于一个归一化的 BoxFilter。本文会用不同窗口大小对比结果并说明它为什么适合初学者先学“简单平均”。1. 函数名称带参数签名publicstaticvoidBlur(InputArraysrc,OutputArraydst,Sizeksize,Point?anchornull,BorderTypesborderTypeBorderTypes.Default)2. 函数用途Cv2.Blur的作用是对图像做最基础的平均模糊。它常用于去除少量随机噪声。降低图像细节强度。为后续边缘检测或阈值处理做预平滑。作为初学者理解“窗口平均”的第一步。如果你只是想做“简单模糊”Blur通常比BoxFilter更直接因为它已经帮你固定了归一化逻辑。3. 函数公式Blur可以理解成下面这个等价形式Blur(src,dst,ksize)≡BoxFilter(src,dst,src.type(),ksize,anchor,true,borderType) Blur(src, dst, ksize) \equiv BoxFilter(src, dst, src.type(), ksize, anchor, true, borderType)Blur(src,dst,ksize)≡BoxFilter(src,dst,src.type(),ksize,anchor,true,borderType)也就是说它本质上就是一个归一化的方框滤波只是把最常用的参数组合封装起来了。平均结果可以写成dst(x,y)1N∑i1Nsrci dst(x, y) \frac{1}{N} \sum_{i1}^{N} src_idst(x,y)N1i1∑Nsrci其中Nksize.width×ksize.heightN \text{ksize.width} \times \text{ksize.height}Nksize.width×ksize.height。4. 函数原理说明Blur的工作流程很简单以ksize为窗口尺寸。在每个像素周围取一个矩形邻域。把邻域里的像素做平均。用borderType处理靠近边缘的位置。它和BoxFilter的区别主要在于Blur默认就是归一化平均。BoxFilter需要你自己决定是否归一化、输出深度等更细的参数。这也是为什么很多教程会先讲Blur再讲BoxFilter。5. 参数含义解析参数名类型必填含义srcInputArray是输入图像dstOutputArray是输出图像ksizeSize是平均窗口大小anchorPoint?否核的锚点默认中心borderTypeBorderTypes否边界外推方式补充说明Blur的输出类型与源图一致。窗口越大平滑越明显。BORDER_WRAP不支持。它更像“教学版 BoxFilter”而不是一个全新算法。6. 应用场景列表场景名场景说明典型用途场景A基础平滑用平均值抑制轻微噪声预处理场景B多窗口对比观察 3x3、5x5、7x7 差异教学演示场景C边缘影响比较不同 borderType边界理解场景D与 BoxFilter 对照认识两者的关系概念入门7. 函数使用示例下面的 Console 程序演示Cv2.Blur。示例会对同一张图做 3x3、5x5、7x7 的平均模糊并把结果和BoxFilter做一个等价性对比。usingSystem;usingSystem.Text;usingOpenCvSharp;internalstaticclassProgram{privatestaticvoidMain(){// 让中文输出正确显示。Console.OutputEncodingEncoding.UTF8;RunDemo();}/// summary/// 演示 Blur 的基础平均模糊效果。/// /summaryprivatestaticvoidRunDemo(){usingvarsourceCreateDemoImage();usingvarnoisyAddGaussianNoise(source,14.0,2027);usingvarblur3newMat();usingvarblur5newMat();usingvarblur7newMat();usingvarboxEquivalentnewMat();// Blur 内部就是归一化方框滤波所以结果会和 BoxFilter 的默认平均模式非常接近。Cv2.Blur(noisy,blur3,newSize(3,3),null,BorderTypes.Default);Cv2.Blur(noisy,blur5,newSize(5,5),null,BorderTypes.Default);Cv2.Blur(noisy,blur7,newSize(7,7),null,BorderTypes.Replicate);Cv2.BoxFilter(noisy,boxEquivalent,MatType.CV_32F,newSize(5,5),null,true,BorderTypes.Default);Console.WriteLine(场景ABlur(InputArray src, OutputArray dst, Size ksize, Point? anchor null, BorderTypes borderType BorderTypes.Default));Console.WriteLine(Blur 是最基础的平均模糊接口它本质上等价于归一化 BoxFilter。\n);Console.WriteLine($带噪声图像{DescribeMat(noisy)});Console.WriteLine();PrintCase(3x3 Blur,noisy,blur3,小窗口会保留更多细节只做轻微平滑。,1.0,blur-3x3.png);PrintCase(5x5 Blur,noisy,blur5,中等窗口是初学者最常看的平均模糊效果。,1.0,blur-5x5.png);PrintCase(7x7 Blur,noisy,blur7,窗口更大后边缘和纹理会被平均得更明显。,1.0,blur-7x7.png);Console.WriteLine(和 BoxFilter 的对比);Console.WriteLine($Blur(5x5) 与 BoxFilter(5x5, normalizetrue) 的差值{DescribeDifferenceStatistics(blur5,boxEquivalent)});Console.WriteLine();Console.WriteLine(教学结论如果你的目标只是平均模糊Blur 就是最直接的入口如果你要控制更细的输出行为再换成 BoxFilter。\n);}/// summary/// 创建一张用于教学的源图。/// /summaryprivatestaticMatCreateDemoImage(){varcanvasnewMat(400,400,MatType.CV_8UC3,newScalar(246,243,238));Cv2.Circle(canvas,newPoint(90,86),54,newScalar(90,180,255),-1,LineTypes.AntiAlias);Cv2.Circle(canvas,newPoint(292,104),48,newScalar(135,220,110),-1,LineTypes.AntiAlias);Cv2.Rectangle(canvas,newRect(52,216,118,98),newScalar(60,160,235),3,LineTypes.AntiAlias);Cv2.PutText(canvas,Blur,newPoint(54,368),HersheyFonts.HersheySimplex,0.95,newScalar(45,38,34),2,LineTypes.AntiAlias);returncanvas;}/// summary/// 给图像叠加高斯噪声。/// /summaryprivatestaticMatAddGaussianNoise(Matsource,doublesigma,intseed){varrngnewRandom(seed);varresultsource.Clone();// 这里直接对每个像素做扰动方便在教学里观察模糊效果。for(varrow0;rowresult.Rows;row){for(varcol0;colresult.Cols;col){varpixelsource.AtVec3b(row,col);result.AtVec3b(row,col)newVec3b(ClampToByte(pixel.Item0NextGaussian(rng)*sigma),ClampToByte(pixel.Item1NextGaussian(rng)*sigma),ClampToByte(pixel.Item2NextGaussian(rng)*sigma));}}returnresult;}/// summary/// 打印一个对比场景。/// /summaryprivatestaticvoidPrintCase(stringlabel,Matsource,Matresult,stringcomment,doublealpha,stringfileName){Console.WriteLine($[{label}]);Console.WriteLine(comment);Console.WriteLine($结果{DescribeMat(result)});Console.WriteLine($与源图的灰度差值统计{DescribeDifferenceStatistics(source,result)});Console.WriteLine();SavePreview(fileName,result,alpha);}/// summary/// 保存可视化预览。/// /summaryprivatestaticvoidSavePreview(stringfileName,Matimage,doublealpha){usingvarpreviewnewMat();Cv2.ConvertScaleAbs(image,preview,alpha);Cv2.ImWrite(fileName,preview);}/// summary/// 描述 Mat 的核心信息。/// /summaryprivatestaticstringDescribeMat(Matmat){return$Size{mat.Width}x{mat.Height}, Channels{mat.Channels()}, Type{mat.Type()}, Depth{mat.Depth()};}/// summary/// 计算灰度图的基本统计信息。/// /summaryprivatestaticstringDescribeDifferenceStatistics(Matsource,Matresult){usingvardiffnewMat();Cv2.Absdiff(source,result,diff);usingvargraynewMat();Cv2.CvtColor(diff,gray,ColorConversionCodes.BGR2GRAY);Cv2.MeanStdDev(gray,outvarmean,outvarstddev);Cv2.MinMaxLoc(gray,outdoubleminVal,outdoublemaxVal);return$灰度均值{mean.Val0:F2}, 灰度标准差{stddev.Val0:F2}, 最小值{minVal:F0}, 最大值{maxVal:F0};}/// summary/// 生成高斯噪声。/// /summaryprivatestaticdoubleNextGaussian(Randomrng){varu11.0-rng.NextDouble();varu21.0-rng.NextDouble();returnMath.Sqrt(-2.0*Math.Log(u1))*Math.Cos(2.0*Math.PI*u2);}/// summary/// 把浮点值夹紧到 8 位像素范围。/// /summaryprivatestaticbyteClampToByte(doublevalue){varclampedMath.Clamp((int)Math.Round(value),byte.MinValue,byte.MaxValue);return(byte)clamped;}}8. 注意事项Blur输出类型和源图一致。Blur只负责“平均”并不提供像BoxFilter那样更多的输出控制。窗口越大图像越平滑但细节也越少。BORDER_WRAP不支持。9. 调优建议初学者先从 3x3 或 5x5 开始看。如果想做更明显的平滑再逐步增大窗口。遇到边缘问题时优先比较BorderTypes.Default和BorderTypes.Replicate。如果你需要控制输出深度就把视野切换到BoxFilter。10. 运行说明如果你在控制台工程里运行本文示例直接把代码放进Program.cs即可。如果你在本仓库里学习请打开 WPF 控件Cv2.Blur点击“运行场景A”后查看右侧文本框和预览图。WPF 示例会把 3x3、5x5、7x7 的结果放在一起对比。11. 常见错误排查把Blur和GaussianBlur当成同一种滤波器。以为它可以控制ddepth但其实这个接口没有暴露这个参数。窗口太大导致图像看起来“糊成一片”。没有注意到Blur本质上就是归一化方框滤波。