邻域滤波(卷积)

邻域算子值利用给定像素周围像素的值决定此像素的最终输出。如图左边图像与中间图像卷积禅城右边图像。目标图像中绿色的像素由原图像中蓝色标记的像素计算得到。

通用线性邻域滤波是一种常用的邻域算子,输入像素加权得到输出像素:

其中权重核   为“滤波系数”。上面的式子可以简记为:

【方框滤波】

最简单的线性滤波是移动平均或方框滤波,用 窗口中的像素值平均后输出,核函数为:
其实等价于图像与全部元素值为1的核函数进行卷积再进行尺度缩放。

代码

OpenCV中的 blur函数是进行标准方框滤波:
[cpp] view plaincopy
  1. void cv::blur( InputArray src, OutputArray dst,
  2. Size ksize, Point anchor, int borderType )
  3. {
  4. boxFilter( src, dst, -1, ksize, anchor, true, borderType );
  5. }

而boxFilter函数源码如下:

[cpp] view plaincopy
  1. cv::Ptr<cv::FilterEngine> cv::createBoxFilter( int srcType, int dstType, Size ksize,
  2. Point anchor, bool normalize, int borderType )
  3. {
  4. int sdepth = CV_MAT_DEPTH(srcType);
  5. int cn = CV_MAT_CN(srcType), sumType = CV_64F;
  6. if( sdepth <= CV_32S && (!normalize ||
  7. ksize.width*ksize.height <= (sdepth == CV_8U ? (1<<23) :
  8. sdepth == CV_16U ? (1 << 15) : (1 << 16))) )
  9. sumType = CV_32S;
  10. sumType = CV_MAKETYPE( sumType, cn );
  11. Ptr<BaseRowFilter> rowFilter = getRowSumFilter(srcType, sumType, ksize.width, anchor.x );
  12. Ptr<BaseColumnFilter> columnFilter = getColumnSumFilter(sumType,
  13. dstType, ksize.height, anchor.y, normalize ? 1./(ksize.width*ksize.height) : 1);
  14. return Ptr<FilterEngine>(new FilterEngine(Ptr<BaseFilter>(0), rowFilter, columnFilter,
  15. srcType, dstType, sumType, borderType ));
  16. }

这里 blur 和 boxFilter 的区别是,blur是标准化后的 boxFilter,即boxFilter的核函数:

其中,
[cpp] view plaincopy
  1. blur( src, dst, Size( 1, 1 ), Point(-1,-1));
  2. blur( src, dst, Size( 4, 4 ), Point(-1,-1));
  3. blur( src, dst, Size( 8, 8 ), Point(-1,-1));
  4. blur( src, dst, Size( 16, 16 ), Point(-1,-1));

实验结果

下图是对一幅图像分别用1*1,4*4,8*8,16*16标准方框滤波后的图像:
      

【高斯滤波】

高斯滤波器是一类根据高斯函数的形状来选择权值的线性平滑滤波器。它对去除服从正态分布的噪声很有效。
常用的零均值离散高斯滤波器函数:

2D图像中表示为:

代码

