傅里叶变换基本概念及复数类实现

        最近对图像处理算法比较感兴趣,也看了一些数字图像处理相关书籍,自己也实现过一些简单的图像处理算法。随着了解的深入,发现要真正理解图像处理各种骚操作,绕不开基于傅里叶变换的各种频率域滤波。对于lz这种数学渣来说,看到各种数学公式推导,简直令人头大,通过傅里叶变换把直观图像转换为抽象的频谱图更是让人难以理解。

        上面是一张傅里叶变换后的频谱图,如果不了解傅里叶变换,很难从上图中分析出原图包含的一些特征。研究了一段时间傅里叶变换,按照lz的理解,其实图像的傅里叶变换就是根据空间位置对图像灰度值进行拆分,分离出从高到低不同频率,频谱图就是展示一张图像从低频(频谱中心,图像平均灰度)到高频(远离频谱中心,对应灰度变化大的区域)的分布。而频率域滤波则是根据图像的频率特征,采用不同滤波器,理想低通、高通,高斯低通、高通等,对频率域的图像数据进行处理,之后通过逆傅里叶变换还原出滤波后的图像。

        冈萨雷斯的《数字图像处理》第四章节用了将近70页的篇幅对傅里叶变换进行了逐步讲解,同时也表明了频率域滤波在图像滤波中的重要性。虽然是图像处理的业余爱好者,lz还是决定有必要把傅里叶变换尽量搞清楚一些。因为没有接触过MATLAB和OpenCV(暂时也不打算使用第三方工具),所以文章所涉及到的代码都使用lz比较熟悉的java来实现。

        为了更好的理解傅里叶变换,lz将傅里叶变换从一些基本概念到应用拆分为不同层次,基本概念及推导、一维离散傅里叶变换、快速傅里叶变换、二维傅里叶变换、频率域滤波。这里就对傅里叶变换的一些基本概念做个总结。

        傅里叶变换涉及的一些基本概念:复数、欧拉公式、傅里叶公式、取样和卷积。

        一、复数

        复数的形式:C = a + jb

        a为实部,jb为虚部,其中b为实数,j=\sqrt{-1},为虚数。复数也可以理解为一个二维平面上的点,实部为x轴,虚部为y轴。

        复数的共轭复数:C’ = a - jb,实部相同,虚部符号相反。

        复数满足四则运算法则

        例如:C’’ = c + jd;

        复数相加:C + C’’ = (a + c) + j(b+d)

        复数相减:C - C” = (a - c) + j(b - d)

        复数相乘:C * C” = (a + jb) * (c + jd) = a*c - bd + j(ad + bc)

        复数相除:C / C” = (a + jb)/(c + jd) = (a + jb)*(c - jd)/(c + jd)(c - jd)

                                    =[(ac + bd) - j(bc - ad)] / (c2 + d2)

       复数的模长:C = a + jb模长为|C| =\sqrt{a^{2}+b^{^{2}}}

       根据三角函数,复数可变换为:复数可变换为:C = |C|(cos\theta +sin\theta)形式,实际上在编程中傅里叶变换也是通过这种形式进行计算的。

        二、欧拉公式

        e^{j\theta }=cos\theta +jsin\theta

        傅里叶变换公式也就是通过欧拉公式变换为方便计算的复数形式。

       三、傅里叶变换公式

      傅里叶变换公式:F(\mu )=\int_{-\infty }^{\infty }f(t)e^{-j2\pi \mu t}d_{t}  

      傅里叶反变换公式:f(t )=\int_{-\infty }^{\infty }F(\mu)e^{j2\pi \mu t}d_{\mu}

      以上两个公式就是傅里叶变换和逆变换,但这是在连续函数上的变换,我们实际使用的是离散函数的傅里叶变换。

      傅里叶变换公式的简单推导过程:

        例:常数函数f(t)=A ,在t\epsilon [-W/2,W/2]有限区间内,傅里叶公式可做如下变换:

        F(\mu )=\int_{-W/2 }^{W/2 }f(t)e^{-j2\pi \mu t}d_{t}

                  =\frac{-A}{j2\pi \mu }[e^{-j2\pi \mu t}]_{W/2}^{W/2}

                  =\frac{-A}{j2\pi \mu }[e^{-j\pi \mu W}-e^{j\pi \mu W}]

                  =\frac{A}{j2\pi \mu }[e^{j\pi \mu W}-e^{-j\pi \mu W}]

        根据欧拉公式

            e^{j\pi \mu W}=cos(\pi \mu W)+jsin(\pi \mu W)

        上面公式可以做进一步变换

        F(\mu )=\frac{A}{j2\pi \mu }[cos(\pi \mu W)+jsin(\pi \mu W)-cos(\pi \mu W)+jsin(\pi \mu W)]

                  =\frac{A}{\pi \mu }sin(\pi \mu W)

                  =\frac{AW}{\pi \mu W}sin(\pi \mu W)

            上式结果也可由三角恒等式直接变换过来:

            三角恒等式:sin\theta =(e^{j\theta }-e^{-j\theta })/2j

            通常,为了显示,需要处理幅值,变换为傅里叶谱或频谱:

            |F(\mu )|=AW|\tfrac{sin(\pi \mu W)}{\pi \mu W}|

        四、取样和卷积

               上面所讲的傅里叶公式及推导都是在连续函数上进行的,而要实现计算机能够处理的数据必须是离散的信号,所以这就           涉及到取样。连续的数字信号,例如音频、电磁波信号等,需要对原始信号采样后再进行处理,而图像处理方面,可                   以把每一个像素上的灰度值看做已经采样后的数据,我们只需要把图像数据作为离散的信号输入即可。

                至于卷积,做过图像空间域滤波以后不难理解,图像的傅里叶变换,是以整个图像所有对图像上每个像素进行傅里叶计           算,也就是说以整个图像作为一个周期,计算不同频率在某个像素位置上的叠加。那么一幅像素数量为N的图像,需要进             行的离散傅里叶变换计算次数为N * N。例如,对一幅2000万像素的图片做傅里叶变换计算,需要计算2000万 * 2000万                 次,这对超级计算机来说也是一个不小的挑战,于是有了快速傅里叶变换,把计算量降低为N * log2N,这才使傅里叶变换           有了实用价值,至于快速傅里叶变换,后面再详细讲。

        五、复数类的实现

               上面只是对傅里叶变换概念及公式做了个简单的讲解,其实从代码上实现傅里叶变换并没有那么复杂。由于傅里叶变换          是在复数基础上进行计算的,接下来就迈出傅里叶变换的第一步,复数的代码实现。

          附上java代码:

