OpenCV 频率域处理:离散傅里叶变换、频率域滤波

数字图像本质就是一种信号,那信号自然就有频率。在数字图像中,频率就是指灰度变化的速度。并且数字图像信号是离散的,那么要分析频率域时,就要用到离散傅里叶变换及其逆变换之类的。本文公式主要来自《冈萨雷斯》。


OpenCV 频率域处理:离散傅里叶变换、频率域滤波_第1张图片
某知乎大神做的图

前言

如果读者跟我一样,是EE专业出身的学生(电子、电气、自动化、通信),学过复变函数、信号与系统之类的课程,那么理论部分可以直接去看《冈萨雷斯》的第四章频率域滤波。因为有相关基础,就算不能完全看懂也能半懂。

如果读者是其它专业出身,比如计算机类和机械类专业,那对这方面的理解可能就会不太容易(信号之类的课程应该是选修?),因为对傅里叶变换的理解可能会有所欠缺。当年要用到傅里叶变换的时候(不是用在图像上),还没有学到相关课程,那个时候可是让我抓耳挠腮。不过很幸运阅读了知乎大神Heinrich的文章,很快就对傅里叶变换有了初步了解,在这里推荐一下。

傅里叶变换

在连续的情况下,傅里叶变换和逆变换很简洁,公式如下


傅里叶变换

反变换

由欧拉公式,傅里叶变换可以写成这样。其中,正弦项的频率由μ决定


欧拉公式 + 傅里叶变换

在离散的情况下,公式如下,也很好理解


正变换

逆变换

上面的都是一维的情况。显然,图像是二维的,于是要推广到二维的傅里叶变换。类似,连续情况下傅里叶变换公式如下


二维傅里叶变换

二维傅里叶逆变换

离散的情况如下。其中图像为M*N的图像。


DFT

IDFT

二维DFT一般是复函数,用极坐标来表示如下


极坐标形式

取幅度就得到了被称为傅里叶谱或者频谱的玩意

幅度

计算一下反正切就得到了相角。


相角

因为是二维信号,所以频谱和相角都是二维的。其中相角包含了频率的位置信息。总的来说,图像经过DFT之后得到了频谱和相角。我们在分析的时候,一般只看频谱。但如果要进行逆变换,就同时需要频谱和相角的信息,才能正确还原图像。

频率域滤波

频率域滤波的基本公式如下。H为频率域上的滤波函数。


频率域滤波公式

常见的频率域滤波函数有理想高低通滤波器、布特沃斯滤波器、高斯滤波器。其中前面两者的滤波效果会发生振铃现象。这里的代码实现只搞下高斯滤波。

高斯低通滤波
OpenCV 频率域处理:离散傅里叶变换、频率域滤波_第2张图片
中间高四周低,高频被滤除
高斯高通滤波

OpenCV 频率域处理:离散傅里叶变换、频率域滤波_第3张图片
低频被滤除

编程实现

opencv有用于离散傅里叶变换和逆变换的函数,但还是要稍微处理一些细节,并且频率域的滤波需要自己去编写。

/********************************************************************
 * Created by 杨帮杰 on 12/8/2018
 * Right to use this code in any way you want without
 * warranty, support or any guarantee of it working
 * E-mail: [email protected]
 * Association: SCAU 华南农业大学
 ********************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define IMG_PATH "//home//jacob//图片//lenna.jpg"

using namespace std;
using namespace cv;

int main()
{
    Mat inputImg = imread(IMG_PATH, IMREAD_GRAYSCALE);

    if(inputImg.empty())
    {
        cout << "图片没读到,傻逼!" << endl;
        return -1;
    }

    //得到DFT的最佳尺寸(2的指数),以加速计算
    Mat paddedImg;
    int m = getOptimalDFTSize(inputImg.rows);
    int n = getOptimalDFTSize(inputImg.cols);

    cout << "图片原始尺寸为:" << inputImg.cols << "*" << inputImg.rows <(paddedImg), Mat::zeros(paddedImg.size(), CV_32F)};
    Mat complexInput, complexOutput;
    merge(matArray, 2, complexInput);

    dft(complexInput, complexOutput);

    //计算幅度谱(傅里叶谱)
    split(complexOutput, matArray);
    Mat magImg;
    magnitude(matArray[0], matArray[1], magImg);

    //转换到对数坐标
    magImg += Scalar::all(1);
    log(magImg, magImg);

    //将频谱图像裁剪成偶数并将低频部分放到图像中心
    int width = (magImg.cols / 2)*2;
    int height = (magImg.cols / 2)*2;
    magImg = magImg(Rect(0, 0, width, height));

    int colToCut = magImg.cols/2;
    int rowToCut = magImg.rows/2;

    //获取图像,分别为左上右上左下右下
    //注意这种方式得到的是magImg的ROI的引用
    //对下面四幅图像进行修改就是直接对magImg进行了修改
    Mat topLeftImg(magImg, Rect(0, 0, colToCut, rowToCut));
    Mat topRightImg(magImg, Rect(colToCut, 0, colToCut, rowToCut));
    Mat bottomLeftImg(magImg, Rect(0, rowToCut, colToCut, rowToCut));
    Mat bottomRightImg(magImg, Rect(colToCut, rowToCut, colToCut, rowToCut));

    //第二象限和第四象限进行交换
    Mat tmpImg;
    topLeftImg.copyTo(tmpImg);
    bottomRightImg.copyTo(topLeftImg);
    tmpImg.copyTo(bottomRightImg);

    //第一象限和第三象限进行交换
    bottomLeftImg.copyTo(tmpImg);
    topRightImg.copyTo(bottomLeftImg);
    tmpImg.copyTo(topRightImg);

    //归一化图像
    normalize(magImg, magImg, 0, 1, CV_MINMAX);

    //傅里叶反变换
    Mat complexIDFT, IDFTImg;
    idft(complexOutput, complexIDFT);
    split(complexIDFT, matArray);
    magnitude(matArray[0], matArray[1], IDFTImg);
    normalize(IDFTImg, IDFTImg, 0, 1, CV_MINMAX);

    imshow("输入图像", inputImg);
    imshow("频谱图像", magImg);
    imshow("反变换图像", IDFTImg);

    /***********************频率域滤波部分*************************/
    //高斯低通滤波函数(中间高两边低)
    Mat gaussianBlur(paddedImg.size(),CV_32FC2);
    //高斯高通滤波函数(中间低两边高)
    Mat gaussianSharpen(paddedImg.size(),CV_32FC2);
    double D0=2*10*10*10;
    for(int i=0;i(i);
        float*q=gaussianSharpen.ptr(i);
        for(int j=0;j
OpenCV 频率域处理:离散傅里叶变换、频率域滤波_第4张图片
输入-DFT-IDFT

OpenCV 频率域处理:离散傅里叶变换、频率域滤波_第5张图片
低通滤波

OpenCV 频率域处理:离散傅里叶变换、频率域滤波_第6张图片
高通滤波

Reference:
opencv学习(十五)之图像傅里叶变换dft
opencv 频率域滤波实例
opencv官方例程 :dtf.cpp
《数字图像处理》 ——冈萨雷斯

你可能感兴趣的:(OpenCV 频率域处理:离散傅里叶变换、频率域滤波)