数字图像处理(8):实现FFT快速算法(C语言)

1. 实验内容

1.1 使用平台及语言

使用平台:VS2015

语言:C语言

1.2 代码流程

在这里插入图片描述

其中,对频谱图像进行了一三、二四象限的对调,这样便于观察分析。

1.3 FFT、IFFT

对图像进行操作要进行两次,先对行进行FFT,再对列进行FFT,顺序反过来也可以。

FFT最主要的是蝶形运算

    /*蝶形运算*/
    for (k = 0; k<power; k++)
    {
        for (j = 0; j<1 << k; j++)
        {
            bfsize = 1 << (power - k);
            for (i = 0; i<bfsize / 2; i++)
            {
                p = j*bfsize;
                X2[i + p] = Add(X1[i + p], X1[i + p + bfsize / 2]);
                X2[i + p + bfsize / 2] = Mul(Sub(X1[i + p],X1[i + p + bfsize / 2]), W[i*(1 << k)]);
            }
        }
        X = X1;
        X1 = X2;
        X2 = X ;
    }

IFFT计算过程:

数据取共轭,然后fft,结果再取共轭后除以N

void IFFT(COMPLEX *FD, COMPLEX *TD, int power)
{
    int i, count;
    COMPLEX *x;
    /*计算傅里叶反变换点数*/
    count = 1 << power;
    /*分配运算所需存储器*/
    x = (COMPLEX *)malloc(sizeof(COMPLEX)*count);
    /*将频域点写入存储器*/
    memcpy(x, FD, sizeof(COMPLEX)*count);
    /*求频域点的共轭*/
    for (i = 0; i<count; i++)
    {
        x[i].im = -x[i].im;
    }
    /*调用快速傅里叶变换*/
    FFT(x, TD, power);
    /*求时域点的共轭*/
    for (i = 0; i<count; i++)
    {
        TD[i].re /= count;
        TD[i].im = -TD[i].im / count;
    }
    /*释放存储器*/
    free(x);
}

2. 实验结果

2.1 输入图片及其频谱

在这里插入图片描述
在这里插入图片描述

2.2 进行低频滤波

将频谱图中心20*20区域置零,进行IFFT变换,得到结果
在这里插入图片描述
分析:得到的结果比较恐怖,和预期不符。怀疑是把直流分量置零的原因。

2.3 去除直流分量

对频谱中心2*2置0(即去除直流成分)结果
在这里插入图片描述

2.4 低频滤波

进行低频滤波,将频谱图中心20*20区域置零(除了中间的直流成分),进行IFFT变换,得到结果
在这里插入图片描述
分析:这是高通滤波,低频成分被去除。图片的效果很明显,变化不大的地方被抑制,留下的都是变化快的地方、边缘部分(高频部分)。

2.5 高频滤波

进行高频滤波,除了频谱中心300*300区域其他置零,进行IFFT变换,得到结果
在这里插入图片描述

分析:这是低通滤波,高频成分被去除。图片里变化快的地方(树的纹理、博雅塔受到影响)。

2.6 进一步的高频率波

上个效果不是很明显,重新进行高频滤波,除了频谱中心200*200区域其他置零,进行IFFT变换,得到结果
在这里插入图片描述

分析:对比博雅塔部分——此次的结果和上次没有太大差别,只是云那儿受到影响多了一点。怀疑是加的这点效果对博雅塔没什么效果(因为这儿本来就高频比较高吧)
在这里插入图片描述

2.7 更进一步的高频滤波

上个效果加强其实也不是很明显,重新进行高频滤波,除了频谱中心100*100区域其他置零,进行IFFT变换,得到结果
在这里插入图片描述

分析:没想到这么丑,感觉这次的结果可以和高频滤波结果对比观看——
在这里插入图片描述

很明显二者重点的区域完全相反。

3. 遇到的问题及收获

3.1 问题一

最开始有一次遇到了这个问题(堆栈溢出问题),遇过这个坑,所以解决得比较顺利——
在这里插入图片描述