[cpp] view plaincopy
  1. /****************************************************************************************\
  2. Gaussian Blur
  3. \****************************************************************************************/
  4. cv::Mat cv::getGaussianKernel( int n, double sigma, int ktype )
  5. {
  6. const int SMALL_GAUSSIAN_SIZE = 7;
  7. static const float small_gaussian_tab[][SMALL_GAUSSIAN_SIZE] =
  8. {
  9. {1.f},
  10. {0.25f, 0.5f, 0.25f},
  11. {0.0625f, 0.25f, 0.375f, 0.25f, 0.0625f},
  12. {0.03125f, 0.109375f, 0.21875f, 0.28125f, 0.21875f, 0.109375f, 0.03125f}
  13. };
  14. const float* fixed_kernel = n % 2 == 1 && n <= SMALL_GAUSSIAN_SIZE && sigma <= 0 ?
  15. small_gaussian_tab[n>>1] : 0;
  16. CV_Assert( ktype == CV_32F || ktype == CV_64F );
  17. Mat kernel(n, 1, ktype);
  18. float* cf = (float*)kernel.data;
  19. double* cd = (double*)kernel.data;
  20. double sigmaX = sigma > 0 ? sigma : ((n-1)*0.5 - 1)*0.3 + 0.8;
  21. double scale2X = -0.5/(sigmaX*sigmaX);
  22. double sum = 0;
  23. int i;
  24. for( i = 0; i < n; i++ )
  25. {
  26. double x = i - (n-1)*0.5;
  27. double t = fixed_kernel ? (double)fixed_kernel[i] : std::exp(scale2X*x*x);
  28. if( ktype == CV_32F )
  29. {
  30. cf[i] = (float)t;
  31. sum += cf[i];
  32. }
  33. else
  34. {
  35. cd[i] = t;
  36. sum += cd[i];
  37. }
  38. }
  39. sum = 1./sum;
  40. for( i = 0; i < n; i++ )
  41. {
  42. if( ktype == CV_32F )
  43. cf[i] = (float)(cf[i]*sum);
  44. else
  45. cd[i] *= sum;
  46. }
  47. return kernel;
  48. }
  49. cv::Ptr<cv::FilterEngine> cv::createGaussianFilter( int type, Size ksize,
  50. double sigma1, double sigma2,
  51. int borderType )
  52. {
  53. int depth = CV_MAT_DEPTH(type);
  54. if( sigma2 <= 0 )
  55. sigma2 = sigma1;
  56. // automatic detection of kernel size from sigma
  57. if( ksize.width <= 0 && sigma1 > 0 )
  58. ksize.width = cvRound(sigma1*(depth == CV_8U ? 3 : 4)*2 + 1)|1;
  59. if( ksize.height <= 0 && sigma2 > 0 )
  60. ksize.height = cvRound(sigma2*(depth == CV_8U ? 3 : 4)*2 + 1)|1;
  61. CV_Assert( ksize.width > 0 && ksize.width % 2 == 1 &&
  62. ksize.height > 0 && ksize.height % 2 == 1 );
  63. sigma1 = std::max( sigma1, 0. );
  64. sigma2 = std::max( sigma2, 0. );
  65. Mat kx = getGaussianKernel( ksize.width, sigma1, std::max(depth, CV_32F) );
  66. Mat ky;
  67. if( ksize.height == ksize.width && std::abs(sigma1 - sigma2) < DBL_EPSILON )
  68. ky = kx;
  69. else
  70. ky = getGaussianKernel( ksize.height, sigma2, std::max(depth, CV_32F) );
  71. return createSeparableLinearFilter( type, type, kx, ky, Point(-1,-1), 0, borderType );
  72. }
  73. void cv::GaussianBlur( InputArray _src, OutputArray _dst, Size ksize,
  74. double sigma1, double sigma2,
  75. int borderType )
  76. {
  77. Mat src = _src.getMat();
  78. _dst.create( src.size(), src.type() );
  79. Mat dst = _dst.getMat();
  80. if( borderType != BORDER_CONSTANT )
  81. {
  82. if( src.rows == 1 )
  83. ksize.height = 1;
  84. if( src.cols == 1 )
  85. ksize.width = 1;
  86. }
  87. if( ksize.width == 1 && ksize.height == 1 )
  88. {
  89. src.copyTo(dst);
  90. return;
  91. }
  92. #ifdef HAVE_TEGRA_OPTIMIZATION
  93. if(sigma1 == 0 && sigma2 == 0 && tegra::gaussian(src, dst, ksize, borderType))
  94. return;
  95. #endif
  96. Ptr<FilterEngine> f = createGaussianFilter( src.type(), ksize, sigma1, sigma2, borderType );
  97. f->apply( src, dst );
  98. }

实验结果

下图是对一幅图像分别用1*1,3*3,5*5,9*9标准方框滤波后的图像:
      

非线性滤波

