TMS320C6455二维FFT和IFFT实现
参考资料:
- Rafael C. Gonzalez, Richard E. Woods, 数字图像处理(第三版)4.11,配套书内资源
- Alan V.Oppenheim, Ronald W.Schafer, 离散时间信号处理(第三版)9.5
- SPRUEB8B - TMS320C64x+ DSP Little-Endian DSP Library Programmer’s Reference
- CCS Doc, 7.8 Image Analyzer
FFT原理简介
FFT的计算在任意一本《数字信号处理》的书中肯定都有讲,我看的是《离散时间信号处理》这本书。整个第9章对DFT和FFT做了详细的介绍。
最开始讲的Goertzel算法,引入一个数字序列,通过这一序列的递推公式求第N个值,等价于计算相应的DFT的结果。虽然这一算法的计算量仍然正比于,但也在一定程度上降低了计算复杂度。
而后的按时间抽取和按频率抽取的FFT算法在后面又归结为N为复合数的更为一般的FFT算法。按时间抽取的算法是比较好理解的。一个数字序列,根据标号的奇偶性分为两个序列,分别计算DFT,再由它们的结果计算整个序列的DFT就非常方便。其中可以灵活运用旋转因子的对称性和周期性,最后表示成一个“蝶形运算”。
FFT的计算中,数据存储也是一个需要审慎考虑的问题。蝶形运算两端的数据索引如果不需要发生改变,那样就可以把计算结果再存回原来的位置,而不需要额外开辟空间,这就被称为“同址计算(in-place)”。经过观察可以发现,如果输入是到位序的,然后用同址计算得到的结果就是正序的;如果输入是正序的,再用同址计算得到的结果就是到位序的。也有那种输入输出都是正序的算法,那样就没有采用同址计算。
我们熟悉的FFT算法,把序列分成奇偶序列递归地运算,就要求序列长度是2的幂次,这种算法称为“radix-2”算法;而如果按照类似的方法,把整个序列分成4组,即x[4n]、x[4n+1]、x[4n+2]、x[4n+3],这被称为“radix-4”算法。那样所需要的级数()就更少,同时也要求序列的的长度是4的幂次。当然,还有将两种算法结合,主要采用radix-4,而在最后根据情况采用radix-2或者radix-4。
DSPLib配置
DSP库安装在哪也都无所谓,在Window->Preferences->Code Composer Studio->Products页面下可以找到安装的DSP库。
但是这个Product我不知道怎么用,就是我在实际的工程里即使勾选这个product好像也没啥用。
在跑例程的时候,我看到源文件中只包含了dsplib.h,所以只要包含它的路径和对应的静态库就行。
可以在Window->Preferences->Code Composer Studio->Build->Environment页面下添加DSP的安装路径。方便在工程中设置相应的包含路径和包含的静态库。
图像数据生成
用opencv-python读图像数据,调整图像分辨率,长宽同比例缩放,并在空白区域填充“0”,用于满足FFT对2的整数次幂的要求。生成输出.h文件和调整分辨率后的图像。
1 | import cv2 |
DSPLib中的FFT和IFFT
DSPLib中的函数可以从源文件中的注释进行理解,也可以直接看SPRUEB8B这个文档。FFT根据输入和输出数据的位宽也分好多种,我实现的是那种输入输出都是32位的fft,没有缩放系数,也就是在"dsplib安装路径\packages\ti\dsplib\src"路径下的DSP_fft32x32和DSP_ifft32x32。
在上面的路径下有四个测试例程的文件夹还有三种FFT的实现方式,分别是纯C语言实现(_cn)、带有编译器指令的C语言实现(_i)和汇编实现(_sa)。这三种方式中,直接采用汇编实现的代码效率最高,速度最快。而DSP库默认调用的也就是这个汇编的FFT。
这三种实现方式,采用的计算方式都是相同的,只是采用专门的汇编指令有更高的效率。所以想要理解这个代码可以直接看_cn版本的。蝶形运算的示意图大概是下面这个样子:
输入输出都是正序,没有同址计算。采用radix-4的算法,一个蝶形运算需要4个数。在第一级运算中,这四个数的标号间隔为N/4(类比图里radix-2是N/2),这在代码中用变量stride表示,后续每级逐级减小。
IFFT的计算和FFT非常像,我们可以用类似的方式计算IFFT,《数字图像处理》的4.11.2给出了计算方法。
一般情况下,所以我们只需要对原复数序列取共轭后做DFT,再把得到的结果除以“N”就能得到IFFT的结果。
二维FFT和IFFT实现
在《数字图像处理》4.11.1中介绍了二维DFT的可分性,即要实现二维DFT,可以对行列两个维度分别做DFT。在代码中实现时,可以先对行做FFT,再将结果转置,之后再对行做FFT,最后再转置回去就能够得到对应的二维DFT的结果。\
1 | /* |
这里的转置就是一般的转置,不是共轭转置。如果图像的行和列长度是相等的,转置只需要交换行列坐标相对的两个位置的数据。而如果图像的行和列的长度不相等,则需要额外开辟空间用来存放转置的数据,额外的存储开销较大,因此我就没写这部分代码,而是要求图像行列等长。
DSP库中提供的IFFT仅是在FFT的基础上进行了一点修改,改了一些符号,实现了取复共轭的计算,但是没有除以序列长度。因此在每次调用DSP_ifft32x32后需要对结果再除以“N”才是IFFT的结果。
1 | /* |
图像分析工具(Image Analyzer)
CCS自带图像分析工具。在调试界面,从Tools->Image Analyzer打开,电梯Image窗口内的区域,可以编辑Properties窗口中的属性。
- Image Format: 输入图像格式,取决于具体的数据格式
- Number of pixels per line: 每行像素
- Number of lines: 图像行数
- Data format: 数据格式,packed就是一个像素的数据是连续存放的,还有一种planar的就是同一通道的数据是连续存放的
- Pixel stride (bytes): 像素步进,就是每个像素占几个字节
- Red/Green/Blue/Alpha mask: “1”有效,可以让ARGB从几个字节中找到对应位置的值,可以重叠,RGB三个通道重叠就是灰度图像。
- Line stride (bytes): 每行占多少字节
- Start address: 图像起始地址
- Read data as: 读数据的格式,根据图像的数据格式设置,int(32bit), short(16bit), char(8bit)
- Start address: 图像起始地址
- Read data as: 读数据的格式,根据图像的数据格式设置,int(32bit), short(16bit), char(8bit)
设置完成后在Image窗口中点刷新按钮,即可刷新显示图像。
测试结果
预先存入的图像:
经过FFT变换,实际数据已经超出灰度范围,无法显示正常的FFT频谱:
IFFT变换后的图像: