当前位置:   article > 正文

OpenCV笔记21 频率域滤波_opencv分离频域

opencv分离频域

一、概述

       图像的傅里叶变换及其两个重要的度量:幅度谱和相位谱。了解两个重要的概念:低频和高频。低频指的是图
的傅里叶变换 中心位置 附近的区域。注意,如无特殊说明,后面所提到的图像的傅里叶变换都是中心化后的。高频随着到“ 中心位置 距离的增加而增加,即傅里叶变换中心位置的外围区域,这里的“ 中心位置 指的是傅里叶变换所对应的幅度谱最大值的位置。
频率域滤波器在程序或者数学运算中的呈现可以理解为一个矩阵,该矩阵的宽、高和图像的傅里叶变换的宽、高是相同的,下面所涉及的常用的低通、高通、带通、带阻 等滤波的关键步骤,就是通过一定的准则构造该矩阵的。

二、步骤

步骤如下:

下面通过一个简单的例子来详细解释频率域滤波的整个步骤。
第一步:输入图像矩阵 I 。假设为:
第二步:图像矩阵的每一个像素值乘以 (-1)r+c 得到矩阵 I′ I′ =I.* -1 r+c ,其
r c 代表当前像素值在矩阵中的位置索引。
第三步:因为图像矩阵的宽和高均为 7 ,为了利用傅里叶变换的快速算法,对 I′ 0 ,使用命令getOptimalDFTSize 7 )得到一个不小于 7 且可以分解为 2p ×3q ×5r 的最小整数,计算结果为8 。所以在矩阵 I′ 的右侧和下侧各补一行 0 ,记为 f

 第四步:利用傅里叶变换的快速算法得到复数矩阵F

 

        OpenCV是将复数矩阵按照双通道存储的,即第一通道存储的是复数矩阵的实部,第二通道存储的是复数矩阵的虚部。

第五步:构建频率域滤波器 Filter 。频率域滤波器本质上是一个和第四步得到的快速傅里叶变换矩阵F 具有相同行数、列数的复数矩阵,一般情况下为实数矩阵,这里假设是 一个全是1的矩阵:

本章提到的频率域滤波,如低通滤波、高通滤波、自定义滤波等,其关键步骤就是 通过一定的标准构造该矩阵以完成图像在频率域上的滤波。
第六步:将第四步得到的快速傅里叶变换矩阵 F 和第五步得到的频率域滤波器 Filter 的对应位置相乘(矩阵的点乘)。当然,如果滤波器是一个实数矩阵,那么在代码实现 中,将傅里叶变换的实部和虚部分别与频率域滤波器进行点乘即可,即Ffilter =F.*Filter,因为这里构造的滤波器是一个全是1 的矩阵,所以 F filter =F
第七步:对第六步得到的点乘矩阵 Ffilter 进行傅里叶逆变换,得到复数矩阵 F′
第八步: 取复数矩阵F′的实部
第九步:与第二步类似,将第八步得到的矩阵乘以( -1 r+c
第十步:因为在快速傅里叶变换的步骤中进行了补 0 操作,所以第九步得到的实部矩 阵的尺寸有可能比原图大,所以要进行裁剪,取该实部矩阵的左上角,尺寸和原图相同。裁剪得到的结果,即为频率域滤波的结果。在该示例中,因为滤波器是一个全是1
矩阵,相当于对原图没有做任何处理,即最后滤波的结果和原图是一样的。
频率域滤波算法均是按照上述十个步骤完成的,接下来详细介绍常用滤波器的构建 方法、代码实现及其效果。

三、低通滤波

 针对图像的傅里叶变换,低频信息表示图像中灰度值缓慢变化的区域;而高频信息则正好相反,表示灰度值变化迅速的部分,如边缘。低通滤波,顾名思义,保留傅里叶变换的低频信息;或者削弱傅里叶变换的高频信息;而高通滤波则正好相反,保留傅里叶变换的高频信息,移除或者削弱傅里叶变换的低频信息。
三种常用的低通滤波器:
               H、 W 分别代表图像快速傅里叶变换的高、宽, 傅里叶谱的最大值(中心点)的位置在maxR,maxC , radius 代表截断频率, D r c )代表到中心位置的距离
1、理想低通滤波:ilpFilter=[ilpFilter(r,c)]H*w,

 2、巴特沃斯低通滤波器

第二种是巴特沃斯低通滤波器,记 blpFilter=[blpFilter r c ]H×W

 3、高斯低通滤波器

glpFilter=[glpFilterrc)]H×W;

 作用:

滤波器越靠近中心点位置的值越接近于1,越远离中心位置的值就越小于1,与傅里叶变换相乘后,相当于保留了低频信息,消弱或者移除了高频信息

四、低通滤波的代码实现

  1. // 快速傅里叶变换
  2. void fft2Image(InputArray image, OutputArray F)
  3. {
  4. //的到Mat 的类型
  5. Mat Image = image.getMat();
  6. int rows = Image.rows;
  7. int cols = Image.cols;
  8. // 获得最佳两列
  9. int r = getOptimalDFTSize(rows);
  10. int c = getOptimalDFTSize(cols);
  11. // 在左侧和下面进10
  12. Mat f;
  13. copyMakeBorder(Image, f, 0, r - rows, 0, c - cols, BORDER_CONSTANT,Scalar::all(0));
  14. // 两个通道,第一个通道用存储实部 第二个通道是存放虚部的
  15. dft(f, F, DFT_COMPLEX_OUTPUT);
  16. //傅里叶逆变换
  17. //dft(f, F, DFT_INVERSE+DFT_REAL_OUTPUT+DFT_SCALE);//只返回实部
  18. }
  19. // 幅度谱
  20. void amplitudeSpectrum(InputArray _srcfft, OutputArray _dstSpectrum)
  21. {
  22. if (_srcfft.channels() != 2) return ;
  23. // 如果是两个通道则开始分离通道
  24. vector<Mat> FFT2Channels;
  25. split(_srcfft, FFT2Channels);
  26. // 计算傅里叶变换幅度值 sqrt(pow(r,2)+pow(L,2))
  27. magnitude(FFT2Channels[0], FFT2Channels[1], _dstSpectrum);
  28. }
  29. // 傅里叶谱的灰度级显示
  30. Mat graySpectrum(Mat s)
  31. {
  32. Mat dst;
  33. log(s + 1, dst);
  34. // 归一化
  35. normalize(dst,dst,0,1,NORM_MINMAX);
  36. // 为了显示 灰度级
  37. dst.convertTo(dst,CV_8UC1,255,0);
  38. return dst;
  39. }
  40. // 计算傅里叶变换的相位角==phase 算子
  41. Mat phaseS(Mat sfft)
  42. {
  43. // 相位谱
  44. Mat phase;
  45. phase.create(sfft.size(), CV_64FC1);
  46. // 分离通道
  47. vector<Mat> fft2Channels;
  48. split(sfft,fft2Channels);
  49. // 开始计算相位谱
  50. for (int r = 0; r < phase.rows;r++) {
  51. for (int c = 0; c < phase.cols; c++) {
  52. // 实部 虚部
  53. double real = fft2Channels[0].at<double>(r,c);
  54. double img = fft2Channels[1].at<double>(r,c);
  55. // atan2 的返回值就是【0,180】,【-180,0】
  56. phase.at<double>(r, c) = atan2(img,real);
  57. }
  58. }
  59. return phase;
  60. }
  61. // -----------------------------------------------------------
  62. #if 1 //低通滤波
  63. enum typeFilter
  64. {
  65. ILPFILTER,
  66. BLPFILTER,
  67. GLPFILTER,
  68. };
  69. Mat createFilter(Size size, Point center,float radius, int type, int n = 2)
  70. {
  71. Mat ilpFilter = Mat::zeros(size,CV_32FC1); // 定义一个size大小的滤波器
  72. int rows = size.height;
  73. int cols = size.width;
  74. if (radius <= 0)return ilpFilter;
  75. // 构建理想的低通滤波器
  76. if (type== ILPFILTER)
  77. {
  78. for (int r=0;r< rows;r++)
  79. {
  80. for (int c = 0; c < cols;c++)
  81. {
  82. // 计算距离
  83. float norm2 = pow(abs(float(r - center.y)), 2)+ pow(abs(float(r - center.y)), 2);
  84. if (norm2 < radius)
  85. {
  86. ilpFilter.at<float>(r, c) = 1;
  87. }
  88. else
  89. {
  90. ilpFilter.at<float>(r, c) = 0;
  91. }
  92. }
  93. }
  94. }
  95. //
  96. else if(type == BLPFILTER)
  97. {
  98. for (int r = 0; r < rows; r++)
  99. {
  100. for (int c = 0; c < cols; c++)
  101. {
  102. /* float m = sqrt(pow(r - center.y, 2.0) + pow(c - center.x, 2.0));
  103. float n = m / radius;
  104. float o = pow(n,2*n);
  105. float s = 1.0 + o;
  106. float i = 1.0 / s;*/
  107. ilpFilter.at<float>(r, c) = float(1.0 / (1.0 + (pow(sqrt(pow(r - center.y, 2.0) + pow(c - center.x, 2.0)) / radius, 2 * n))));
  108. }
  109. }
  110. }
  111. // 高斯滤波
  112. else if (type == GLPFILTER)
  113. {
  114. for (int r = 0; r < rows; r++)
  115. {
  116. for (int c = 0; c < cols; c++)
  117. {
  118. ilpFilter.at<float>(r, c) = float(exp(-(pow(c-center.x,2.0)+pow(r-center.y,2.0))/(2*pow(radius,2.0))));
  119. }
  120. }
  121. }
  122. return ilpFilter;
  123. }
  124. Mat src;// 输入的图像
  125. Mat F;// 图像的快速傅里叶变换
  126. Point maxLocation;//傅里叶谱的最大值的坐标
  127. int radius = 20; // 截断频率
  128. const int max_Radius = 100;// 最大截断频率
  129. Mat ilpFilter;//低通滤波器
  130. int filterType=0;//
  131. int max_FilterType = 2;
  132. Mat F_ilpFilter;// 低通傅里叶变换
  133. Mat FlpSpetrum;// 低通傅里叶变换的傅里叶谱的灰度级
  134. Mat result;// 滤波后的图像
  135. string lpFilterSpectrum = "低通傅里叶谱";//
  136. void callback_lpFilter(int ,void *);
  137. int main()
  138. {
  139. src = imread("C:\\Users\\19473\\Desktop\\opencv_images\\611.png",CV_LOAD_IMAGE_GRAYSCALE);
  140. if (!src.data)
  141. {
  142. cout << "原始图读取失败" << endl;
  143. return -1;
  144. }
  145. imshow("原图",src);
  146. // 数据转换 将数据转换为double
  147. Mat f1;
  148. src.convertTo(f1,CV_32FC1,1.0,0.0);
  149. // 2 每个数 x pow(-1,c+r);
  150. for (int r = 0; r < f1.rows; r++)
  151. {
  152. for (int c = 0; c < f1.cols; c++)
  153. {
  154. // 判断奇偶
  155. if (r+c%2)
  156. {
  157. f1.at<float>(r, c) *= -1;
  158. }
  159. }
  160. }
  161. // 开始傅里叶变换
  162. fft2Image(f1,F);// F 就是傅里叶变换之后的
  163. Mat as;
  164. //得到傅里叶谱
  165. amplitudeSpectrum(F,as);
  166. //傅里叶谱的灰度级显示
  167. Mat s = graySpectrum(as);
  168. imshow("原傅里叶谱的灰度级显示",s);
  169. // 找到傅里叶谱的最大值的坐标
  170. minMaxLoc(s,NULL,NULL,NULL,&maxLocation);
  171. //------------------------------------ 低通滤波----------------------------------------------
  172. namedWindow(lpFilterSpectrum,WINDOW_AUTOSIZE);
  173. createTrackbar("低通滤波:", lpFilterSpectrum,&filterType, max_FilterType, callback_lpFilter);
  174. createTrackbar("半径:", lpFilterSpectrum, &radius, max_Radius, callback_lpFilter);
  175. callback_lpFilter(0,0);
  176. waitKey(0);
  177. return 0;
  178. }
  179. // 回调函数
  180. void callback_lpFilter(int, void*)
  181. {
  182. // ----------------- 构建低通滤波器----------------------------------
  183. ilpFilter = createFilter(F.size(),maxLocation,radius, filterType,2);
  184. // -------------------- 低通滤波器和图像的傅里叶变换开始点乘=====================================
  185. F_ilpFilter.create(F.size(),F.type());
  186. for (int r = 0; r < F_ilpFilter.rows; r++)
  187. {
  188. for (int c = 0; c < F_ilpFilter.cols; c++)
  189. {
  190. // 分别取出当前位置的快速傅里叶变换和理想低通滤波的值
  191. Vec2f f_rc = F.at<Vec2f>(r,c);
  192. float lpFilter_rc = ilpFilter.at<float>(r,c);
  193. // 低通滤波器和图像的快速傅里叶变换的对应位置相乘
  194. F_ilpFilter.at<Vec2f>(r, c) = f_rc * lpFilter_rc;
  195. }
  196. }
  197. // 低通傅里叶变换的傅里叶谱
  198. amplitudeSpectrum(F_ilpFilter, FlpSpetrum);
  199. FlpSpetrum = graySpectrum(FlpSpetrum);
  200. imshow(lpFilterSpectrum, FlpSpetrum);
  201. // 对傅里叶变换逆变换 并且只要实部
  202. dft(F_ilpFilter,result,DFT_SCALE+DFT_INVERSE+DFT_REAL_OUTPUT);
  203. // 乘以 (-1)的(r+c)次方
  204. for (int r = 0; r < result.rows; r++)
  205. {
  206. for (int c = 0; c < result.cols; c++)
  207. {
  208. if ((c + r) % 2) result.at<float>(r, c) *= -1;
  209. }
  210. }
  211. // 很重要的一步 将结果转换成 CV_8u
  212. result.convertTo(result, CV_8UC1, 1.0, 0);
  213. result = result(Rect(0,0,src.cols,src.rows)).clone();
  214. imshow("经过低通滤波后的图像",result);
  215. }
  216. #endif

 五、高通滤波

