OpencvSharp 算子学习教案之 - Cv2.Scharr
OpencvSharp 算子学习教案之 - Cv2.Scharr大家好Opencv在很多工程项目中都会用到而OpencvSharp则是以C#开发与实现的Opencv操作库对.NET开发人员友好但很多API的中文资料、应用场景及常见坑点等缺乏系统性归纳因此这系列博客将给大家带来Cv2及Mat对象全系列算子学习教案供大家参考学习。Cv2.Scharr教案版本V1.0面向对象OpenCvSharp 初学者所属模块imgproc源码位置OpenCvSharp/Cv2/Cv2_imgproc.cs:458摘要Scharr 可以看成是 Sobel 在 3x3 情况下的高精度替代方案。本文会先说明它和 Sobel 的等价关系再用一个简单几何图形示例对比 Sobel X / Y 和 Scharr X / Y 的差别。1. 函数名称带参数签名publicstaticvoidScharr(InputArraysrc,OutputArraydst,MatTypeddepth,intdx,intdy,doublescale1,doubledelta0,BorderTypesborderTypeBorderTypes.Default)2. 函数用途Cv2.Scharr用来计算图像的一阶导数但它只在 3x3 情况下有特殊意义。它常用于比较普通 3x3 Sobel 和 Scharr 的差别。讲解更高精度的一阶导数估计。做边缘方向分析。作为 Sobel 的教学补充。如果你希望初学者理解“同样是求导核系数不同会带来什么变化”Scharr 很适合做对照。3. 函数公式官方文档说明Scharr(src,dst,ddepth,dx,dy,scale,delta,borderType) Scharr(src, dst, ddepth, dx, dy, scale, delta, borderType)Scharr(src,dst,ddepth,dx,dy,scale,delta,borderType)等价于Sobel(src,dst,ddepth,dx,dy,FILTER_SCHARR,scale,delta,borderType) Sobel(src, dst, ddepth, dx, dy, FILTER\_SCHARR, scale, delta, borderType)Sobel(src,dst,ddepth,dx,dy,FILTER_SCHARR,scale,delta,borderType)在 x 方向上Scharr 的 3x3 核可以写成[−303−10010−303] \begin{bmatrix} -3 0 3\\ -10 0 10\\ -3 0 3 \end{bmatrix}−3−10−30003103y 方向则是它的转置。4. 函数原理说明Scharr 主要解决的是“小核导数近似精度”的问题。dx和dy仍然表示导数阶数。它没有ksize参数因为它固定就是 3x3 特例。ddepth的选择和 Sobel 一样要避免负值丢失。borderType仍然决定边界如何补值。教学上可以把它理解成“更讲究系数的 Sobel 3x3 版本”。5. 参数含义解析参数名类型必填含义srcInputArray是输入图像dstOutputArray是输出图像ddepthMatType是输出深度dxint是x 方向导数阶数dyint是y 方向导数阶数scaledouble否结果缩放系数deltadouble否结果偏移量borderTypeBorderTypes否边界外推方式补充说明Scharr只适合 3x3 这一种导数核场景。常见做法仍然是把结果存到CV_16S。如果只是想做一般教学先理解 Sobel 再看 Scharr 会更顺。BORDER_WRAP不支持。6. 应用场景列表场景名场景说明典型用途场景AX 方向对照观察 Scharr X 和 Sobel X边缘比较场景BY 方向对照观察 Scharr Y 和 Sobel Y边缘比较场景C精度讨论解释 Scharr 的系数意义理论教学场景D特例理解说明 FILTER_SCHARRAPI 教学7. 函数使用示例下面的 Console 程序演示Cv2.Scharr。示例会同时计算 Sobel 和 Scharr 的 x / y 导数再打印它们之间的差异帮助初学者理解“Scharr 不是新框架而是 Sobel 的特例”。usingSystem;usingSystem.Text;usingOpenCvSharp;internalstaticclassProgram{privatestaticvoidMain(){// 让控制台输出中文说明。Console.OutputEncodingEncoding.UTF8;RunDemo();}/// summary/// 运行 Scharr 教学示例。/// /summaryprivatestaticvoidRunDemo(){usingvarsourceCreateDemoImage();usingvargraySourcenewMat();// 先转成灰度图这样导数比较更直接。Cv2.CvtColor(source,graySource,ColorConversionCodes.BGR2GRAY);usingvarsobelXnewMat();usingvarsobelYnewMat();usingvarscharrXnewMat();usingvarscharrYnewMat();// 普通 3x3 Sobel 和 Scharr 做对照最容易看出它们的区别。Cv2.Sobel(graySource,sobelX,MatType.CV_16S,1,0,3,1.0,0.0,BorderTypes.Default);Cv2.Sobel(graySource,sobelY,MatType.CV_16S,0,1,3,1.0,0.0,BorderTypes.Default);Cv2.Scharr(graySource,scharrX,MatType.CV_16S,1,0,1.0,0.0,BorderTypes.Default);Cv2.Scharr(graySource,scharrY,MatType.CV_16S,0,1,1.0,0.0,BorderTypes.Default);usingvarmagnitudenewMat();usingvarscharrXFloatnewMat();usingvarscharrYFloatnewMat();// 计算 Scharr 的梯度幅值方便从“方向”和“强度”两个角度看边缘。scharrX.ConvertTo(scharrXFloat,MatType.CV_32F);scharrY.ConvertTo(scharrYFloat,MatType.CV_32F);Cv2.Magnitude(scharrXFloat,scharrYFloat,magnitude);Console.WriteLine(场景AScharr(InputArray src, OutputArray dst, MatType ddepth, int dx, int dy, double scale 1, double delta 0, BorderTypes borderType BorderTypes.Default));Console.WriteLine(Scharr 本质上就是 Sobel 的 FILTER_SCHARR 特例最适合拿来做 3x3 一阶导数的精度对照。\n);Console.WriteLine($源图{DescribeMat(source)});Console.WriteLine($灰度图{DescribeMat(graySource)});Console.WriteLine($Sobel X{DescribeMat(sobelX)});Console.WriteLine($Scharr X{DescribeMat(scharrX)});Console.WriteLine($Sobel Y{DescribeMat(sobelY)});Console.WriteLine($Scharr Y{DescribeMat(scharrY)});Console.WriteLine($Scharr X 与 Sobel X 的差异{DescribeDifferenceStatistics(scharrX,sobelX)});Console.WriteLine($Scharr Y 与 Sobel Y 的差异{DescribeDifferenceStatistics(scharrY,sobelY)});Console.WriteLine($Scharr 梯度幅值{DescribeMat(magnitude)});Console.WriteLine();SavePreview(scharr-src.png,graySource);SavePreview(scharr-sobelx.png,sobelX);SavePreview(scharr-scharrx.png,scharrX);SavePreview(scharr-scharry.png,scharrY);SavePreview(scharr-magnitude.png,magnitude);Console.WriteLine(教学结论Scharr 适合在 3x3 的条件下替代 Sobel 做更细致的一阶导数教学。\n);}/// summary/// 创建教学测试图。/// /summaryprivatestaticMatCreateDemoImage(){varcanvasnewMat(400,400,MatType.CV_8UC3,newScalar(245,242,236));Cv2.Rectangle(canvas,newRect(48,52,122,120),newScalar(80,175,250),-1,LineTypes.AntiAlias);Cv2.Circle(canvas,newPoint(288,104),50,newScalar(128,214,112),-1,LineTypes.AntiAlias);Cv2.Line(canvas,newPoint(52,260),newPoint(350,330),newScalar(60,70,80),5,LineTypes.AntiAlias);Cv2.PutText(canvas,Scharr,newPoint(48,370),HersheyFonts.HersheySimplex,0.9,newScalar(42,40,36),2,LineTypes.AntiAlias);returncanvas;}/// summary/// 保存一个适合观察的单通道预览图。/// /summaryprivatestaticvoidSavePreview(stringfileName,Matimage){usingvarpreviewnewMat();Cv2.Normalize(image,preview,0,255,NormTypes.MinMax,(int)MatType.CV_8UC1);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(Matleft,Matright){usingvardiffnewMat();Cv2.Absdiff(left,right,diff);Cv2.MeanStdDev(diff,outvarmean,outvarstddev);Cv2.MinMaxLoc(diff,outdoubleminVal,outdoublemaxVal);return$均值{mean.Val0:F2}, 标准差{stddev.Val0:F2}, 最小值{minVal:F0}, 最大值{maxVal:F0};}}8. 注意事项Scharr只有在 3x3 条件下才是特殊实现。它本质上等价于Sobel(..., FILTER_SCHARR, ...)。输出通常也应该放在 16 位有符号深度里。BORDER_WRAP不支持。9. 调优建议先和Cv2.Sobel的 3x3 结果做对照看。如果想讲更平滑的响应可以同时展示 Sobel 作为参照。如果想讲边缘强度可以再接Cv2.Magnitude。如果想讲方向可以分别看 X 和 Y 方向结果。10. 运行说明如果你在控制台工程里运行本文示例直接把代码放到Program.cs即可。如果你在本仓库里学习请打开 WPF 控件Cv2.Scharr点击“运行场景A”后查看右侧文本框和预览图。WPF 示例会同时展示 Sobel 和 Scharr 的对照结果便于理解差异。11. 常见错误排查把 Scharr 当成一个和 Sobel 完全无关的新函数。以为它能自由调ksize其实它就是 3x3 特例。用CV_8U输出保存导数导致负值丢失。只看 X 方向不看 Y 方向导致边缘理解不完整。