这个问题的定位在于最开始定义了数组

    COMPLEX td[height*width];
    COMPLEX fd[height*width];

其中

typedef struct
{
        double re;
        double im;
}COMPLEX;

图像大小是512*512的,这么一算,这两句需要的堆栈就是
2 * 512 * 512 * 2 * 8Bit(size of double) = 8M

解决方法有两种:一是设置堆栈增大容量;二是开辟内存存放数据。我用的第二种。即



td =(COMPLEX*)malloc(height*width*sizeof(COMPLEX));
if (td == NULL)
    return -1;
fd = (COMPLEX*)malloc(height*width*sizeof(COMPLEX));
if (fd == NULL)
    return -1;

3.2 问题二

关于频谱图归一化问题,在显示频谱图时,尝试进行了归一化至0~255

temp = (temp - min) * 255.0 / (max - min);

但是效果很不好——
在这里插入图片描述

如图,只有中间有个白点,别的看不出来。不得已将temp的值乘以100后才能看到比较好的效果。
在这里插入图片描述

感觉这一问题应该是老师上课讲的直流分量数值太大的原因。致使即使归一化也还是不能很好的展现高频分量。

3.3 问题三

进行IFFT变换后,保存图像时想当然的将复数的幅度的值给了图像,代码如下——

double temp = sqrt((td[i * width + j].re) * (td[i * width + j].re) + (fd[i * width + j].im) * (fd[i * width + j].im));
Pic[i][j] = (unsigned char)temp;

未对频域进行处理,只对图像进行FFT和IFFT,输出结果如下
在这里插入图片描述

更改代码,只将实部赋值给图像

Pic[i][j] = (unsigned char)td[i * width + j].re;

效果正常
在这里插入图片描述

附代码:

main.c

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include "fft_ifft.h"
#include <math.h>

#define height  512  
#define width   512 
#define LOW_PASS 1   //是否为低通
#define DEGREE   150     //滤波程度
typedef unsigned char  BYTE;    // 定义BYTE类型,占1个字节  

