当前位置:   article > 正文

OpenCV-高斯低通&高通滤波器(C++)_c++进行高通滤波

c++进行高通滤波

作者:翟天保Steven
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处

场景需求

       做图像处理,滤波是家常便饭,今天给大家分享高斯滤波器实现。

       众所周知,在频谱中,低频主要对应图像在平滑区域的总体灰度级分布,高频对应图像细节部分,如边缘和噪声。高斯滤波是一种线性平滑滤波,适用于消除高斯噪声,广泛应用于图像处理的减噪过程。公式和具体原理就不再罗列了,百度一下全都有,接下来是硬货——C++&OpenCV代码实现。

相关功能函数的C++实现代码

  1. // 高斯低通滤波核函数
  2. cv::Mat gaussian_low_pass_kernel(cv::Mat scr, float sigma)
  3. {
  4. cv::Mat gaussianBlur(scr.size(), CV_32FC1); //,CV_32FC1
  5. float d0 = sigma;//高斯函数参数,越小,频率高斯滤波器越窄,滤除高频成分越多,图像就越平滑
  6. for (int i = 0; i < scr.rows; i++) {
  7. for (int j = 0; j < scr.cols; j++) {
  8. float d = pow(float(i - scr.rows / 2), 2) + pow(float(j - scr.cols / 2), 2);//分子,计算pow必须为float型
  9. gaussianBlur.at<float>(i, j) = expf(-d / (2 * d0*d0));//expf为以e为底求幂(必须为float型)
  10. }
  11. }
  12. return gaussianBlur;
  13. }
  14. // 高斯低通滤波
  15. cv::Mat gaussian_low_pass_filter(cv::Mat &src, float d0)
  16. {
  17. cv::Mat padded = image_make_border(src);
  18. cv::Mat gaussian_kernel = gaussian_low_pass_kernel(padded, d0);
  19. cv::Mat result = frequency_filter(padded, gaussian_kernel);
  20. return result;
  21. }
  22. // 高斯高通滤波核函数
  23. cv::Mat gaussian_high_pass_kernel(cv::Mat scr, float sigma)
  24. {
  25. cv::Mat gaussianBlur(scr.size(), CV_32FC1); //,CV_32FC1
  26. float d0 = sigma;
  27. for (int i = 0; i < scr.rows; i++) {
  28. for (int j = 0; j < scr.cols; j++) {
  29. float d = pow(float(i - scr.rows / 2), 2) + pow(float(j - scr.cols / 2), 2);//分子,计算pow必须为float型
  30. gaussianBlur.at<float>(i, j) = 1 - expf(-d / (2 * d0*d0));;
  31. }
  32. }
  33. return gaussianBlur;
  34. }
  35. // 高斯高通滤波
  36. cv::Mat gaussian_high_pass_filter(cv::Mat &src, float d0)
  37. {
  38. cv::Mat padded = image_make_border(src);
  39. cv::Mat gaussian_kernel = gaussian_high_pass_kernel(padded, d0);//理想低通滤波器
  40. cv::Mat result = frequency_filter(padded, gaussian_kernel);
  41. return result;
  42. }
  43. // 频率域滤波
  44. cv::Mat frequency_filter(cv::Mat &scr, cv::Mat &blur)
  45. {
  46. cv::Mat mask = scr == scr;
  47. scr.setTo(0.0f, ~mask);
  48. //创建通道,存储dft后的实部与虚部(CV_32F,必须为单通道数)
  49. cv::Mat plane[] = { scr.clone(), cv::Mat::zeros(scr.size() , CV_32FC1) };
  50. cv::Mat complexIm;
  51. cv::merge(plane, 2, complexIm); // 合并通道 (把两个矩阵合并为一个2通道的Mat类容器)
  52. cv::dft(complexIm, complexIm); // 进行傅立叶变换,结果保存在自身
  53. // 分离通道(数组分离)
  54. cv::split(complexIm, plane);
  55. // 以下的操作是频域迁移
  56. fftshift(plane[0], plane[1]);
  57. // *****************滤波器函数与DFT结果的乘积****************
  58. cv::Mat blur_r, blur_i, BLUR;
  59. cv::multiply(plane[0], blur, blur_r); // 滤波(实部与滤波器模板对应元素相乘)
  60. cv::multiply(plane[1], blur, blur_i); // 滤波(虚部与滤波器模板对应元素相乘)
  61. cv::Mat plane1[] = { blur_r, blur_i };
  62. // 再次搬移回来进行逆变换
  63. fftshift(plane1[0], plane1[1]);
  64. cv::merge(plane1, 2, BLUR); // 实部与虚部合并
  65. cv::idft(BLUR, BLUR); // idft结果也为复数
  66. BLUR = BLUR / BLUR.rows / BLUR.cols;
  67. cv::split(BLUR, plane);//分离通道,主要获取通道
  68. return plane[0];
  69. }
  70. // 图像边界处理
  71. cv::Mat image_make_border(cv::Mat &src)
  72. {
  73. int w = cv::getOptimalDFTSize(src.cols); // 获取DFT变换的最佳宽度
  74. int h = cv::getOptimalDFTSize(src.rows); // 获取DFT变换的最佳高度
  75. cv::Mat padded;
  76. // 常量法扩充图像边界,常量 = 0
  77. cv::copyMakeBorder(src, padded, 0, h - src.rows, 0, w - src.cols, cv::BORDER_CONSTANT, cv::Scalar::all(0));
  78. padded.convertTo(padded, CV_32FC1);
  79. return padded;
  80. }
  81. // fft变换后进行频谱搬移
  82. void fftshift(cv::Mat &plane0, cv::Mat &plane1)
  83. {
  84. // 以下的操作是移动图像 (零频移到中心)
  85. int cx = plane0.cols / 2;
  86. int cy = plane0.rows / 2;
  87. cv::Mat part1_r(plane0, cv::Rect(0, 0, cx, cy)); // 元素坐标表示为(cx, cy)
  88. cv::Mat part2_r(plane0, cv::Rect(cx, 0, cx, cy));
  89. cv::Mat part3_r(plane0, cv::Rect(0, cy, cx, cy));
  90. cv::Mat part4_r(plane0, cv::Rect(cx, cy, cx, cy));
  91. cv::Mat temp;
  92. part1_r.copyTo(temp); //左上与右下交换位置(实部)
  93. part4_r.copyTo(part1_r);
  94. temp.copyTo(part4_r);
  95. part2_r.copyTo(temp); //右上与左下交换位置(实部)
  96. part3_r.copyTo(part2_r);
  97. temp.copyTo(part3_r);
  98. cv::Mat part1_i(plane1, cv::Rect(0, 0, cx, cy)); //元素坐标(cx,cy)
  99. cv::Mat part2_i(plane1, cv::Rect(cx, 0, cx, cy));
  100. cv::Mat part3_i(plane1, cv::Rect(0, cy, cx, cy));
  101. cv::Mat part4_i(plane1, cv::Rect(cx, cy, cx, cy));
  102. part1_i.copyTo(temp); //左上与右下交换位置(虚部)
  103. part4_i.copyTo(part1_i);
  104. temp.copyTo(part4_i);
  105. part2_i.copyTo(temp); //右上与左下交换位置(虚部)
  106. part3_i.copyTo(part2_i);
  107. temp.copyTo(part3_i);
  108. }

测试代码

  1. #include<iostream>
  2. #include<opencv2/opencv.hpp>
  3. #include<ctime>
  4. using namespace std;
  5. using namespace cv;
  6. cv::Mat gaussian_low_pass_kernel(cv::Mat scr, float sigma);
  7. cv::Mat gaussian_low_pass_filter(cv::Mat &src, float d0);
  8. cv::Mat gaussian_high_pass_kernel(cv::Mat scr, float sigma);
  9. cv::Mat gaussian_high_pass_filter(cv::Mat &src, float d0);
  10. cv::Mat frequency_filter(cv::Mat &scr, cv::Mat &blur);
  11. cv::Mat image_make_border(cv::Mat &src);
  12. void fftshift(cv::Mat &plane0, cv::Mat &plane1);
  13. int main(void)
  14. {
  15. Mat test = imread("tangsan.jpg", 0);
  16. float D0 = 50.0f;
  17. float D1 = 5.0f;
  18. Mat lowpass = gaussian_low_pass_filter(test, D0);
  19. Mat highpass = gaussian_high_pass_filter(test, D1);
  20. imshow("original", test);
  21. imshow("low pass", lowpass / 255); // lowpass的数据有正有负,又因为数据的模值大于1,imshow显示出来就是大于1的就是白,小于0的都是黑
  22. imshow("high pass", highpass / 255); // highpass的数据都比较大,0-255,imshow对于float型Mat显示需要除以255
  23. waitKey(0);
  24. system("pause");
  25. return 0;
  26. }
  27. // 高斯低通滤波核函数
  28. cv::Mat gaussian_low_pass_kernel(cv::Mat scr, float sigma)
  29. {
  30. cv::Mat gaussianBlur(scr.size(), CV_32FC1); //,CV_32FC1
  31. float d0 = sigma;//高斯函数参数,越小,频率高斯滤波器越窄,滤除高频成分越多,图像就越平滑
  32. for (int i = 0; i < scr.rows; i++) {
  33. for (int j = 0; j < scr.cols; j++) {
  34. float d = pow(float(i - scr.rows / 2), 2) + pow(float(j - scr.cols / 2), 2);//分子,计算pow必须为float型
  35. gaussianBlur.at<float>(i, j) = expf(-d / (2 * d0*d0));//expf为以e为底求幂(必须为float型)
  36. }
  37. }
  38. return gaussianBlur;
  39. }
  40. // 高斯低通滤波
  41. cv::Mat gaussian_low_pass_filter(cv::Mat &src, float d0)
  42. {
  43. cv::Mat padded = image_make_border(src);
  44. cv::Mat gaussian_kernel = gaussian_low_pass_kernel(padded, d0);
  45. cv::Mat result = frequency_filter(padded, gaussian_kernel);
  46. return result;
  47. }
  48. // 高斯高通滤波核函数
  49. cv::Mat gaussian_high_pass_kernel(cv::Mat scr, float sigma)
  50. {
  51. cv::Mat gaussianBlur(scr.size(), CV_32FC1); //,CV_32FC1
  52. float d0 = sigma;
  53. for (int i = 0; i < scr.rows; i++) {
  54. for (int j = 0; j < scr.cols; j++) {
  55. float d = pow(float(i - scr.rows / 2), 2) + pow(float(j - scr.cols / 2), 2);//分子,计算pow必须为float型
  56. gaussianBlur.at<float>(i, j) = 1 - expf(-d / (2 * d0*d0));;
  57. }
  58. }
  59. return gaussianBlur;
  60. }
  61. // 高斯高通滤波
  62. cv::Mat gaussian_high_pass_filter(cv::Mat &src, float d0)
  63. {
  64. cv::Mat padded = image_make_border(src);
  65. cv::Mat gaussian_kernel = gaussian_high_pass_kernel(padded, d0);//理想低通滤波器
  66. cv::Mat result = frequency_filter(padded, gaussian_kernel);
  67. return result;
  68. }
  69. // 频率域滤波
  70. cv::Mat frequency_filter(cv::Mat &scr, cv::Mat &blur)
  71. {
  72. cv::Mat mask = scr == scr;
  73. scr.setTo(0.0f, ~mask);
  74. //创建通道,存储dft后的实部与虚部(CV_32F,必须为单通道数)
  75. cv::Mat plane[] = { scr.clone(), cv::Mat::zeros(scr.size() , CV_32FC1) };
  76. cv::Mat complexIm;
  77. cv::merge(plane, 2, complexIm); // 合并通道 (把两个矩阵合并为一个2通道的Mat类容器)
  78. cv::dft(complexIm, complexIm); // 进行傅立叶变换,结果保存在自身
  79. // 分离通道(数组分离)
  80. cv::split(complexIm, plane);
  81. // 以下的操作是频域迁移
  82. fftshift(plane[0], plane[1]);
  83. // *****************滤波器函数与DFT结果的乘积****************
  84. cv::Mat blur_r, blur_i, BLUR;
  85. cv::multiply(plane[0], blur, blur_r); // 滤波(实部与滤波器模板对应元素相乘)
  86. cv::multiply(plane[1], blur, blur_i); // 滤波(虚部与滤波器模板对应元素相乘)
  87. cv::Mat plane1[] = { blur_r, blur_i };
  88. // 再次搬移回来进行逆变换
  89. fftshift(plane1[0], plane1[1]);
  90. cv::merge(plane1, 2, BLUR); // 实部与虚部合并
  91. cv::idft(BLUR, BLUR); // idft结果也为复数
  92. BLUR = BLUR / BLUR.rows / BLUR.cols;
  93. cv::split(BLUR, plane);//分离通道,主要获取通道
  94. return plane[0];
  95. }
  96. // 图像边界处理
  97. cv::Mat image_make_border(cv::Mat &src)
  98. {
  99. int w = cv::getOptimalDFTSize(src.cols); // 获取DFT变换的最佳宽度
  100. int h = cv::getOptimalDFTSize(src.rows); // 获取DFT变换的最佳高度
  101. cv::Mat padded;
  102. // 常量法扩充图像边界,常量 = 0
  103. cv::copyMakeBorder(src, padded, 0, h - src.rows, 0, w - src.cols, cv::BORDER_CONSTANT, cv::Scalar::all(0));
  104. padded.convertTo(padded, CV_32FC1);
  105. return padded;
  106. }
  107. // fft变换后进行频谱搬移
  108. void fftshift(cv::Mat &plane0, cv::Mat &plane1)
  109. {
  110. // 以下的操作是移动图像 (零频移到中心)
  111. int cx = plane0.cols / 2;
  112. int cy = plane0.rows / 2;
  113. cv::Mat part1_r(plane0, cv::Rect(0, 0, cx, cy)); // 元素坐标表示为(cx, cy)
  114. cv::Mat part2_r(plane0, cv::Rect(cx, 0, cx, cy));
  115. cv::Mat part3_r(plane0, cv::Rect(0, cy, cx, cy));
  116. cv::Mat part4_r(plane0, cv::Rect(cx, cy, cx, cy));
  117. cv::Mat temp;
  118. part1_r.copyTo(temp); //左上与右下交换位置(实部)
  119. part4_r.copyTo(part1_r);
  120. temp.copyTo(part4_r);
  121. part2_r.copyTo(temp); //右上与左下交换位置(实部)
  122. part3_r.copyTo(part2_r);
  123. temp.copyTo(part3_r);
  124. cv::Mat part1_i(plane1, cv::Rect(0, 0, cx, cy)); //元素坐标(cx,cy)
  125. cv::Mat part2_i(plane1, cv::Rect(cx, 0, cx, cy));
  126. cv::Mat part3_i(plane1, cv::Rect(0, cy, cx, cy));
  127. cv::Mat part4_i(plane1, cv::Rect(cx, cy, cx, cy));
  128. part1_i.copyTo(temp); //左上与右下交换位置(虚部)
  129. part4_i.copyTo(part1_i);
  130. temp.copyTo(part4_i);
  131. part2_i.copyTo(temp); //右上与左下交换位置(虚部)
  132. part3_i.copyTo(part2_i);
  133. temp.copyTo(part3_i);
  134. }

测试效果

图1 低通高通效果图

       不同的滤波参数导致的滤波器尺寸大小不一,得到的结果也就不一样~

       另外,如果我的代码有什么问题,欢迎大家提出异议批评指正,一同进步~

       如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/72604
推荐阅读
相关标签
  

闽ICP备14008679号