maxR maxC 代表图像傅里叶谱的最大值的位置,D(r,c):

1、 理想高通滤波

2. 巴特沃斯高通滤波器

3. 高斯高通滤波器

 六、带通 带阻滤波器

带通滤波是指只保留某一范围区域的频率带。下面介绍三种常用的带通滤波器。假 设BW 代表带宽, D0 代表带宽的径向中心,其他符号与低通、高通滤波相同。
1. 理想带通滤波器和理想带阻滤波器

 2. 巴特沃斯带通滤波器和巴特沃斯带阻滤波器

 3. 高斯带通滤波器和高斯带阻滤波器

 带滤波器和这个上述的恰好相反

七、自定义滤波器

  1. #if 1 //自定义滤波器
  2. Mat image;// 输入的图像
  3. Mat ImageFFT;// 图像的快速傅里叶变换
  4. Point maxLocation;//傅里叶谱的最大值的坐标
  5. Mat ilpFilter;//低通滤波器
  6. Mat F_ImageSpetrum;// 傅里叶变换的傅里叶谱
  7. Mat result;// 滤波后的图像
  8. string windowName = "傅里叶幅度谱的灰度级";//
  9. bool drawing_box = false;// 鼠标事件
  10. Point drawPoint;
  11. Rect rectFilter;
  12. bool gotRectFilter = false;
  13. void mouseRectHandler(int event,int x,int y,int,void* )
  14. {
  15. switch (event)
  16. {
  17. case CV_EVENT_LBUTTONDOWN:// 鼠标按下
  18. drawing_box = true;
  19. // 记录起点
  20. drawPoint = Point(x, y);
  21. break;
  22. case CV_EVENT_MOUSEMOVE:
  23. if (drawing_box)
  24. {
  25. // 将鼠标移动到右下角
  26. if (x> drawPoint.x&&y>= drawPoint.y)
  27. {
  28. rectFilter.x = drawPoint.x;
  29. rectFilter.y = drawPoint.y;
  30. rectFilter.width = x - drawPoint.x;
  31. rectFilter.height= y - drawPoint.y;
  32. }
  33. // 将鼠标移动到有右上角
  34. if (x >= drawPoint.x && y <= drawPoint.y)
  35. {
  36. rectFilter.x = drawPoint.x;
  37. rectFilter.y = y;
  38. rectFilter.width = x - drawPoint.x;
  39. rectFilter.height = drawPoint.y- y ;
  40. }
  41. // 将鼠标移动到有左上角
  42. if (x >= drawPoint.x && y <= drawPoint.y)
  43. {
  44. rectFilter.x = x;
  45. rectFilter.y = y;
  46. rectFilter.width = drawPoint.x - x;
  47. rectFilter.height = drawPoint.y - y;
  48. }
  49. // 将鼠标移动到有左下角
  50. if (x >= drawPoint.x && y <= drawPoint.y)
  51. {
  52. rectFilter.x = x;
  53. rectFilter.y = drawPoint.y;
  54. rectFilter.width = drawPoint.x-x;
  55. rectFilter.height = y- drawPoint.y ;
  56. }
  57. }
  58. break;
  59. case CV_EVENT_LBUTTONUP:
  60. drawing_box = false;
  61. gotRectFilter = true;
  62. break;
  63. default:
  64. break;
  65. }
  66. }
  67. int main()
  68. {
  69. image = imread("C:\\Users\\19473\\Desktop\\opencv_images\\612.png",CV_LOAD_IMAGE_GRAYSCALE);
  70. if (!image.data)
  71. {
  72. cout << "原始图读取失败" << endl;
  73. return -1;
  74. }
  75. imshow("原图", image);
  76. // 数据转换 将数据转换为double
  77. Mat f1;
  78. image.convertTo(f1,CV_32FC1,1.0,0.0);
  79. // 2 每个数 x pow(-1,c+r);
  80. for (int r = 0; r < f1.rows; r++)
  81. {
  82. for (int c = 0; c < f1.cols; c++)
  83. {
  84. // 判断奇偶
  85. if (r+c%2)
  86. {
  87. f1.at<float>(r, c) *= -1;
  88. }
  89. }
  90. }
  91. // 开始傅里叶变换
  92. fft2Image(f1,ImageFFT);// F 就是傅里叶变换之后的
  93. Mat amplSpec;
  94. //得到傅里叶谱
  95. amplitudeSpectrum(ImageFFT, amplSpec);
  96. //傅里叶谱的灰度级显示
  97. Mat spectrum = graySpectrum(amplSpec);
  98. // 找到傅里叶谱的最大值的坐标
  99. minMaxLoc(amplSpec,NULL,NULL,NULL,&maxLocation);
  100. //------------------------------------ 自定义 滤波----------------------------------------------
  101. namedWindow(windowName,WINDOW_AUTOSIZE);
  102. setMouseCallback(windowName, mouseRectHandler,NULL);
  103. for (;;)
  104. {
  105. spectrum(rectFilter).setTo(0);
  106. // 自定义 滤波器和傅里叶变换点乘
  107. ImageFFT(rectFilter).setTo(Scalar::all(0));
  108. imshow(windowName, spectrum);
  109. // 按esc 见退出编辑
  110. if (waitKey(10)==27)
  111. {
  112. break;
  113. }
  114. }
  115. Mat result;
  116. // 对傅里叶变换逆变换 并且只要实部
  117. dft(ImageFFT, result, DFT_SCALE + DFT_INVERSE + DFT_REAL_OUTPUT);
  118. // 乘以 (-1)的(r+c)次方
  119. for (int r = 0; r < result.rows; r++)
  120. {
  121. for (int c = 0; c < result.cols; c++)
  122. {
  123. if ((c + r) % 2) result.at<float>(r, c) *= -1;
  124. }
  125. }
  126. // 很重要的一步 将结果转换成 CV_8u
  127. result.convertTo(result, CV_8UC1, 1.0, 0);
  128. result = result(Rect(0, 0, image.cols, image.rows)).clone();
  129. imshow("经过自定义滤波后的图像", result);
  130. waitKey(0);
  131. return 0;
  132. }
  133. // 回调函数
  134. #endif

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

闽ICP备14008679号