int main(void)
{
    FILE *fp = NULL;
    //  BYTE Pic[height][width];
    BYTE *ptr;
    BYTE **Pic = new BYTE *[height];
    for (int i = 0; i != height; ++i)
    {
        Pic[i] = new BYTE[width];
    }
    int i, j;
    double max = 0;
    double min = 255;
    COMPLEX *td = NULL;
    COMPLEX *fd = NULL;
    //COMPLEX td[height*width];
    //COMPLEX fd[height*width];
    td = (COMPLEX*)malloc(height*width*sizeof(COMPLEX));
    if (td == NULL)
        return -1;
    fd = (COMPLEX*)malloc(height*width*sizeof(COMPLEX));
    if (fd == NULL)
        return -1;
    fp = fopen("weiminglake512x512.raw", "rb");
    ptr = (BYTE*)malloc(width * height * sizeof(BYTE));//创建内存
    for (i = 0; i < height; i++)
    {
        for (j = 0; j < width; j++)
        {
            fread(ptr, 1, 1, fp);
            Pic[i][j] = *ptr;  // 把图像输入到2维数组中,变成矩阵型式 
            td[i * width + j].re = *ptr;
            td[i * width + j].im = 0.0;
            ptr++;
        }
    }
    fclose(fp);
    FFT2(td, height, width, fd);

    for (i = 0; i < height; i++)
    {
        for (j = 0; j < width; j++)
        {
            double temp = sqrt((fd[i * width + j].re / (height*width)) * (fd[i * width + j].re / (height*width)) + (fd[i * width + j].im / (height*width)) * (fd[i * width + j].im / (height*width)));
            if (temp > max)
                max = temp;
            if (temp < min)
                min = temp;
        }
    }
    //显示频谱
    for (i = 0; i < height; i++)
    {
        for (j = 0; j < width; j++)
        {
            double temp = sqrt((fd[i * width + j].re / (height*width)) * (fd[i * width + j].re / (height*width)) + (fd[i * width + j].im / (height*width)) * (fd[i * width + j].im / (height*width)));
            temp = (temp - min) * 25500.0 / (max - min);
            Pic[i][j] = (unsigned char)temp;
            //printf("%f\t", temp);
        }
    }
    //频谱更改位置
    //二四象限置换位置
    for (i = 0; i < height / 2;i++)
    {
        for (j = 0; j < width / 2; j++)
        {
            unsigned char t = Pic[i][j];
            Pic[i][j] = Pic[height/2 + i][width/2 + j];
            Pic[height / 2 + i][width / 2 + j] = t;
        }
    }
    //一三象限置换位置
    for (i = 0; i < height / 2; i++)
    {
        for (j = width/2; j < width; j++)
        {
            unsigned char t = Pic[i][j];
            Pic[i][j] = Pic[height / 2 + i][j - width / 2];
            Pic[height / 2 + i][j - width / 2] = t;
        }
    }
    //保存频谱图
    fp = fopen("pinpu.raw", "wb");
    for (i = 0; i < height; i++)
    {
        for (j = 0; j < width; j++)
        {
            fwrite(&Pic[i][j], 1, 1, fp);
        }
    }
    fclose(fp);
    //对频谱进行处理
    for (i = 0; i < height; i++){
        for (j = 0; j < width; j++){
            if (LOW_PASS == 1){       //低通,滤掉高频。
                if (((i<DEGREE) && (j<DEGREE)) || ((i>(width - DEGREE)) && (j<DEGREE)) || (i<DEGREE) && (j>(height - DEGREE))) || ((i>(width - DEGREE)) && (j>(height - DEGREE))))
                {
                }
                else
                {
                    fd[i * width + j].re = 0;
                    fd[i * width + j].im = 0;
                }
            }
            else{                   //高通,滤掉低频。即四个角置0
                if (((i<DEGREE) && (j<DEGREE)) || ((i>(width - DEGREE)) && (j<DEGREE)) || i<DEGREE) && (j>(height - DEGREE))) || ((i>(width - DEGREE)) && (j>(height - DEGREE))))
                {
                    if (((i<1) && (j<1)) || ((i>(width - 1)) && (j<1)) || ((i<1) && (j>(height - 1))) || ((i>(width - 1)) && (j>(height - 1))))
                    {
                    }
                    else
                    {
                        fd[i * width + j].re = 0;
                        fd[i * width + j].im = 0;
                    }
                }
            }
        }
    }
    //保存处理频谱图
    for (i = 0; i < height; i++)
    {
        for (j = 0; j < width; j++)
        {
            double temp = sqrt((fd[i * width + j].re / (height*width)) * (fd[i * width + j].re / (height*width)) + fd[i * width + j].im / (height*width)) * (fd[i * width + j].im / (height*width)));
            temp = (temp - min) * 25500.0 / (max - min);
            Pic[i][j] = (unsigned char)temp;
            //printf("%f\t", temp);
        }
    }
    //频谱更改位置
    //二四象限置换位置
    for (i = 0; i < height / 2; i++)
    {
        for (j = 0; j < width / 2; j++)
        {
            unsigned char t = Pic[i][j];
            Pic[i][j] = Pic[height / 2 + i][width / 2 + j];
            Pic[height / 2 + i][width / 2 + j] = t;
        }
    }
    //一三象限置换位置
    for (i = 0; i < height / 2; i++)
    {
        for (j = width / 2; j < width; j++)
        {
            unsigned char t = Pic[i][j];
            Pic[i][j] = Pic[height / 2 + i][j - width / 2];
            Pic[height / 2 + i][j - width / 2] = t;
        }
    }
    fp = fopen("p_pinpu.raw", "wb");
    for (i = 0; i < height; i++)
    {
        for (j = 0; j < width; j++)
        {
            fwrite(&Pic[i][j], 1, 1, fp);
        }
    }
    fclose(fp);
    IFFT2(td,height, width, fd)
    for (i = 0; i < height; i++)
    {
        for (j = 0; j < width; j++)
        {
            double temp = sqrt((td[i * width + j].re) * (td[i * width + j].re) + fd[i * width + j].im) * (fd[i * width + j].im));
            Pic[i][j] = (unsigned char)td[i * width + j].re;
            //printf("%f\t", temp);
        }
    }

    fp = fopen("processed.raw", "wb");
    for (i = 0; i < height; i++)
    {
        for (j = 0; j < width; j++)
        {
            fwrite(&Pic[i][j], 1, 1, fp);
        }
    }
    fclose(fp);
    system("pause");
    return 0;
}