线性滤波易于构造,且易于从频率响应的角度分析,但如果噪声是散粒噪声而非高斯噪声时线性滤波不能去除噪声。如图像突然出现很大的值,线性滤波只是转换为柔和但仍可见的散粒。这时需要非线性滤波。

简单的非线性滤波有 中值滤波-截尾均值滤波定义域滤波 值域滤波 

中值滤波选择每个邻域像素的中值输出; -截尾均值滤波是指去掉百分率为 的最小值和最大值;定义域滤波中沿着边界的数字是像素的距离;值域就是去掉值域外的像素值。

中值滤波代码

[cpp] view plaincopy
  1. medianBlur ( src, dst, i );

中值滤波实验

下图是对一幅图像分别用3*3,5*5,7*7,9*9(这里必须是奇数)标准方框滤波后的图像:

      

【双边滤波】

双边滤波的思想是抑制与中心像素值差别太大的像素,输出像素值依赖于邻域像素值的加权合:

权重系数 取决于定义域核

和依赖于数据的值域核

的乘积。相乘后会产生依赖于数据的双边权重函数:

双边滤波源码

[cpp] view plaincopy
  1. /****************************************************************************************\
  2. Bilateral Filtering
  3. \****************************************************************************************/
  4. namespace cv
  5. {
  6. static void
  7. bilateralFilter_8u( const Mat& src, Mat& dst, int d,
  8. double sigma_color, double sigma_space,
  9. int borderType )
  10. {
  11. int cn = src.channels();
  12. int i, j, k, maxk, radius;
  13. Size size = src.size();
  14. CV_Assert( (src.type() == CV_8UC1 || src.type() == CV_8UC3) &&
  15. src.type() == dst.type() && src.size() == dst.size() &&
  16. src.data != dst.data );
  17. if( sigma_color <= 0 )
  18. sigma_color = 1;
  19. if( sigma_space <= 0 )
  20. sigma_space = 1;
  21. double gauss_color_coeff = -0.5/(sigma_color*sigma_color);
  22. double gauss_space_coeff = -0.5/(sigma_space*sigma_space);
  23. if( d <= 0 )
  24. radius = cvRound(sigma_space*1.5);
  25. else
  26. radius = d/2;
  27. radius = MAX(radius, 1);
  28. d = radius*2 + 1;
  29. Mat temp;
  30. copyMakeBorder( src, temp, radius, radius, radius, radius, borderType );
  31. vector<float> _color_weight(cn*256);
  32. vector<float> _space_weight(d*d);
  33. vector<int> _space_ofs(d*d);
  34. float* color_weight = &_color_weight[0];
  35. float* space_weight = &_space_weight[0];
  36. int* space_ofs = &_space_ofs[0];
  37. // initialize color-related bilateral filter coefficients
  38. for( i = 0; i < 256*cn; i++ )
  39. color_weight[i] = (float)std::exp(i*i*gauss_color_coeff);
  40. // initialize space-related bilateral filter coefficients
  41. for( i = -radius, maxk = 0; i <= radius; i++ )
  42. for( j = -radius; j <= radius; j++ )
  43. {
  44. double r = std::sqrt((double)i*i + (double)j*j);
  45. if( r > radius )
  46. continue;
  47. space_weight[maxk] = (float)std::exp(r*r*gauss_space_coeff);
  48. space_ofs[maxk++] = (int)(i*temp.step + j*cn);
  49. }
  50. for( i = 0; i < size.height; i++ )
  51. {
  52. const uchar* sptr = temp.data + (i+radius)*temp.step + radius*cn;
  53. uchar* dptr = dst.data + i*dst.step;
  54. if( cn == 1 )
  55. {
  56. for( j = 0; j < size.width; j++ )
  57. {
  58. float sum = 0, wsum = 0;
  59. int val0 = sptr[j];
  60. for( k = 0; k < maxk; k++ )
  61. {
  62. int val = sptr[j + space_ofs[k]];
  63. float w = space_weight[k]*color_weight[std::abs(val - val0)];
  64. sum += val*w;
  65. wsum += w;
  66. }
  67. // overflow is not possible here => there is no need to use CV_CAST_8U
  68. dptr[j] = (uchar)cvRound(sum/wsum);
  69. }
  70. }
  71. else
  72. {
  73. assert( cn == 3 );
  74. for( j = 0; j < size.width*3; j += 3 )
  75. {
  76. float sum_b = 0, sum_g = 0, sum_r = 0, wsum = 0;
  77. int b0 = sptr[j], g0 = sptr[j+1], r0 = sptr[j+2];
  78. for( k = 0; k < maxk; k++ )
  79. {
  80. const uchar* sptr_k = sptr + j + space_ofs[k];
  81. int b = sptr_k[0], g = sptr_k[1], r = sptr_k[2];
  82. float w = space_weight[k]*color_weight[std::abs(b - b0) +
  83. std::abs(g - g0) + std::abs(r - r0)];
  84. sum_b += b*w; sum_g += g*w; sum_r += r*w;
  85. wsum += w;
  86. }
  87. wsum = 1.f/wsum;
  88. b0 = cvRound(sum_b*wsum);
  89. g0 = cvRound(sum_g*wsum);
  90. r0 = cvRound(sum_r*wsum);
  91. dptr[j] = (uchar)b0; dptr[j+1] = (uchar)g0; dptr[j+2] = (uchar)r0;
  92. }
  93. }
  94. }
  95. }

双边滤波调用

[cpp] view plaincopy
  1. bilateralFilter(InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace,
  2. int borderType=BORDER_DEFAULT );

d 表示滤波时像素邻域直径,d为负时由 sigaColor计算得到;d>5时不能实时处理。

sigmaColor、sigmaSpace非别表示颜色空间和坐标空间的滤波系数sigma。可以简单的赋值为相同的值。<10时几乎没有效果;>150时为油画的效果。
borderType可以不指定。

双边滤波实验

用sigma为10,150,240,480时效果如下:
       

参考文献:

Richard Szeliski 《Computer Vision: Algorithms and Applications》
http://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/MANDUCHI1/Bilateral_Filtering.html
《The OpenCV Tutorials》 Release 2.4.2
《The OpenCV Reference Manual 》 Release 2.4.2

【OpenCV】邻域滤波:方框、高斯、中值、双边滤波相关推荐

  1. opencv方框内图像保存_opencv 图像滤波(均值,方框,高斯,中值)

    为什么要使用滤波 消除图像中的噪声成分叫作图像的平滑化或滤波操作.信号或图像的能量大部分集中在幅度谱的低频和中频段是很常见的,而在较高频段,感兴趣的信息经常被噪声淹没.因此一个能降低高频成分幅度的滤波 ...

  2. 【图像去噪】基于matlab高斯+均值+中值+双边滤波图像去噪【含Matlab源码 1872期】

    ⛄一.高斯+均值+中值+双边滤波图像去噪简介 1 数字图像去噪技术简述 1.1 研究背景及目的 图像是人类认识世界的第一视角,我们可以通过图像获得比较真实的信息和直观的结果.但实际上,在产生和传输过程 ...

  3. NR基础篇下——中值滤波、多级中值滤波、多级中值混合滤波、加权中值滤波、中值有理滤波

    上一篇分享了一些均值滤波相关的算法,均值滤波作为一种线性滤波器,在滤除噪声的同时也会导致边缘模糊问题.而且均值滤波对高斯噪声的效果很好,但是对于椒盐噪声的效果就很一般.但是中值滤波作为一种顺序滤波器, ...

  4. opencv 图像雾检测_专栏 | OpenCV图像处理专栏十 | 利用中值滤波进行去雾

    原标题:专栏 | OpenCV图像处理专栏十 | 利用中值滤波进行去雾 前言 这是OpenCV图像处理专栏的第十篇文章,介绍一种利用中值滤波来实现去雾的算法.这个方法发表于国内的一篇论文,链接我放附录 ...

  5. 【OpenCV 例程200篇】59. 非线性滤波—双边滤波

    [OpenCV 例程200篇]59. 非线性滤波-双边滤波(Bilateral filter) 欢迎关注 『OpenCV 例程200篇』 系列,持续更新中 欢迎关注 『Python小白的OpenCV学 ...

  6. 滤波算法、中值和均值滤波区别

    滤波算法:  这里所讲的算法都是针对图像空间的滤波算法,其中模板,可以理解为图像形态学中的结构元素,是用来选取图像中的那些像素点被用来操作的.空间滤波根据其功能划分为平滑滤波和锐化滤波.平滑滤波:能减 ...

  7. 【opencv】(3) 图像滤波:均值、方框、中值、高斯

    内容有: 均值滤波 cv2.blur(),方框滤波 cv2.boxFilter(),高斯滤波 cv2.GaussianBlur(),中值滤波 cv2.medianBlur() 滤波可理解为,平均卷积操 ...

  8. c++ openvc4.5.5 学习笔记(五)图像平滑滤波几种基本方法(平均滤波blur、高斯平滑滤波GaussianBlur、中值滤波medianBlur、双边滤波bilateralFilter )

    平滑,也称为模糊,是一种简单而经常使用的图像处理操作. 要执行平滑操作,我们将对我们的图像应用过滤器.最常见的滤波器类型是线性的,其中输出像素的值(i.e. g(i,j)),被确定为输入像素值的加权和 ...

  9. Opencv之图像滤波:5.中值滤波(cv2.medianBlur)

    之前介绍的均值滤波.方框滤波.高斯滤波,都是线性滤波方式.由于线性滤波的结果是所有像素值的线性组合,因此含有噪声的像素也会被考虑进去,噪声不会被消除,而是以更柔和的方式存在.这时使用非线性滤波效果可能 ...

  10. matlab编程实现自适应均值滤波和自适应中值滤波

    matlab编程实现自适应滤波器 一.自适应均值滤波器 1. 原理部分: 2. 程序代码 3. 结果对比 二.自适应中值滤波 1. 原理部分 2.程序代码 3. 结果对比 一.自适应均值滤波器 1. ...

最新文章

  1. 同一局域网内_Pycharm访问服务器
  2. golang中的fallthrough
  3. 高通平台msm8909 LK 实现LCD 兼容
  4. c++中的pod类型
  5. LeetCode 2090. 半径为 k 的子数组平均值(滑窗)
  6. android今日头条刷新,仿今日头条刷新vector动画
  7. Ajax在请求数据时显示等待动画遮罩
  8. 工作流添加跟踪后,实例一启动就会自动关闭
  9. [CF]Round 516
  10. 缠中说禅电子书_缠中说禅作者的可怕之处
  11. 怎么把电脑计算机桌面调出来,手把手教你电脑怎么设置屏保
  12. linux远程连接交换机,总结:交换机远程登陆的两种方式,Telnet与SSH那种好?
  13. 外企计算机英语,职场英语:外企生存十大必备英语词汇
  14. Python玩转图像格式转换
  15. 数学运算符“异或”的妙用
  16. DenseCLIP:Language-Guided Dense Prediction with Context-Aware Prompting
  17. python谱聚类算法_Python机器学习高级算法!谱聚类(Spectal Clustering)算法分析和实例详解...
  18. 如何自学单片机? 单片机怎么入门?
  19. Warmup 原理与实现
  20. 连接正常(或者能上QQ)但是上不了网

热门文章

  1. android 安装 apk 7.0,android 7.0及以上版本安装apk
  2. .net 考试系统人一多就断开了_2020年银行从业资格考试成绩查询入口已开放
  3. 简单的Java秒表计时器(线程),Java计时器使功能每分钟运行
  4. 遇到 400、500 错误千万不要慌!
  5. 最华丽的 Kubernetes 桌面客户端:Lens
  6. 深度剖析RabbitMQ可靠性消息投递以及实践方案
  7. 死磕Java并发:J.U.C之Condition
  8. 渗透测试中使用浏览器的正确姿势
  9. java poi 导出 国际化_更好用的excel国际化多语言导出
  10. UML之构件图和部署图