zoukankan      html  css  js  c++  java
  • opencvdft离散傅立叶变换(把空域变成频域)

    傅立叶变换原理:http://daily.zhihu.com/story/3935067   

                                https://www.zhihu.com/question/22085329/answer/774074211   

    频域:一种描述信号在频率方面特性的坐标系

      

    6.png

    #include<opencv2/opencv.hpp>]
    #include<iostream>
    
    void fftshift(cv::Mat& plane0, cv::Mat& plane1);
    
    int main(int argc, char** argv) {
    
        cv::Mat test = cv::imread("D:/bb/tu/6.png", 0);
        test.convertTo(test, CV_32FC1);
        cv::Mat plane[] = { test.clone(), cv::Mat::zeros(test.size() , CV_32FC1) };
        cv::Mat complexIm;
        cv::merge(plane, 2, complexIm); // 合并通道
        cv::dft(complexIm, complexIm, 0); // 进行傅立叶变换(把空域变成频域)
        /*
        参数1:InputArray src: 输入图像,可以是实数或虚数;必须是单通道或双通道,数据类型必须是float
                对于单通道:输入被认为是实数,输出结果保存为CCS格式
                对于双通道:输入分别被认为是实数和虚数部分---建议双通道,便于区分实数和虚数
                    第一通道是实数,第二通道是虚数
        参数2:OutputArray dst: 输出图像,其大小和类型取决于第三个参数flags
        参数3:int flags: 转换的标识符,有默认值0.其可取的值如下所示:
                DFT_INVERSE=1: 用一维或二维逆变换取代默认的正向变换
                DFT_SCALE=2: 缩放比例标识符,根据数据元素个数平均求出其缩放结果,如有N个元素,
                    则输出结果以1/N缩放输出,常与DFT_INVERSE搭配使用
                DFT_ROWS=4: 对输入矩阵的每行进行正向或反向的傅里叶变换;此标识符可在处理多种适
                    量的的时候用于减小资源的开销,这些处理常常是三维或高维变换等复杂操作
                DFT_COMPLEX_OUTPUT=16: 对一维或二维的实数数组进行正向变换,这样的结果虽然是复数
                    阵列,但拥有复数的共轭对称性(CCS),可以以一个和原数组尺寸大小相同的实数
                    数组进行填充,这是最快的选择也是函数默认的方法。你可能想要得到一个全尺寸
                    的复数数组(像简单光谱分析等等),通过设置标志位可以使函数生成一个全尺寸
                    的复数输出数组
                DFT_REAL_OUTPUT=32: 对一维二维复数数组进行逆向变换,这样的结果通常是一个尺寸相同
                    的复数矩阵,但是如果输入矩阵有复数的共轭对称性(比如是一个带有DFT_COMPLEX_OUTPUT
                    标识符的正变换结果),便会输出实数矩阵
         参数4:int nonzeroRows = 0: 在绝大多数DFT的算法中,建议的尺寸都是2的指数。
                cv::getOptimalDFTSize()帮助获得这个尺寸
                离散傅里叶变换的运算速度与图片的尺寸息息相关,当图像的尺寸是2、3、5的整数倍时,计算速度最快
                nonzeroRows表示多少Rows不参与这个简化运算    
        */
    
        cv::split(complexIm, plane);// 分离通道
        //即planes[0]为实部---幅度图像
        //planes[1]为虚部---相位图像
    
        fftshift(plane[0], plane[1]);
    
        cv::Mat mag, mag_log, mag_nor, mag_log_nor;
        cv::magnitude(plane[0], plane[1], mag); //计算二维矢量的幅值
        //第一个参数:InputArray类型的x,表示矢量的浮点型X坐标值,也就是实部
        //第二个参数:InputArray类型的y,表示矢量的浮点型Y坐标值,也就是虚部
        //第三个参数:OutputArray类型的magnitude,输出的幅值,它和第一个参数X有着同样的尺寸和类型
        //【幅值图   大于等于1是白色  小于1是黑色    幅值>0】
    
        // 幅值对数化:log(1+m),便于观察频谱信息
        mag += cv::Scalar::all(1);
        cv::log(mag, mag_log);
        cv::normalize(mag, mag_nor, 1, 0, cv::NORM_MINMAX);//归一化
        //归一化之后 越靠近1越亮
        cv::normalize(mag_log, mag_log_nor, 1, 0, cv::NORM_MINMAX);
    
        cv::Mat BLUR;
        // 再次搬移回来进行逆变换
        fftshift(plane[0], plane[1]);
        cv::merge(plane, 2, BLUR); // 实部与虚部合并
        cv::idft(BLUR, BLUR); //把频域变成空域
        // idft结果也为复数---第一通道为原图像
    
        BLUR = BLUR / BLUR.rows / BLUR.cols; //让数据正常
        cv::split(BLUR, plane);//分离通道
    
        cv::Mat p = plane[0].clone();
    
    
        cv::waitKey(0);
        return 0;
    }
    
    // fft变换后进行频谱搬移-->搬移目的:将亮度集中于中心
    void fftshift(cv::Mat& plane0, cv::Mat& plane1)
    {
        // 以下的操作是移动图像  (零频移到中心)
        int cx = plane0.cols / 2;
        int cy = plane0.rows / 2;
        cv::Mat part1_r(plane0, cv::Rect(0, 0, cx, cy));  // 元素坐标表示为(cx, cy)
        //part1_r与plane[0]共享数据
        cv::Mat part2_r(plane0, cv::Rect(cx, 0, cx, cy));
        cv::Mat part3_r(plane0, cv::Rect(0, cy, cx, cy));
        cv::Mat part4_r(plane0, cv::Rect(cx, cy, cx, cy));
    
        cv::Mat temp;
        part1_r.copyTo(temp);  //左上与右下交换位置(实部)
        part4_r.copyTo(part1_r);
        temp.copyTo(part4_r);
    
        part2_r.copyTo(temp);  //右上与左下交换位置(实部)
        part3_r.copyTo(part2_r);
        temp.copyTo(part3_r);
    
        cv::Mat part1_i(plane1, cv::Rect(0, 0, cx, cy));  //元素坐标(cx,cy)
        cv::Mat part2_i(plane1, cv::Rect(cx, 0, cx, cy));
        cv::Mat part3_i(plane1, cv::Rect(0, cy, cx, cy));
        cv::Mat part4_i(plane1, cv::Rect(cx, cy, cx, cy));
    
        part1_i.copyTo(temp);  //左上与右下交换位置(虚部)
        part4_i.copyTo(part1_i);
        temp.copyTo(part4_i);
    
        part2_i.copyTo(temp);  //右上与左下交换位置(虚部)
        part3_i.copyTo(part2_i);
        temp.copyTo(part3_i);
    }

  • 相关阅读:
    1.1 java变量及数据类型
    3. 软件测试的类型
    2.3 软件测试模型之 敏捷测试
    2.2 软件测试的手段
    2.1 软件测试的阶段
    1.1 软件测试基础概念
    浅谈内联元素inline
    微信内置浏览器清除缓存的方法
    我的package.json清单
    我的gulp.js清单
  • 原文地址:https://www.cnblogs.com/liming19680104/p/15571744.html
Copyright © 2011-2022 走看看