OpencvSharp 算子学习教案之 - Cv2.BoxFilter
OpencvSharp 算子学习教案之 - Cv2.BoxFilter大家好Opencv在很多工程项目中都会用到而OpencvSharp则是以C#开发与实现的Opencv操作库对.NET开发人员友好但很多API的中文资料、应用场景及常见坑点等缺乏系统性归纳因此这系列博客将给大家带来Cv2及Mat对象全系列算子学习教案供大家参考学习。Cv2.BoxFilter教案版本V1.0面向对象OpenCvSharp 初学者所属模块imgproc源码位置OpenCvSharp/Cv2/Cv2_imgproc.cs:219摘要BoxFilter 会在局部窗口中做求和或平均normalizetrue 时更像普通模糊normalizefalse 时更适合局部统计。本文用 5x5、9x9 和不同 borderType 做对比帮助初学者理解归一化、输出深度和边界处理。1. 函数名称带参数签名publicstaticvoidBoxFilter(InputArraysrc,OutputArraydst,MatTypeddepth,Sizeksize,Point?anchornull,boolnormalizetrue,BorderTypesborderTypeBorderTypes.Default)2. 函数用途Cv2.BoxFilter的作用是用方框核对图像做线性平滑。这个函数最常见的用途有进行最基础的平均平滑。抑制轻微的随机噪声。计算局部求和、局部均值等统计量。作为一些高阶算法的底层积木例如局部方差、协方差和积分特征。当normalizetrue时它更像普通模糊当normalizefalse时它更像“窗口求和”。3. 函数公式方框核可以写成Kα[11⋯111⋯1⋮⋮⋱⋮11⋯1] K \alpha \begin{bmatrix} 1 1 \cdots 1 \\ 1 1 \cdots 1 \\ \vdots \vdots \ddots \vdots \\ 1 1 \cdots 1 \end{bmatrix}Kα11⋮111⋮1⋯⋯⋱⋯11⋮1其中α{1ksize.width×ksize.height,normalizetrue1,normalizefalse \alpha \begin{cases} \frac{1}{\text{ksize.width} \times \text{ksize.height}}, normalize true \\ 1, normalize false \end{cases}α{ksize.width×ksize.height1,1,normalizetruenormalizefalse滤波结果可以理解成局部窗口内的加权和dst(x,y)∑u,vsrc(u,v) K(x−u,y−v) dst(x, y) \sum_{u,v} src(u, v)\,K(x-u, y-v)dst(x,y)u,v∑src(u,v)K(x−u,y−v)4. 函数原理说明这个函数会在每个像素周围取一个ksize大小的矩形窗口然后把窗口里的像素做求和或平均。ksize决定窗口范围。anchor决定窗口参考点默认在中心。normalize决定结果是“平均值”还是“原始求和”。ddepth决定输出图像的深度和源图深度可以不同。borderType决定靠近边缘时图像外侧像素怎么补。官方文档还提示如果你需要对可变窗口做累积求和通常会考虑integral如果你只想做一个简单、规则的方框模糊BoxFilter就很直接。5. 参数含义解析参数名类型必填含义srcInputArray是输入图像dstOutputArray是输出图像ddepthMatType是输出深度决定结果如何保存ksizeSize是方框核大小anchorPoint?否核的锚点默认中心normalizebool否是否按面积归一化默认 trueborderTypeBorderTypes否边界外推方式补充说明normalizetrue更适合普通平滑。normalizefalse更适合做局部统计。ddepth如果选得太低可能会丢失精度。BORDER_WRAP不支持。6. 应用场景列表场景名场景说明典型用途场景A基础平滑用均值压低轻微噪声图像预处理场景B局部求和观察窗口内像素总量特征统计场景C局部均值观察窗口平均亮度平滑处理场景D边界对比研究不同 borderType教学演示7. 函数使用示例下面的 Console 程序演示Cv2.BoxFilter。示例会先给图像加入一点高斯噪声再分别做归一化方框滤波、非归一化方框滤波和更大窗口的对比。usingSystem;usingSystem.Text;usingOpenCvSharp;internalstaticclassProgram{privatestaticvoidMain(){// 控制台输出切换到 UTF-8避免中文说明乱码。Console.OutputEncodingEncoding.UTF8;// 运行完整演示观察 normalizetrue 和 normalizefalse 的差异。RunDemo();}/// summary/// 演示 BoxFilter 的输出特点。/// /summaryprivatestaticvoidRunDemo(){usingvarsourceCreateDemoImage();usingvarnoisyAddGaussianNoise(source,18.0,2026);usingvarnormalizednewMat();usingvarrawSumnewMat();usingvarlargeWindownewMat();// normalizetrue 时更像普通模糊normalizefalse 时更像窗口求和。Cv2.BoxFilter(noisy,normalized,MatType.CV_32F,newSize(5,5),null,true,BorderTypes.Default);Cv2.BoxFilter(noisy,rawSum,MatType.CV_32F,newSize(5,5),null,false,BorderTypes.Default);Cv2.BoxFilter(noisy,largeWindow,MatType.CV_32F,newSize(9,9),null,true,BorderTypes.Replicate);Console.WriteLine(场景ABoxFilter(InputArray src, OutputArray dst, MatType ddepth, Size ksize, Point? anchor null, bool normalize true, BorderTypes borderType BorderTypes.Default));Console.WriteLine(BoxFilter 可以做局部平均也可以做局部求和。它是很多统计型图像算法的基础组件。\n);Console.WriteLine($带噪声图像{DescribeMat(noisy)});Console.WriteLine();PrintCase(normalizetrue,noisy,normalized,5x5 平均方框滤波适合做普通平滑。);PrintCase(normalizefalse,noisy,rawSum,5x5 原始求和结果会明显变大适合做局部统计。);PrintCase(9x9 Replicate,noisy,largeWindow,更大的窗口会让平滑更强Replicate 边界会把边缘像素复制出去。);// 把结果保存成图片方便读者直接查看。SavePreview(boxfilter-normalized.png,normalized,1.0);SavePreview(boxfilter-rawsum.png,rawSum,0.04);SavePreview(boxfilter-large.png,largeWindow,1.0);Console.WriteLine(教学结论BoxFilter 的关键区别在 normalize、ddepth 和 ksize。normalizetrue 更像普通模糊normalizefalse 更适合统计。\n);}/// summary/// 创建一张用于教学的源图。/// /summaryprivatestaticMatCreateDemoImage(){// 用简单几何图形拼出一张彩色示意图便于观察模糊前后的差别。varcanvasnewMat(400,400,MatType.CV_8UC3,newScalar(246,243,238));Cv2.Circle(canvas,newPoint(90,90),58,newScalar(80,170,255),-1,LineTypes.AntiAlias);Cv2.Circle(canvas,newPoint(300,100),46,newScalar(120,225,110),-1,LineTypes.AntiAlias);Cv2.Rectangle(canvas,newRect(50,220,120,96),newScalar(50,180,235),3,LineTypes.AntiAlias);Cv2.PutText(canvas,BoxFilter,newPoint(48,366),HersheyFonts.HersheySimplex,0.9,newScalar(55,40,35),2,LineTypes.AntiAlias);returncanvas;}/// summary/// 给图像叠加高斯噪声。/// /summaryprivatestaticMatAddGaussianNoise(Matsource,doublesigma,intseed){varrngnewRandom(seed);varresultsource.Clone();// 这里只改少量随机像素便于观察 BoxFilter 对轻微噪声的平滑效果。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){Console.WriteLine($[{label}]);Console.WriteLine(comment);Console.WriteLine($结果{DescribeMat(result)});Console.WriteLine($与源图的灰度差值统计{DescribeDifferenceStatistics(source,result)});Console.WriteLine();}/// 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){// Box-Muller 变换可以把均匀分布随机数转换成高斯分布随机数。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. 注意事项BORDER_WRAP不支持。normalizefalse时结果会明显变大不要和普通模糊结果直接放在同一尺度下比较。ddepth太低时可能损失精度尤其是做统计型计算时。多通道图像会按通道独立处理。9. 调优建议先从ksize5x5开始看效果。如果只想要普通平滑优先使用normalizetrue。如果你要做局部统计考虑让输出使用更高精度的深度。遇到边界问题时先换一个borderType对比。10. 运行说明如果你在控制台工程里运行本文示例直接把代码放进Program.cs即可。如果你在本仓库里学习请打开 WPF 控件Cv2.BoxFilter点击“运行场景A”后查看右侧文本框和预览图。WPF 示例会把 normalizetrue、normalizefalse 和更大窗口的结果放在一起对比。11. 常见错误排查把normalizefalse当成普通模糊来理解。忘记ddepth会影响结果的存储深度。把窗口大小设得太大导致细节被平均得太厉害。忽略了边界模式对边缘区域的影响。