滤波器(利用傅里叶变换后)



前提需要用到的知识傅里叶变换

常用滤波器(利用傅里叶变换后)

我们可以为上述滤波器提供基于频域的数学定义。为了简化描述,我们将仅讨论二维情形,适用于图像处理。假设图像的傅里叶变换为 F ( u , v ) F(u,v) F(u,v),其中 u u u v v v 是频率变量。下面是滤波器的定义和相关数学公式:

低通滤波器 (LPF)

理想低通滤波器 (ILPF)

H ( u , v ) = { 1 if  D ( u , v ) ≤ D 0 0 otherwise  H(u,v) = \begin{cases} 1 & \text{if } D(u,v) \leq D_0 \\0 & \text{otherwise }\end{cases} H(u,v)={ 10if D(u,v)D0otherwise 
其中 D ( u , v ) D(u,v) D(u,v) 是频率坐标 (u,v) 到中心 (0,0) 的距离, D 0 D_0 D0 是截止频率。

#include <iostream>
#include <Eigen/Dense>
#include <cmath>

// 计算两点之间的距离
double distance(int x, int y, int u, int v) {
    
    
    return std::sqrt((x-u)*(x-u) + (y-v)*(y-v));
}

// 定义理想低通滤波器
Eigen::MatrixXd idealLowPassFilter(int rows, int cols, float D0) {
    
    
    Eigen::MatrixXd filter(rows, cols);
    
    int centerX = rows / 2;  // 计算中心点x坐标
    int centerY = cols / 2;  // 计算中心点y坐标

    for(int i = 0; i < rows; i++) {
    
    
        for(int j = 0; j < cols; j++) {
    
    
            // 判断当前点到中心点的距离是否小于或等于D0
            if(distance(i, j, centerX, centerY) <= D0) {
    
    
                filter(i, j) = 1;
            } else {
    
    
                filter(i, j) = 0;
            }
        }
    }

    return filter;
}

巴特沃斯低通滤波器 (BLPF)

H ( u , v ) = 1 1 + ( D ( u , v ) D 0 ) 2 n H(u,v) = \frac{1}{1+\left(\frac{D(u,v)}{D_0}\right)^{2n}} H(u,v)=1+(D0D(u,v))2n1
其中 n n n 是滤波器的阶数。

#include <iostream>
#include <Eigen/Dense>
#include <cmath>

// 计算两点之间的距离
double distance(int x, int y, int u, int v) {
    
    
    return std::sqrt((x-u)*(x-u) + (y-v)*(y-v));
}
// 定义巴特沃斯低通滤波器
Eigen::MatrixXd butterworthLowPassFilter(int rows, int cols, float D0, int n) {
    
    
    Eigen::MatrixXd filter(rows, cols);
    
    int centerX = rows / 2;  // 计算中心点x坐标
    int centerY = cols / 2;  // 计算中心点y坐标

    for(int i = 0; i < rows; i++) {
    
    
        for(int j = 0; j < cols; j++) {
    
    
            double D = distance(i, j, centerX, centerY);
            // 应用巴特沃斯低通滤波器的公式
            filter(i, j) = 1 / (1 + std::pow(D/D0, 2*n));
        }
    }

    return filter;
}

高斯低通滤波器 (GLPF)

H ( u , v ) = e − D 2 ( u , v ) 2 D 0 2 H(u,v) = e^{\frac{-D^2(u,v)}{2D_0^2}} H(u,v)=e2D02D2(u,v)

#include <iostream>
#include <Eigen/Dense>
#include <cmath>

// 计算两点之间的距离
double distance(int x, int y, int u, int v) {
    
    
    return std::sqrt((x-u)*(x-u) + (y-v)*(y-v));
}
// 定义高斯低通滤波器
Eigen::MatrixXd gaussianLowPassFilter(int rows, int cols, float D0) {
    
    
    Eigen::MatrixXd filter(rows, cols);
    
    int centerX = rows / 2;  // 计算中心点x坐标
    int centerY = cols / 2;  // 计算中心点y坐标

    for(int i = 0; i < rows; i++) {
    
    
        for(int j = 0; j < cols; j++) {
    
    
            double D = distance(i, j, centerX, centerY);
            // 应用高斯低通滤波器的公式
            filter(i, j) = std::exp(-(D*D) / (2*D0*D0));
        }
    }

    return filter;
}

高通滤波器 (HPF)

理想高通滤波器 (IHPF)

H ( u , v ) = { 0 if  D ( u , v ) ≤ D 0 1 otherwise  H(u,v) = \begin{cases} 0 & \text{if } D(u,v) \leq D_0 \\ 1 & \text{otherwise }\end{cases} H(u,v)={ 01if D(u,v)D0otherwise 

#include <iostream>
#include <Eigen/Dense>
#include <cmath>

// 计算两点之间的距离
double distance(int x, int y, int u, int v) {
    
    
    return std::sqrt((x-u)*(x-u) + (y-v)*(y-v));
}

// 定义理想高通滤波器
Eigen::MatrixXd idealHighPassFilter(int rows, int cols, float D0) {
    
    
    Eigen::MatrixXd filter = Eigen::MatrixXd::Ones(rows, cols);  // 初始化为全1矩阵
    
    int centerX = rows / 2;
    int centerY = cols / 2;

    for(int i = 0; i < rows; i++) {
    
    
        for(int j = 0; j < cols; j++) {
    
    
            if(distance(i, j, centerX, centerY) <= D0) {
    
    
                filter(i, j) = 0;
            }
        }
    }

    return filter;
}

巴特沃斯高通滤波器 (BHPF)

H ( u , v ) = 1 1 + ( D 0 D ( u , v ) ) 2 n H(u,v) = \frac{1}{1+\left(\frac{D_0}{D(u,v)}\right)^{2n}} H(u,v)=1+(D(u,v)D0)2n1

#include <iostream>
#include <Eigen/Dense>
#include <cmath>

// 计算两点之间的距离
double distance(int x, int y, int u, int v) {
    
    
    return std::sqrt((x-u)*(x-u) + (y-v)*(y-v));
}
// 定义巴特沃斯高通滤波器
Eigen::MatrixXd butterworthHighPassFilter(int rows, int cols, float D0, int n) {
    
    
    Eigen::MatrixXd filter(rows, cols);
    
    int centerX = rows / 2;
    int centerY = cols / 2;

    for(int i = 0; i < rows; i++) {
    
    
        for(int j = 0; j < cols; j++) {
    
    
            double D = distance(i, j, centerX, centerY);
            filter(i, j) = 1 / (1 + std::pow(D0/D, 2*n));
        }
    }

    return filter;
}

高斯高通滤波器 (GHPF)

H ( u , v ) = 1 − e − D 2 ( u , v ) 2 D 0 2 H(u,v) = 1 - e^{\frac{-D^2(u,v)}{2D_0^2}} H(u,v)=1e2D02D2(u,v)

#include <iostream>
#include <Eigen/Dense>
#include <cmath>

// 计算两点之间的距离
double distance(int x, int y, int u, int v) {
    
    
    return std::sqrt((x-u)*(x-u) + (y-v)*(y-v));
}
// 定义高斯高通滤波器
Eigen::MatrixXd gaussianHighPassFilter(int rows, int cols, float D0) {
    
    
    Eigen::MatrixXd filter(rows, cols);
    
    int centerX = rows / 2;
    int centerY = cols / 2;

    for(int i = 0; i < rows; i++) {
    
    
        for(int j = 0; j < cols; j++) {
    
    
            double D = distance(i, j, centerX, centerY);
            filter(i, j) = 1 - std::exp(-(D*D) / (2*D0*D0));
        }
    }

    return filter;
}

带通滤波器( BPF)

带通滤波器(Band-Pass Filter, BPF)在频域内允许一段连续的频率范围通过,同时阻挡该范围外的其他频率。在图像处理中,它常用于只保留图像中的某些特定的空间频率特性,从而抑制过低和过高的频率成分。

  • 数学公式:

在二维频域里,设 D ( u , v ) D(u,v) D(u,v)是频率坐标(u,v)到傅里叶变换中心的距离,常用的带通滤波器有以下几种:

理想带通滤波器 (Ideal BPF)

H ( u , v ) = { 1 if  D 1 ≤ D ( u , v ) ≤ D 2 0 otherwise H(u,v) = \begin{cases} 1 & \text{if } D_1 \leq D(u,v) \leq D_2 \\0 & \text{otherwise}\end{cases} H(u,v)={ 10if D1D(u,v)D2otherwise
其中, D 1 D_1 D1 D 2 D_2 D2 是内外两个截止频率,满足 D 1 < D 2 D_1 < D_2 D1<D2

#include <iostream>
#include <Eigen/Dense>

using namespace Eigen;

//**理想带通滤波器 (Ideal BPF)**:

MatrixXd idealBPF(int rows, int cols, double D1, double D2) {
    
    
    // 创建一个空的矩阵用于存放滤波器的值
    MatrixXd filter(rows, cols);

    // 计算傅里叶变换的中心
    int centerX = rows / 2;
    int centerY = cols / 2;

    for(int i = 0; i < rows; i++) {
    
    
        for(int j = 0; j < cols; j++) {
    
    
            double D = std::sqrt((i - centerX) * (i - centerX) + (j - centerY) * (j - centerY));
            filter(i, j) = (D >= D1 && D <= D2) ? 1.0 : 0.0;
        }
    }

    return filter;
}

巴特沃斯带通滤波器 (Butterworth BPF)

H ( u , v ) = 1 1 + ( D ( u , v ) D 1 D ( u , v ) 2 − D 2 2 ) 2 n H(u,v) = \frac{1}{1+\left(\frac{D(u,v)D_1}{D(u,v)^2 - D_2^2}\right)^{2n}} H(u,v)=1+(D(u,v)2D22D(u,v)D1)2n1
其中,n是滤波器的阶数。

#include <iostream>
#include <Eigen/Dense>

using namespace Eigen;

//**巴特沃斯带通滤波器 (Butterworth BPF)**:

MatrixXd butterworthBPF(int rows, int cols, double D1, double D2, int n) {
    
    
    MatrixXd filter(rows, cols);

    int centerX = rows / 2;
    int centerY = cols / 2;

    for(int i = 0; i < rows; i++) {
    
    
        for(int j = 0; j < cols; j++) {
    
    
            double D = std::sqrt((i - centerX) * (i - centerX) + (j - centerY) * (j - centerY));
            filter(i, j) = 1.0 / (1.0 + std::pow((D * D1) / (D * D - D2 * D2), 2 * n));
        }
    }

    return filter;
}

高斯带通滤波器 (Gaussian BPF)

H ( u , v ) = e − ( D 2 ( u , v ) − D 2 2 ) 2 D 1 2 D 2 ( u , v ) H(u,v) = e^{-\frac{(D^2(u,v) - D_2^2)^2}{D_1^2D^2(u,v)}} H(u,v)=eD12D2(u,v)(D2(u,v)D22)2

#include <iostream>
#include <Eigen/Dense>

using namespace Eigen;

// **高斯带通滤波器 (Gaussian BPF)**:

MatrixXd gaussianBPF(int rows, int cols, double D1, double D2) {
    
    
    MatrixXd filter(rows, cols);

    int centerX = rows / 2;
    int centerY = cols / 2;

    for(int i = 0; i < rows; i++) {
    
    
        for(int j = 0; j < cols; j++) {
    
    
            double D = std::sqrt((i - centerX) * (i - centerX) + (j - centerY) * (j - centerY));
            filter(i, j) = std::exp(-(D * D - D2 * D2) * (D * D - D2 * D2) / (D1 * D1 * D * D));
        }
    }

    return filter;
}
  • 特性:

  • 当滤波器的带宽(即 D 2 − D 1 D_2 - D_1 D2D1)较窄时,滤波器只允许一个很小的频率范围通过,可以用于某些特定的应用,如频率选择或某种频率噪声的消除。

  • 当带宽较宽时,该滤波器可以用来在一个更广的范围内保留图像细节,同时过滤掉过高和过低的频率。

带阻滤波器 (BRF)

在二维频域里,设 D ( u , v ) D(u,v) D(u,v)是频率坐标(u,v)到傅里叶变换中心的距离,常用的带阻滤波器有以下几种:

理想带阻滤波器 (Ideal BRF)

H ( u , v ) = { 0 if  D 1 ≤ D ( u , v ) ≤ D 2 1 otherwise H(u,v) = \begin{cases} 0 & \text{if } D_1 \leq D(u,v) \leq D_2 \\1 & \text{otherwise}\end{cases} H(u,v)={ 01if D1D(u,v)D2otherwise
其中, D 1 D_1 D1 D 2 D_2 D2 是内外两个截止频率,满足 D 1 < D 2 D_1 < D_2 D1<D2

#include <iostream>
#include <Eigen/Dense>

using namespace Eigen;

// 计算频率到中心的距离
double D(int u, int v, int centerU, int centerV) {
    
    
    return std::sqrt((u - centerU) * (u - centerU) + (v - centerV) * (v - centerV));
}

// 理想带阻滤波器
MatrixXd idealBRF(int rows, int cols, double D1, double D2) {
    
    
    MatrixXd filter(rows, cols);

    int centerU = rows / 2;
    int centerV = cols / 2;

    for (int u = 0; u < rows; ++u) {
    
    
        for (int v = 0; v < cols; ++v) {
    
    
            double distance = D(u, v, centerU, centerV);
            filter(u, v) = (distance >= D1 && distance <= D2) ? 0 : 1;
        }
    }

    return filter;
}

巴特沃斯带阻滤波器 (Butterworth BRF)

H ( u , v ) = 1 − 1 1 + ( D ( u , v ) D 2 D ( u , v ) 2 − D 1 2 ) 2 n H(u,v) = 1 - \frac{1}{1+\left(\frac{D(u,v)D_2}{D(u,v)^2 - D_1^2}\right)^{2n}} H(u,v)=11+(D(u,v)2D12D(u,v)D2)2n1
其中,n是滤波器的阶数。

#include <iostream>
#include <Eigen/Dense>

using namespace Eigen;

// 计算频率到中心的距离
double D(int u, int v, int centerU, int centerV) {
    
    
    return std::sqrt((u - centerU) * (u - centerU) + (v - centerV) * (v - centerV));
}

// 巴特沃斯带阻滤波器
MatrixXd butterworthBRF(int rows, int cols, double D1, double D2, int n) {
    
    
    MatrixXd filter(rows, cols);

    int centerU = rows / 2;
    int centerV = cols / 2;

    for (int u = 0; u < rows; ++u) {
    
    
        for (int v = 0; v < cols; ++v) {
    
    
            double distance = D(u, v, centerU, centerV);
            double fraction = (D1 * D2) / (distance * distance - D1 * D1);
            filter(u, v) = 1.0 / (1.0 + std::pow(fraction, 2 * n));
        }
    }

    return filter;
}

高斯带阻滤波器 (Gaussian BRF)

H ( u , v ) = 1 − e − ( D 2 ( u , v ) − D 2 2 ) 2 D 1 2 D 2 ( u , v ) H(u,v) = 1 - e^{-\frac{(D^2(u,v) - D_2^2)^2}{D_1^2D^2(u,v)}} H(u,v)=1eD12D2(u,v)(D2(u,v)D22)2

#include <iostream>
#include <Eigen/Dense>

using namespace Eigen;

// 计算频率到中心的距离
double D(int u, int v, int centerU, int centerV) {
    
    
    return std::sqrt((u - centerU) * (u - centerU) + (v - centerV) * (v - centerV));
}

// 高斯带阻滤波器
MatrixXd gaussianBRF(int rows, int cols, double D1, double D2) {
    
    
    MatrixXd filter(rows, cols);

    int centerU = rows / 2;
    int centerV = cols / 2;

    for (int u = 0; u < rows; ++u) {
    
    
        for (int v = 0; v < cols; ++v) {
    
    
            double distance = D(u, v, centerU, centerV);
            double value = std::exp(-(distance * distance - D2 * D2) * (distance * distance - D2 * D2) / (D1 * D1 * distance * distance));
            filter(u, v) = 1 - value;
        }
    }

    return filter;
}

  • 特性:

  • BRF是带通滤波器(BPF)的反向。它主要用于消除图像中某个特定频率范围内的成分。

  • 它的效果与BPF相反:BRF会抑制一个频率范围内的信号,而BPF则会允许这个范围内的信号通过。

  • 当知道噪声或不希望的成分位于特定的频率范围内时,BRF特别有用。

陷波滤波器 ( Notch Filter)

陷波滤波器(Notch Filter)是一种特殊的带阻滤波器,用于消除或减少某些特定的频率。与常规的带阻滤波器(BRF)不同,陷波滤波器针对的是非常具体的、特定的频率或频率集,而不是一个连续的频率范围。在图像处理中,它们通常用于消除某种特定的干扰,例如周期性噪声或其他已知的频率成分。

  • 数学公式:

假设 D ( u , v ) D(u,v) D(u,v)是频率坐标(u,v)到傅里叶变换中心的距离,以下是常见的陷波滤波器的定义:

理想陷波滤波器 (Ideal Notch Filter)

假设我们要消除一个特定的频率(u_0,v_0):
H ( u , v ) = { 0 if  u = u 0  and  v = v 0 1 otherwise  H(u,v) = \begin{cases} 0 & \text{if } u = u_0 \text{ and } v = v_0 \\1 & \text{otherwise }\end{cases} H(u,v)={ 01if u=u0 and v=v0otherwise 
实际上,对于陷波滤波,经常会涉及到一个小的范围而不仅仅是一个点,因此上面的定义可能会稍有扩展。

#include <Eigen/Dense>
#include <cmath>

// 计算两点之间的距离
double distance(int x, int y, int u0, int v0) {
    
    
    return std::sqrt((x - u0) * (x - u0) + (y - v0) * (y - v0));
}

// 理想陷波滤波器
Eigen::MatrixXd IdealNotchFilter(int width, int height, int u0, int v0, double D0) {
    
    
    Eigen::MatrixXd filter(width, height);

    for(int i = 0; i < width; i++) {
    
    
        for(int j = 0; j < height; j++) {
    
    
            double d = distance(i, j, u0, v0);
            if(d <= D0) {
    
    
                filter(i, j) = 0;  // 抑制特定频率
            } else {
    
    
                filter(i, j) = 1;  // 其他频率不受影响
            }
        }
    }

    return filter;
}

巴特沃斯陷波滤波器 (Butterworth Notch Filter)

H ( u , v ) = 1 1 + ( D 0 D ( u − u 0 , v − v 0 ) ) 2 n H(u,v) = \frac{1}{1+\left(\frac{D_0}{D(u-u_0,v-v_0)}\right)^{2n}} H(u,v)=1+(D(uu0,vv0)D0)2n1
其中, D 0 D_0 D0是陷波滤波器的截止频率,n是滤波器的阶数。

#include <Eigen/Dense>
#include <cmath>

// 计算两点之间的距离
double distance(int x, int y, int u0, int v0) {
    
    
    return std::sqrt((x - u0) * (x - u0) + (y - v0) * (y - v0));
}
// 巴特沃斯陷波滤波器
Eigen::MatrixXd ButterworthNotchFilter(int width, int height, int u0, int v0, double D0, int n) {
    
    
    Eigen::MatrixXd filter(width, height);

    for(int i = 0; i < width; i++) {
    
    
        for(int j = 0; j < height; j++) {
    
    
            double d = distance(i, j, u0, v0);
            filter(i, j) = 1 / (1 + std::pow(D0 / d, 2*n));
        }
    }

    return filter;
}

高斯陷波滤波器 (Gaussian Notch Filter)

H ( u , v ) = 1 − e − D 2 ( u − u 0 , v − v 0 ) D 0 2 H(u,v) = 1 - e^{-\frac{D^2(u-u_0,v-v_0)}{D_0^2}} H(u,v)=1eD02D2(uu0,vv0)

#include <Eigen/Dense>
#include <cmath>

// 计算两点之间的距离
double distance(int x, int y, int u0, int v0) {
    
    
    return std::sqrt((x - u0) * (x - u0) + (y - v0) * (y - v0));
}
// 高斯陷波滤波器
Eigen::MatrixXd GaussianNotchFilter(int width, int height, int u0, int v0, double D0) {
    
    
    Eigen::MatrixXd filter(width, height);

    for(int i = 0; i < width; i++) {
    
    
        for(int j = 0; j < height; j++) {
    
    
            double d = distance(i, j, u0, v0);
            filter(i, j) = 1 - std::exp(-d*d / (2*D0*D0));
        }
    }

    return filter;
}

  • 特性:

  • 陷波滤波器通常用于消除特定的干扰频率,而不影响其他频率。例如,在一个图像中,如果有某种周期性的干扰,可以用陷波滤波器来消除它。

  • 对于复杂的噪声模式,可能需要设计多个陷波滤波器来处理不同的干扰频率。

高频强调滤波器 (Emphasis Filters)

Emphasis Filters,也称为高频强调滤波器,是频域滤波器的一种,用于提高图像中的高频部分,从而增强图像的细节。这种滤波器常常用于图像恢复中,尤其是在那些由于某些原因导致高频部分受损的图像中。

基本思想是将低频部分乘以一个小于1的常数,而将高频部分乘以一个大于1的常数,这样可以增强图像的高频部分。

  • 数学公式:

H ( u , v ) = 1 + α ⋅ ( 1 − L ( u , v ) ) H(u,v) = 1 + \alpha \cdot (1 - L(u,v)) H(u,v)=1+α(1L(u,v))

其中:

  • H ( u , v ) H(u,v) H(u,v) 是高频强调滤波器。
  • L ( u , v ) L(u,v) L(u,v) 是一个低通滤波器,可以是理想的、巴特沃斯的或高斯的。
  • α \alpha α 是一个常数,用于控制高频强调的程度。

解释:
在这个公式中, 1 − L ( u , v ) 1 - L(u,v) 1L(u,v) 实际上是一个高通滤波器,因为它是低通滤波器的反向。因此,该公式的效果是,首先保留原始图像(通过乘以1),然后加上强调的高频部分(通过乘以 α ⋅ ( 1 − L ( u , v ) ) \alpha \cdot (1 - L(u,v)) α(1L(u,v)))。

此方法的优势是,与只使用高通滤波器相比,它能够保留图像的低频内容,同时强调高频内容。这通常可以产生更好的视觉效果,特别是在图像增强应用中。

总的来说,高频强调滤波器可以有效地增强图像的细节和边缘,同时保留图像的大体结构。

#include <iostream>
#include <Eigen/Dense>
#include <cmath>

// 使用Eigen库的矩阵数据结构
using Eigen::MatrixXd;

// 创建一个理想低通滤波器
MatrixXd createIdealLowPassFilter(int rows, int cols, double cutoff) {
    
    
    MatrixXd filter(rows, cols);
    int centerRow = rows / 2;
    int centerCol = cols / 2;

    for (int i = 0; i < rows; i++) {
    
    
        for (int j = 0; j < cols; j++) {
    
    
            double distance = std::sqrt((i - centerRow) * (i - centerRow) + (j - centerCol) * (j - centerCol));
            filter(i, j) = distance <= cutoff ? 1 : 0;
        }
    }

    return filter;
}

// 使用理想低通滤波器创建一个高频强调滤波器
MatrixXd createEmphasisFilter(int rows, int cols, double cutoff, double alpha) {
    
    
    MatrixXd lowPassFilter = createIdealLowPassFilter(rows, cols, cutoff);
    MatrixXd emphasisFilter(rows, cols);

    // 计算高频强调滤波器
    for (int i = 0; i < rows; i++) {
    
    
        for (int j = 0; j < cols; j++) {
    
    
        
            //低频中心本为1 ,这样就变成 1+0 = 1;而高频变成 1+ a*(1-0) >1;增强了高频,低频不变
            emphasisFilter(i, j) = 1 + alpha * (1 - lowPassFilter(i, j));
        }
    }

    return emphasisFilter;
}

在这里插入图片描述
左图为原图,右图为增强后的图像,(alpha =0.5)可以发现显示比较更清晰。

  • 特性:
  1. 高频增强:Emphasis Filters 主要用于强调图像的高频部分,即细节和边缘。

  2. 可调性:通过调整 α \alpha α 的值,可以调节高频部分增强的程度。

  3. 与低通滤波器的结合:此滤波器实际上是将低通滤波器和一个常数相结合,用于强调高频而不是低频。

低通滤波器和高通滤波器的完整实现



#include <opencv2/opencv.hpp> //OpenCV库只用到Mat用于读取和显示图像
#include <Eigen>
#include <iostream>
#include <vector>
#include <cmath>
#include <complex>

#define M_PI       3.14159265358979323846   // pi
// 对数幅度缩放
Eigen::MatrixXd logAmplitudeSpectrum(const Eigen::MatrixXd& spectrum) {
    
    
    return (spectrum.array() + 1).log();
}

//  乘幂尺度变换
Eigen::MatrixXd powerLawScaling(const Eigen::MatrixXd& spectrum, double gamma) {
    
    
    return spectrum.array().pow(gamma);
}

// 归一化 to [0, 1]
Eigen::MatrixXd normalize(const Eigen::MatrixXd& spectrum) {
    
    
    double minVal = spectrum.minCoeff();
    double maxVal = spectrum.maxCoeff();
    return (spectrum.array() - minVal) / (maxVal - minVal);
}

// 增强频谱显示
Eigen::MatrixXd enhanceSpectrumDisplay(const Eigen::MatrixXd& spectrum, double gamma = 1) {
    
    
    Eigen::MatrixXd logSpectrum = logAmplitudeSpectrum(spectrum);
    Eigen::MatrixXd powerScaledSpectrum = powerLawScaling(logSpectrum, gamma);
    return normalize(powerScaledSpectrum);
}


// 从复数矩阵中获取幅度谱和相位谱
void getAmplitudeAndPhaseSpectra(const Eigen::MatrixXcd& data, Eigen::MatrixXd& amplitude, Eigen::MatrixXd& phase) {
    
    

    amplitude = data.array().abs().matrix();
    phase = data.array().arg().matrix();
}

// 从幅度谱和相位谱重构复数矩阵
void reconstructFromAmplitudeAndPhase(const Eigen::MatrixXd& amplitude,
    const Eigen::MatrixXd& phase,
    Eigen::MatrixXcd& data)
{
    
    

    data = (amplitude.array() * (phase.array().cos() + std::complex<double>(0, 1) * phase.array().sin())).matrix();


}


// 1:**振幅谱低频移动到中心(频率平移)**:方便操作,利用象限对称互换 fft之后
Eigen::MatrixXd fftShift(const Eigen::MatrixXd &F)
{
    
    
    int M = F.rows();
    int N = F.cols();
    Eigen::MatrixXd F_shifted(M, N);

    int mid_M = M >> 1;
    int mid_N = N >> 1;

    // 交换第一象限和第三象限
    F_shifted.block(0, 0, mid_M, mid_N) = F.block(mid_M, mid_N, mid_M, mid_N);
    F_shifted.block(mid_M, mid_N, mid_M, mid_N) = F.block(0, 0, mid_M, mid_N);
    // 交换第二象限和第四象限
    F_shifted.block(0, mid_N, mid_M, mid_N) = F.block(mid_M, 0, mid_M, mid_N);
    F_shifted.block(mid_M, 0, mid_M, mid_N) = F.block(0, mid_N, mid_M, mid_N);

    return F_shifted;
}

// 2:振幅谱低频移动到中心  **图像进行-1幂操作**:然后经过fft变换后,低频会在振幅谱中间 fft之前
Eigen::MatrixXd imageShift(const Eigen::MatrixXd &image)
{
    
    
    int M = image.rows();
    int N = image.cols();
    Eigen::MatrixXd F_shifted(M, N);

    // 通过乘以 (-1)^(u+v) 来平移频率
    for (int u = 0; u < M; ++u)
    {
    
    
        for (int v = 0; v < N; ++v)
        {
    
    
            //(u + v) & 1 通过位的判断末尾如果是1 为奇数,为0,为偶数。 -1的奇次幂还是-1,偶次幂为1.
            F_shifted(u, v) = image(u, v) * ((u + v) & 1 ? -1 : 1);
        }
    }


    return F_shifted;
}

Eigen::MatrixXd tMatrixXd(const cv::Mat &img)
{
    
    
    Eigen::MatrixXd image(img.rows, img.cols);
    for (int i = 0; i < img.rows; ++i)
    {
    
    
        for (int j = 0; j < img.cols; ++j)
        {
    
    
            image(i, j) = img.at<float>(i, j);
        }
    }
    return image;
}
cv::Mat tMat(const Eigen::MatrixXd &img)
{
    
    
    cv::Mat image(img.rows(), img.cols(), CV_32FC1);
    for (int i = 0; i < img.rows(); ++i)
    {
    
    
        for (int j = 0; j < img.cols(); ++j)
        {
    
    
            image.at<float>(i, j) = img(i, j);
        }
    }
    return image;
}




//inv =true 为逆变换,false为 正变换
std::vector<std::complex<double>> FFT(const std::vector<std::complex<double>>& y, bool inv = false)
{
    
    
    std::vector<std::complex<double>>x = y;
    //数据的大小
    int N = x.size();

    //    //数据以2为底的指数
    //    double logD = std::log2(N);
    //    int logN = static_cast<int>(logD);
    //    //判断logD 是否为正整数,否则数据大小不是2的幂数,就改变数据的大小为2的幂数 ,并大于原来的数据大小。
    //    if(logD > logN)
    //    {
    
    
    //        // 确保输入的大小是2的幂
    //        N = 1;
    //        N <<= (logN+1);
    //        x.resize(N,std::complex<double>(0, 0)); // 使用零填充
    //    }

        // 确保输入的大小是2的幂
    int nextPower = 1;
    while (nextPower < N)
    {
    
    
        nextPower <<= 1;
    }
    if (nextPower != N)
    {
    
    
        N = nextPower;
        x.resize(N, std::complex<double>(0, 0)); // 使用0填充到下一个2的幂
    }


    // 蝴蝶反转
    for (int i = 1, j = 0; i < N; i++)
    {
    
    
        //bit = N/2
        int bit = N >> 1;
        /*
         * 我们正在查看j的二进制表示中的每一位。
            从左到右,我们检查每一位是否为1。
            对于每一个为1的位,我们将其反转为0。
            当我们遇到第一个为0的位时,循环终止。
        */
        for (; j & bit; bit >>= 1)
        {
    
    
            j ^= bit;
        }
        //进行异或运算(XOR运算的工作原理是:当两个比较的位相同时,结果是0;当两个比较的位不同时,结果是1。)
        j ^= bit;

        //当 i >j 表示前面已经替换了位置, i==j,表示位置不用变
        if (i < j)
        {
    
    
            std::swap(x[i], x[j]);
        }
    }


    // 预先计算旋转因子
    std::vector<std::complex<double>> w(N / 2);
    //逆变换 = 1;傅里叶变换  = -1;
    double imag_i = inv ? 1.0 : -1.0;
    std::complex<double> tempW = std::exp(std::complex<double>(0, imag_i * 2 * M_PI / N));

    w[0] = 1;
    for (int i = 1; i < (N >> 1); ++i)
    {
    
    
        // w[i] = std::polar(1.0, imag_i * 2 * M_PI * i / N);
        //w[i] = std::pow(tempW, i);
        w[i] = w[i - 1] * tempW;
    }

    // 迭代FFT
    for (int len = 2; len <= N; len <<= 1)
    {
    
    
        int halfLen = len >> 1;
        int step = N / len;
        for (int i = 0; i < N; i += len)
        {
    
    
            for (int j = 0; j < halfLen; ++j)
            {
    
    
                std::complex<double> u = x[i + j];
                std::complex<double> v = x[i + j + halfLen] * w[j * step];
                x[i + j] = u + v;
                x[i + j + halfLen] = u - v;
            }
        }
    }

    //使用逆变换时
    if (inv)
    {
    
    
        for (std::complex<double>& a : x) {
    
    
            a /= N;
        }
    }
    return x;
}

//检查一维数据大小是否为2的幂数,不是则填充为0到2的幂数大小
Eigen::VectorXd padToPowerOfOne(const Eigen::VectorXd& vector) {
    
    
    int size = vector.rows();

    // 确保输入的大小是2的幂
    int nextPower = 1;
    while (nextPower < size)
    {
    
    
        nextPower <<= 1;
    }

    // 使用0填充到下一个2的幂
    Eigen::VectorXd paddedMatrix = Eigen::VectorXd::Zero(nextPower);
    paddedMatrix.block(0, 0, size, 1) = vector;

    return paddedMatrix;
}

//FFT 使用Eigen库中的向量表示,方便二维计算
Eigen::VectorXcd FFT(const Eigen::VectorXcd& y, bool inv = false)
{
    
    
    Eigen::VectorXcd x = y;
    //数据的大小
    int N = x.size();


    // 按位反转
    for (int i = 1, j = 0; i < N; i++)
    {
    
    
        //bit = N/2
        int bit = N >> 1;
        /*
         * 我们正在查看j的二进制表示中的每一位。
            从左到右,我们检查每一位是否为1。
            对于每一个为1的位,我们将其反转为0。
            当我们遇到第一个为0的位时,循环终止。
        */
        for (; j & bit; bit >>= 1)
        {
    
    
            j ^= bit;
        }
        //进行异或运算(XOR运算的工作原理是:当两个比较的位相同时,结果是0;当两个比较的位不同时,结果是1。)
        j ^= bit;

        //当 i >j 表示前面已经替换了位置, i==j,表示位置不用变
        if (i < j)
        {
    
    
            std::swap(x[i], x[j]);
        }
    }


    // 预先计算旋转因子
    Eigen::VectorXcd w(N >> 1);
    //逆变换 = 1;傅里叶变换  = -1;
    double imag_i = inv ? 1.0 : -1.0;
    std::complex<double> tempW = std::exp(std::complex<double>(0, imag_i * 2 * M_PI / N));

    w[0] = 1;
    for (int i = 1; i < (N >> 1); ++i)
    {
    
    
        // w[i] = std::polar(1.0, imag_i * 2 * M_PI * i / N);
        //w[i] = std::pow(tempW, i);
        w[i] = w[i - 1] * tempW;
    }

    // 迭代FFT
    for (int len = 2; len <= N; len <<= 1)
    {
    
    
        int halfLen = len >> 1;
        int step = N / len;
        for (int i = 0; i < N; i += len)
        {
    
    
            for (int j = 0; j < halfLen; ++j)
            {
    
    
                std::complex<double> u = x[i + j];
                std::complex<double> v = x[i + j + halfLen] * w[j * step];
                x[i + j] = u + v;
                x[i + j + halfLen] = u - v;
            }
        }
    }

    //使用逆变换时
    if (inv)
    {
    
    
        for (std::complex<double>& a : x) {
    
    
            a /= N;
        }
    }
    return x;
}

//检查二维数据大小是否为2的幂数,不是则填充为0到2的幂数大小
Eigen::MatrixXd padToPowerOfTwo(const Eigen::MatrixXd& matrix) {
    
    
    int rows = matrix.rows();
    int cols = matrix.cols();

    // 确保输入的大小是2的幂
    int newRows = 1, newCols = 1;
    while (newRows < rows)
    {
    
    
        newRows <<= 1;
    }
    while (newCols < cols)
    {
    
    
        newCols <<= 1;
    }

    Eigen::MatrixXd paddedMatrix = Eigen::MatrixXd::Zero(newRows, newCols);
    paddedMatrix.block(0, 0, rows, cols) = matrix;

    return paddedMatrix;
}
// 离散傅里叶变换 -  二维
Eigen::MatrixXcd FFT2D(const Eigen::MatrixXcd& image, bool inv = false) {
    
    
    int rows = image.rows();
    int cols = image.cols();
    Eigen::MatrixXcd result(rows, cols);

    for (int i = 0; i < rows; i++) {
    
    
        Eigen::VectorXcd temp = image.row(i).transpose();
        result.row(i) = FFT(temp, inv);
    }

    for (int j = 0; j < cols; j++) {
    
    
        Eigen::VectorXcd temp = result.col(j);
        result.col(j) = FFT(temp, inv);
    }

    return result;
}



//创建高频  radius越小,越减少高频的部分,越大,越还原图像
Eigen::MatrixXd highFrequency (const Eigen::MatrixXd & data,int radius)
{
    
    

    int rows = data.rows();
    int cols = data.cols();

    // 创建高通滤波器(圆形掩码)
    Eigen::MatrixXd mask = Eigen::MatrixXd::Ones(rows, cols);

    //中心坐标
    int centerRow = rows >> 1;
    int centerCol = cols >> 1;


    //找到以 radius 大小的矩形范围内

    //左边位置
    int left = centerCol - radius;
    //超过边界 为0
    left = left > 0 ? left : 0;

    //右边位置
    int right = centerCol + radius;
    //超过边界 为0
    right = right < cols ? right : cols;

    //上边位置
    int top = centerRow - radius;
    //超过边界 为0
    top = top > 0 ? top : 0;

    //下边位置
    int down = centerRow + radius;
    //超过边界 为0
    down = down < rows ? down : rows;

    //在正矩形内画最大的圆
    for (int i = top; i < down; ++i)
    {
    
    
        for (int j = left; j < right; ++j)
        {
    
    
            //在图像中心画圆,半径不能超过
            double distance = std::sqrt(std::pow(i - centerRow, 2) + std::pow(j - centerCol, 2));
            if (distance <= radius)
            {
    
    
                mask(i, j) = 0.0;
            }
        }
    }
    return mask;

}
//创建高通滤波器 -
//简单的说,就是靠近频谱图中心的低频部分给舍弃掉,远离频谱图中心的高频部分保留。通常会保留物体的边界。
Eigen::MatrixXd  highPassFilter(const Eigen::MatrixXd & image, int radius = 0)
{
    
    
    int rows = image.rows();
    int cols = image.cols();

    //大小变为2的幂数
    Eigen::MatrixXd data = padToPowerOfTwo(image);
    // 计算傅里叶变换
    Eigen::MatrixXcd transformed = FFT2D(data);


    //获取幅度谱和相位谱
    Eigen::MatrixXd amplitude, phase;
    getAmplitudeAndPhaseSpectra(transformed, amplitude, phase);

    //振幅谱移动到中心(频率平移)
    amplitude = fftShift(amplitude);

    ///
    //增强振幅,用于观测 --  实际运算注释掉
    Eigen::MatrixXd amplitude1 = enhanceSpectrumDisplay(amplitude,1);
    cv::Mat highP = tMat(amplitude1);
    cv::imshow("highPassFilter",highP);
    ///

    //根据radius创建高频掩码
    Eigen::MatrixXd mask = highFrequency(amplitude, radius);
    amplitude = amplitude.array() * mask.array();


    //振幅谱移动到中心(频率平移)反转换
    amplitude = fftShift(amplitude);
    // 从幅度谱和相位谱重构复数矩阵
    reconstructFromAmplitudeAndPhase(amplitude, phase, transformed);


    // 计算逆变换
    Eigen::MatrixXcd reconstructed = FFT2D(transformed, true);

    return reconstructed.real().block(0, 0, rows, cols);
}
//创建低频  radius越小,越还原图像,越大,减少低频的部分,
Eigen::MatrixXd lowFrequency(const Eigen::MatrixXd & data,int radius)
{
    
    

    int rows = data.rows();
    int cols = data.cols();
    // 创建低通滤波器(圆形掩码)
    Eigen::MatrixXd mask = Eigen::MatrixXd::Zero(rows, cols);

    //中心坐标
    int centerRow = rows >> 1;
    int centerCol = cols >> 1;


    //找到以 radius 大小的矩形范围内

    //左边位置
    int left = centerCol - radius;
    //超过边界 为0
    left = left > 0 ? left : 0;

    //右边位置
    int right = centerCol + radius;
    //超过边界 为0
    right = right < cols ? right : cols;

    //上边位置
    int top = centerRow - radius;
    //超过边界 为0
    top = top > 0 ? top : 0;

    //下边位置
    int down = centerRow + radius;
    //超过边界 为0
    down = down < rows ? down : rows;

    //在正矩形内画最大的圆
    for (int i = top; i < down; ++i)
    {
    
    
        for (int j = left; j < right; ++j)
        {
    
    
            //在图像中心画圆,半径不能超过
            double distance = std::sqrt(std::pow(i - centerRow, 2) + std::pow(j - centerCol, 2));
            if (distance <= radius)
            {
    
    
                mask(i, j) = 1.0;
            }
        }
    }
    return mask;

}
//创建低通滤波器 -
//简单的说,就是靠近频谱图中心的低频部分给保留,远离频谱图中心的高频部分给去除掉。但是这会影响图像的清晰度。
Eigen::MatrixXd  lowPassFilter(const Eigen::MatrixXd & image, int radius = 0)
{
    
    
    int rows = image.rows();
    int cols = image.cols();

    //大小变为2的幂数
    Eigen::MatrixXd data = padToPowerOfTwo(image);
    // 计算傅里叶变换
    Eigen::MatrixXcd transformed = FFT2D(data);

    //获取幅度谱和相位谱
    Eigen::MatrixXd amplitude, phase;
    getAmplitudeAndPhaseSpectra(transformed, amplitude, phase);


    //振幅谱移动到中心(频率平移)
    amplitude = fftShift(amplitude);


    ///
//    //增强振幅,用于观测 --  实际运算注释掉
//    Eigen::MatrixXd amplitude1 = enhanceSpectrumDisplay(amplitude,1);
//    cv::Mat highP = tMat(amplitude1);
//    cv::imshow("lowPassFilter",highP);
    ///

    //根据radius创建高频掩码
    Eigen::MatrixXd mask = lowFrequency(amplitude, radius);
    amplitude = amplitude.array() * mask.array();


    //振幅谱移动到中心(频率平移)反转换
    amplitude = fftShift(amplitude);

    // 从幅度谱和相位谱重构复数矩阵
    reconstructFromAmplitudeAndPhase(amplitude, phase, transformed);


    // 计算逆变换
    Eigen::MatrixXcd reconstructed = FFT2D(transformed, true);

    return reconstructed.real().block(0, 0, rows, cols);
}



int main()
{
    
    

    cv::Mat img = cv::imread("193560523230866.png");
    if (img.empty())
    {
    
    
        std::cout << "请确定是否输入正确的图像文件" << std::endl;

    }
    cv::Mat gray;
    cvtColor(img, gray, cv::COLOR_BGR2GRAY);
    //图像转换CV_32F储存
    gray.convertTo(gray, CV_32F, 1 / 255.0, 0);

    //图像太大,用直接计算 耗时太长,缩小比例
    //resize(gray, gray, cv::Size(80, 80));

    //Mat  转 MatrixXd
    Eigen::MatrixXd image = tMatrixXd(gray);

    // 记录开始时间
    auto start = std::chrono::high_resolution_clock::now();

    //低频
    Eigen::MatrixXd low = lowPassFilter(image,50);
    //高频
    Eigen::MatrixXd high = highPassFilter(image, 50);
    // 记录结束时间
    auto stop = std::chrono::high_resolution_clock::now();
    // 计算持续时间
    auto duration = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
    qDebug() << "代码运行时长: " << duration.count() << " 微秒" ;

     cv::Mat lowP = tMat(low);
    //+0.5 增加显示效果
    high = high.array() + 0.5;
    cv::Mat highP = tMat(high);

    cv::imshow("lowP",lowP);
    cv::imshow("highP",highP);

	return 0;
}

在这里插入图片描述

在这里插入图片描述

  • 振幅增强
    在这里插入图片描述
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43763292/article/details/132503013