Fft_ifft.c

#include <math.h>
#include <malloc.h>
#include <string.h>
#include <stdio.h>  
#include "fft_ifft.h"

/*快速傅里叶变换
TD为时域值,FD为频域值,power为2的幂数*/
void FFT(COMPLEX * TD, COMPLEX * FD, int power)
{
    int count;
    int i, j, k, bfsize, p;
    double angle;
    COMPLEX *W, *X1, *X2, *X;

    /*计算傅里叶变换点数*/
    count = 1 << power;
    /*分配运算所需存储器*/
    W = (COMPLEX *)malloc(sizeof(COMPLEX)*count / 2);
    X1 = (COMPLEX *)malloc(sizeof(COMPLEX)*count);
    X2 = (COMPLEX *)malloc(sizeof(COMPLEX)*count);
    /*计算加权系数*/
    for (i = 0; i<count / 2; i++)
    {
        angle = -i* pi * 2 / count;
        W[i].re = cos(angle);
        W[i].im = sin(angle);
    }
    /*将时域点写入存储器*/
    memcpy(X1, TD, sizeof(COMPLEX)*count);
    /*蝶形运算*/
    for (k = 0; k<power; k++)
    {
        for (j = 0; j<1 << k; j++)
        {
            bfsize = 1 << (power - k);
            for (i = 0; i<bfsize / 2; i++)
            {
                p = j*bfsize;
                X2[i + p] = Add(X1[i + p], X1[i + p + bfsize / 2]);
                X2[i + p + bfsize / 2] = Mul(Sub(X1[i + p],X1[i + p + bfsize / 2]), W[i*(1 << k)]);
            }
        }
        X = X1;
        X1 = X2;
        X2 = X ;
    }
    /*重新排序*/
    for (j = 0; j<count; j++)
    {
        p = 0;
        for (i = 0; i<power; i++)
        {
            if (j&(1 << i)) p += 1 << (power - i - 1);
        }
        FD[j] = X1[p];
    }
    /*释放存储器*/
    free(W);
    free(X1);
    free(X2);
}
/*快速傅里叶反变换,利用快速傅里叶变换
FD为频域值,TD为时域值,power为2的幂数*/
void IFFT(COMPLEX *FD, COMPLEX *TD, int power)
{
    int i, count;
    COMPLEX *x;
    /*计算傅里叶反变换点数*/
    count = 1 << power;
    /*分配运算所需存储器*/
    x = (COMPLEX *)malloc(sizeof(COMPLEX)*count);
    /*将频域点写入存储器*/
    memcpy(x, FD, sizeof(COMPLEX)*count);
    /*求频域点的共轭*/
    for (i = 0; i<count; i++)
    {
        x[i].im = -x[i].im;
    }
    /*调用快速傅里叶变换*/
    FFT(x, TD, power);
    /*求时域点的共轭*/
    for (i = 0; i<count; i++)
    {
        TD[i].re /= count;
        TD[i].im = -TD[i].im / count;
    }
    /*释放存储器*/
    free(x);
}
 /*************************************************************************
*
* 函数名称:
*   Fourier()
*
* 参数:
*   COMPLEX* TD          -输入的时域序列
*    long lWidth     -图象宽度
*    long lHeight    -图象高度
*    COMPLEX* FD     -输出的频域序列
*
* 返回值:
*   BOOL               - 成功返回TRUE,否则返回FALSE。
*
* 说明:
*   该函数进行二维快速付立叶变换。
*
************************************************************************/
void FFT2(COMPLEX * TD, long lWidth, long lHeight, COMPLEX  * FD)
{
    COMPLEX *TempT, *TempF;
    // 循环变量
    long i;
    long j;
 
    // 进行傅里叶变换的宽度和高度(2的整数次方)
    long w = 1;
    long h = 1;
    int wp = 0;
    int hp = 0;
 
    // 计算进行付立叶变换的宽度和高度(2的整数次方)
    while (w < lWidth){
        w *= 2;
        wp++;
    }

    while (h < lHeight){
        h *= 2;
        hp++;
    }
    // 分配内存
    TempT = (COMPLEX *)malloc(sizeof(COMPLEX)*h);
    TempF = (COMPLEX *)malloc(sizeof(COMPLEX)*h);
    // 对y方向进行快速付立叶变换
    //rgb
    /*for (i = 0; i < w * 3; i++)
    {
    // 抽取数据
    for (j = 0; j < h; j++)
    TempT[j] = TD[j * w * 3 + i];//rgb
    // 一维快速傅立叶变换
    FFT(TempT, TempF, hp);
 
    // 保存变换结果
    for (j = 0; j < h; j++)
    TD[j * w * 3 + i] = TempF[j];
    }
    */
    //灰度
    for (i = 0; i < w; i++){
        // 抽取数据
        for (j = 0; j < h; j++){
            TempT[j] = TD[j * w + i];
        }
         // 一维快速傅立叶变换
        FFT(TempT, TempF, hp);
        // 保存变换结果
        for (j = 0; j < h; j++){
            TD[j * w + i] = TempF[j];
        }
    }
    // 释放内存
    free(TempT);
    free(TempF);

    // 分配内存
    TempT = (COMPLEX *)malloc(sizeof(COMPLEX)*w);
    TempF = (COMPLEX *)malloc(sizeof(COMPLEX)*w);
    // 对x方向进行快速付立叶变换
    //rgb
    /*
    for (i = 0; i < h; i++)
    {
    for (k = 0; k < 3; k++)
    {
    // 抽取数据
    for (j = 0; j < w; j++)
    TempT[j] = TD[i * w * 3 + j * 3 + k];
    // 一维快速傅立叶变换
    FFT(TempT, TempF, wp);
    // 保存变换结果
    for (j = 0; j < w; j++)
    FD[i * w * 3 + j * 3 + k] = TempF[j];
    }
    }
    */
    //灰度
    for (i = 0; i < h; i++)
    {
        // 抽取数据
        for (j = 0; j < w; j++){
            TempT[j] = TD[i * w + j];
        }
        // 一维快速傅立叶变换
        FFT(TempT, TempF, wp);
        // 保存变换结果
        for (j = 0; j < w; j++){
            FD[i * w + j] = TempF[j];
        }
    }
    // 释放内存
    free(TempT);
    free(TempF);
}
/*************************************************************************
*
* 函数名称:
*   IFourier()
*
* 参数:
*   LPBYTE TD        -返回的空域数据
*   long lWidth      -空域图象的宽度
*    long lHeight    -空域图象的高度
*    COMPLEX* FD     -输入的频域数据
*
* 返回值:
*   BOOL               - 成功返回TRUE,否则返回FALSE。
*
* 说明:
*   该函数进行二维快速付立叶逆变换。
*
************************************************************************/
void IFFT2(COMPLEX *TD, long lWidth, long lHeight, COMPLEX * FD)
{
    COMPLEX *TempT, *TempF;
    // 循环变量
    long i;
    long j;
    // 进行傅里叶变换的宽度和高度(2的整数次方)
    long w = 1;
    long h = 1;
    int wp = 0;
    int hp = 0;
    // 计算进行傅里叶变换的宽度和高度(2的整数次方)
    while (w < lWidth){
        w *= 2;
        wp++;
    }
    while (h < lHeight){
        h *= 2;
        hp++;
    }
    // 计算图像每行的字节数
    //long lLineBytes = WIDTHBYTES(lWidth * 24);
    // 分配内存
    TempT = (COMPLEX *)malloc(sizeof(COMPLEX)*w);
    TempF = (COMPLEX *)malloc(sizeof(COMPLEX)*w);
    // 对x方向进行快速付立叶变换
    //rgb
    /*
    for (i = 0; i < h; i++)
    {
    for (k = 0; k < 3; k++)
    {
    // 抽取数据
    for (j = 0; j < w; j++)
    TempF[j] = FD[i * w * 3 + j * 3 + k];
    // 一维快速傅立叶变换
    IFFT(TempF, TempT, wp);
    // 保存变换结果
    for (j = 0; j < w; j++)
    FD[i * w * 3 + j * 3 + k] = TempT[j];
    }
    }
    */
    //灰度
    for (i = 0; i < h; i++){
        // 抽取数据
        for (j = 0; j < w; j++)
            TempF[j] = FD[i * w + j];
        // 一维快速傅立叶变换
        IFFT(TempF, TempT, wp);
        // 保存变换结果
        for (j = 0; j < w; j++)
            FD[i * w + j] = TempT[j];
    }
    // 释放内存
    free(TempT);
    free(TempF);
    TempT = (COMPLEX *)malloc(sizeof(COMPLEX)*h);
    TempF = (COMPLEX *)malloc(sizeof(COMPLEX)*h);
    // 对y方向进行快速付立叶变换
    //rgb
    /*
    for (i = 0; i < w * 3; i++)
    {
    // 抽取数据
    for (j = 0; j < h; j++)
    TempF[j] = FD[j * w * 3 + i];

    // 一维快速傅立叶变换
    IFFT(TempF, TempT, hp);
    // 保存变换结果
    for (j = 0; j < h; j++)
    TD[j * w * 3 + i] = TempT[j];
    }
    */
    //灰度
    for (i = 0; i < w; i++){
        // 抽取数据
        for (j = 0; j < h; j++)
            TempF[j] = FD[j * w + i];
        // 一维快速傅立叶变换
        IFFT(TempF, TempT, hp);
        // 保存变换结果
        for (j = 0; j < h; j++)
            TD[j * w + i] = TempT[j];
    }
    // 释放内存
    free(TempT);
    free(TempF);
 
    /*for (i = 0; i < h; i++)
    {
    for (j = 0; j < w * 3; j++)
    {
    if (i < lHeight && j < lLineBytes)
    *(TD + i * lLineBytes + j) = FD[i * w * 3 +
j].re;
    }
    }
    */
}
 

fft_ifft.h

#pragma once
#ifndef COM_H_INCLUDED
#define COM_H_INCLUDED

#define pi (double)3.14159265359

/*复数定义*/
typedef struct
{
	double re;
	double im;
}COMPLEX;


/*复数加运算*/
static COMPLEX Add(COMPLEX c1, COMPLEX c2)
{
	COMPLEX c;
	c.re = c1.re + c2.re;
	c.im = c1.im + c2.im;
	return c;
}


/*复数减运算*/

static COMPLEX Sub(COMPLEX c1, COMPLEX c2)
{
	COMPLEX c;
	c.re = c1.re - c2.re;
	c.im = c1.im - c2.im;
	return c;
}

/*复数乘运算*/

static COMPLEX Mul(COMPLEX c1, COMPLEX c2)
{
	COMPLEX c;
	c.re = c1.re*c2.re - c1.im*c2.im;
	c.im = c1.re*c2.im + c2.re*c1.im;
	return c;
}
void FFT(COMPLEX * TD, COMPLEX * FD, int power);
void IFFT(COMPLEX *FD, COMPLEX *TD, int power);
void FFT2(COMPLEX * TD, long lWidth, long lHeight, COMPLEX  * FD);
void IFFT2(COMPLEX *TD, long lWidth, long lHeight, COMPLEX * FD);

#endif


有问题多交流,可留言可发邮件,我的邮箱是zhaodongyu艾特pku(这里换成点)edu.cn。

本工程源码已更新至github,欢迎star,欢迎PR:)

  • 8
    点赞
  • 78
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值