opencv dft离散傅立叶变换

文章目录

  • 概念
  • 一、算法步骤
    • 1.将图像扩展到最佳大小
    • 2.为复数和实值开辟空间
    • 3.进行离散傅里叶变换
    • 4.将实部和复部转换为振幅
    • 5.转换到对数尺度
    • 6.裁剪和重新排列
    • 7.归一化
  • 运行效果
  • 完整代码


概念

傅里叶变换将图像分解成它的正和余弦分量。换句话说,它将图像从它的空间域变换到它的频域。其思想是,任何函数都可以精确地逼近无穷个正函数和余弦函数的和。傅里叶变换是一种方法。二维图像的傅里叶变换在数学上为:
opencv dft离散傅立叶变换_第1张图片

这里f是空间域的图像值,F是频域的图像值。变换的结果是复数。可以通过实像和复像显示,也可以通过幅值和相位图像显示。然而,在整个图像处理算法中,只有幅值图像是有意义的,因为它包含了我们需要的关于图像几何结构的所有信息。然而,如果你打算在这些形式中对图像做一些修改,然后你需要重新转换它,你需要保留这两个。

一、算法步骤

1.将图像扩展到最佳大小

DFT的性能取决于图像的大小。当图像大小是数字2、3和5的倍数时,它往往是最快的。因此,为了获得最大的性能,通常一个好主意是填充边界值的图像,以获得具有这些特征的大小。getOptimalDFTSize()返回这个最佳大小,我们可以使用copyMakeBorder()函数来扩展图像的边框(拓展的像素被初始化为0):

     cv::Mat src;
     src = cv::imread("D:\\QtProject\\Opencv_Example\\dft\\dft.png", cv::IMREAD_GRAYSCALE);
     if (src.empty()) {
       cout << "Cannot load image" << endl;
       return;
     }
    show(src);

    cv::Mat padded;                            
    int m = cv::getOptimalDFTSize( src.rows );
    int n = cv::getOptimalDFTSize( src.cols ); 
    cv::copyMakeBorder(src, padded, 0, m - src.rows, 0, n - src.cols, cv::BORDER_CONSTANT, cv::Scalar::all(0));

2.为复数和实值开辟空间

傅里叶变换的结果是复数。这意味着对于每个图像值,结果是两个图像值。此外,频域范围比其对应空间域大得多。因此,我们通常至少以浮点格式存储这些数据。因此,我们将我们的输入图像转换为这种类型,并扩展它与另一个通道来保存复数值:

    cv::Mat planes[] = {cv::Mat_<float>(padded), cv::Mat::zeros(padded.size(), CV_32F)};
    cv::Mat complexI;
    cv::merge(planes, 2, complexI);        

3.进行离散傅里叶变换

可以就地计算(输入与输出相同):

    cv::dft(complexI, complexI);    

4.将实部和复部转换为振幅

一个复数有一个实部(Re)和一个复数部(虚部Im)。DFT的结果是复数。DFT的振幅为:
在这里插入图片描述

    cv::dft(complexI, complexI);            

5.转换到对数尺度

结果表明,傅里叶系数的动态范围太大,无法在屏幕上显示。我们有一些小的和一些高的变化值,我们不能这样观察。因此高的值都是白点,小的值都是黑点。为了使用灰度值来实现可视化,我们可以将线性刻度转换为对数尺度:

    cv::split(complexI, planes);                  
    cv::magnitude(planes[0], planes[1], planes[0]);
    cv::Mat magI = planes[0];
    magI += cv::Scalar::all(1);                    
    cv::log(magI, magI);

6.裁剪和重新排列

在第一步,我们扩展了图像?好了,是时候扔掉这些新引入的价值观了。出于可视化的目的,我们也可以重新排列结果的象限,以便原点(0,0)与图像中心相对应。

    magI = magI(cv::Rect(0, 0, magI.cols & -2, magI.rows & -2));
    int cx = magI.cols/2;
    int cy = magI.rows/2;
    cv::Mat q0(magI, cv::Rect(0, 0, cx, cy));  
    cv::Mat q1(magI, cv::Rect(cx, 0, cx, cy)); 
    cv::Mat q2(magI, cv::Rect(0, cy, cx, cy));  
    cv::Mat q3(magI, cv::Rect(cx, cy, cx, cy)); 
    cv::Mat tmp;                       
    q0.copyTo(tmp);
    q3.copyTo(q0);
    tmp.copyTo(q3);
    q1.copyTo(tmp);                   
    q2.copyTo(q1);
    tmp.copyTo(q2);

7.归一化

这也是为了可视化的目的。我们现在有了幅值,但是这仍然超出了我们图像显示的0到1范围。我们使用cv::normalize()函数将值归一化到这个范围。

    cv::normalize(magI, magI, 0, 1, cv::NORM_MINMAX);                                          
    cv::imshow("Input Image",src);    
    cv::imshow("spectrum magnitude", magI);

运行效果

完整代码

     cv::Mat src;
     src = cv::imread("D:\\QtProject\\Opencv_Example\\dft\\dft.png", cv::IMREAD_GRAYSCALE);
     if (src.empty()) {
       cout << "Cannot load image" << endl;
       return;
     }
    show(src);

    cv::Mat padded;                           
    int m = cv::getOptimalDFTSize( src.rows );
    int n = cv::getOptimalDFTSize( src.cols );
    cv::copyMakeBorder(src, padded, 0, m - src.rows, 0, n - src.cols, cv::BORDER_CONSTANT, cv::Scalar::all(0));

    cv::Mat planes[] = {cv::Mat_<float>(padded), cv::Mat::zeros(padded.size(), CV_32F)};
    cv::Mat complexI;
    cv::merge(planes, 2, complexI);         
    cv::dft(complexI, complexI);            

    cv::split(complexI, planes);                 
    cv::magnitude(planes[0], planes[1], planes[0]);
    cv::Mat magI = planes[0];
    magI += cv::Scalar::all(1);                   
    cv::log(magI, magI);
    magI = magI(cv::Rect(0, 0, magI.cols & -2, magI.rows & -2));
    int cx = magI.cols/2;
    int cy = magI.rows/2;
    cv::Mat q0(magI, cv::Rect(0, 0, cx, cy));   
    cv::Mat q1(magI, cv::Rect(cx, 0, cx, cy));  
    cv::Mat q2(magI, cv::Rect(0, cy, cx, cy)); 
    cv::Mat q3(magI, cv::Rect(cx, cy, cx, cy)); 
    cv::Mat tmp;                         
    q0.copyTo(tmp);
    q3.copyTo(q0);
    tmp.copyTo(q3);
    q1.copyTo(tmp);                   
    q2.copyTo(q1);
    tmp.copyTo(q2);
    cv::normalize(magI, magI, 0, 1, cv::NORM_MINMAX); into a
                                       
    cv::imshow("Input Image",src);  
    cv::imshow("spectrum magnitude", magI);

你可能感兴趣的:(opencv,图像处理,opencv)