package FourierTransform;

/**
 * 复数类
 * @author admin
 *
 */
public class Complex {
	
	private double a; //复数实部
	private double b; //复数虚部
	
	//创建一个复数类实例
	public Complex(double a,double b) {
		this.a = a;
		this.b = b;
	}
	
	//获取复数实部
	public double getRealPart() {
		return this.a;
	}
	
	//获取复数虚部
	public double getImaginePart() {
		return this.b;
	}
	
	//对复数重新赋值
	public void changeVaule(double a, double b) {
		this.a = a;
		this.b = b;
	}
	
	//共轭复数
	public Complex conjugate() {
		return new Complex(this.a,-this.b);
	}
	
//计算复数模长
	public double absValue() {
		return Math.sqrt(this.a * this.a + this.b * this.b);
	}
	
	//复数加运算,实部与实部相加,虚部与虚部相加
	public Complex add(Complex C) {
		return new Complex(this.a + C.a, this.b + C.b);
	}
	
	//复数相减
	public Complex minus(Complex C) {
		return new Complex(this.a - C.a, this.b - C.b);
	}
	
	/**
	 * 复数相乘
	 * a*a1+a*jb1+jb*a1*-b*b1
	 * @param C
	 * @return
	 */
	public Complex multiply(Complex C) {
		double A = this.a * C.a - this.b * C.b;
		double B = this.a * C.b + this.b * C.a;
		return new Complex(A, B);
	}
	
	/**
	 * 复数相除
	 * (a + jb) / (c + jd) = [(a + jb) * (c - jd)] / [(c + jd) * (c - jd)]
	 * =[(ac + bd) + j(bc - ad)] / [(c^2 + d^2)]
	 * @param C
	 * @return
	 */
	public Complex divide(Complex C) {
		if (C.a == 0 && C.b == 0) {
			System.out.println("被除数不能为空!");
			return null;
		}
		double A = (this.a * C.a + this.b * C.b) / (C.a * C.a + C.b * C.b);
		double B = (this.b * C.a - this.a * C.b) / (C.a * C.a + C.b * C.b);
		return new Complex(A,B);
	}
	
	//toString方法
	public String toString() {
		if (a == 0 && b == 0) {
			return "复数为空!";
		}else if (a == 0) {
				return this.b + "j";
		}else if (b == 0) {
				return a + "";
		}else if (b > 0) {
				return this.a + "+" + this.b + "j";
		}else{
			return this.a + "" + this.b + "j";
		}
	}
	//测试
	public static void main(String[] args) {
		Complex c1 = new Complex(1, 2);
		System.out.println(c1.toString());
		Complex c2 = c1.conjugate();
		System.out.println(c2.toString());
		System.out.println(c1.absValue());
	}
}

 

 

 

 

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TheMatrixs

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值