Python 实现图像快速傅里叶变换和离散余弦变换
图像的正交变换在数字图像的处理与分析中起着很重要的作用,被广泛应用于图像增强、去噪、压缩编码等众多领域。本文手工实现了二维离散傅里叶变换和二维离散余弦变换算法,并在多个图像样本上进行测试,以探究二者的变换效果。
1. 傅里叶变换
实验原理
对一幅图像进行离散傅里叶变换(DFT),可以得到图像信号的傅里叶频谱。二维 DFT 的变换及逆变换公式如下:

DFT 尽管解决了频域离散化的问题,但运算量太大。从公式中可以看到,有两个嵌套的求和符号,显然直接计算的复杂度为 \(O(n^2)\) 。为了加快傅里叶变换的运算速度,后人提出快速傅里叶变换(FFT),即蝶形算法,将计算 DFT 的复杂度降低到了 \(O(n\log n)\)。
FFT 利用傅里叶变换的数学性质,采用分治的思想,将一个 \(N\) 点的 FFT,变成两个 \(N/2\) 点的 FFT。以一维 FFT 为例,可以表示如下:


其中,\(G(k)\) 是 \(x(k)\) 的偶数点的 \(N/2\) 点的 FFT,\(H(k)\) 是 \(x(k)\) 的奇数点的 \(N/2\) 点的 FFT。
这样,通过将原问题不断分解为两个一半规模的子问题,然后计算相应的蝶形运算单元,最终得以完成整个 FFT。
算法步骤
本次实验中,一维 FFT 采用递归实现,且仅支持长度为 2 的整数幂的情况。
算法步骤如下:
- 检查图像的尺寸,如果不是 2 的整数幂则直接退出。
- 对图像的灰度值进行归一化。
- 对图像的每一行执行一维 FFT,并保存为中间结果。
- 对上一步结果中的每一列执行一维 FFT,返回变换结果。
- 将零频分量移到频谱中心,并求绝对值进行可视化。
- 对中心化后的结果进行对数变换,以改善视觉效果。
主要代码
一维 FFT
def fft(x):
n = len(x)
if n == 2:
return [x[0] + x[1], x[0] - x[1]]
G = fft(x[::2])
H = fft(x[1::2])
W = np.exp(-2j * np.pi * np.arange(n//2) / n)
WH = W * H
X = np.concatenate([G + WH, G - WH])
return X
二维 FFT
def fft2(img):
h, w = img.shape
if ((h-1) & h) or ((w-1) & w):
print('Image size not a power of 2')
return img
img = normalize(img)
res = np.zeros([h, w], 'complex128')
for i in range(h):
res[i, :] = fft(img[i, :])
for j in range(w):
res[:, j] = fft(res[:, j])
return res
零频分量中心化
def fftshift(img):
# swap the first and third quadrants, and the second and fourth quadrants
h, w = img.shape
h_mid, w_mid = h//2, w//2
res = np.zeros([h, w], 'complex128')
res[:h_mid, :w_mid] = img[h_mid:, w_mid:]
res[:h_mid, w_mid:] = img[h_mid:, :w_mid]
res[h_mid:, :w_mid] = img[:h_mid, w_mid:]
res[h_mid:, w_mid:] = img[:h_mid, :w_mid]
return res
运行结果




2. 余弦变换
实验原理
当一个函数为偶函数时,其傅立叶变换的虚部为零,因而不需要计算,只计算余弦项变换,这就是余弦变换。离散余弦变换(DCT)的变换核为实数的余弦函数,因而计算速度比变换核为指数的 DFT 要快得多。
一维离散余弦变换与离散傅里叶变换具有相似性,对离散傅里叶变换进行下式的修改:

式中

由上式可见,\(\sum\limits_{x=0}^{2M-1}f_e(x)e^{\frac{-j2ux\pi}{2M}}\) 是 \(2M\) 个点的傅里叶变换,因此在做离散余弦变换时,可将其拓展为 \(2M\) 个点,然后对其做离散傅里叶变换,取傅里叶变换的实部就是所要的离散余弦变换。
算法步骤
基于上述原理,二维 DCT 的实现重用了上文中的一维 FFT 函数,并根据公式做了一些修改。
算法步骤如下:
- 检查图像的尺寸,如果不是 2 的整数幂则直接退出。
- 对图像的灰度值进行归一化。
- 对图像的每一行进行延拓,执行一维 FFT 后取实部,乘以公式中的系数,并保存为中间结果。
- 对上一步结果中的每一列进行延拓,执行一维 FFT 后取实部,乘以公式中的系数,返回变换结果。
- 对结果求绝对值,并进行对数变换,以改善视觉效果。
主要代码
二维 DCT
def dct2(img):
h, w = img.shape
if ((h-1) & h) or ((w-1) & w):
print('Image size not a power of 2')
return img
img = normalize(img)
res = np.zeros([h, w], 'complex128')
for i in range(h):
res[i, :] = fft(np.concatenate([img[i, :], np.zeros(w)]))[:w]
res[i, :] = np.real(res[i, :]) * np.sqrt(2 / w)
res[i, 0] /= np.sqrt(2)
for j in range(w):
res[:, j] = fft(np.concatenate([res[:, j], np.zeros(h)]))[:h]
res[:, j] = np.real(res[:, j]) * np.sqrt(2 / h)
res[0, j] /= np.sqrt(2)
return res
运行结果




完整源码请见 GitHub 仓库
Python 实现图像快速傅里叶变换和离散余弦变换的更多相关文章
- JPEG解码——(6)IDCT逆离散余弦变换
本篇是该系列的第六篇,承接上篇IZigZag变换,介绍接下来的一个步骤--逆离散余弦变换,即逆零偏置前的一个步骤. 该步骤比较偏理论,其业务是对IZigZag变换后的数据,再进一步的处理,使其恢复DC ...
- 在python3下使用OpenCV做离散余弦变换DCT及其反变换IDCT
对图像处理经常用到DCT, Python下有很多带有DCT算法包, 这里使用OpenCV的DCT做变换, 并简单置0部分数据, 再查看反变换图像的效果. import numpy as np impo ...
- 在python3下对数据分块(8x8大小)使用OpenCV的离散余弦变换DCT
在MATLAB中有blkproc (blockproc)对数据处理, 在python下没找到对应的Function, 这里利用numpy 的split(hsplit和vsplit) 对数据分块处理成8 ...
- 「快速傅里叶变换(FFT)」学习笔记
FFT即快速傅里叶变换,离散傅里叶变换及其逆变换的快速算法.在OI中用来优化多项式乘法. 本文主要目的是便于自己整理.复习 FFT的算法思路 已知两个多项式的系数表达式,要求其卷积的系数表达式. 先将 ...
- 傅里叶变换通俗解释及快速傅里叶变换的python实现
通俗理解傅里叶变换,先看这篇文章傅里叶变换的通俗理解! 接下来便是使用python进行傅里叶FFT-频谱分析: 一.一些关键概念的引入 1.离散傅里叶变换(DFT) 离散傅里叶变换(discrete ...
- 基于python的快速傅里叶变换FFT(二)
基于python的快速傅里叶变换FFT(二)本文在上一篇博客的基础上进一步探究正弦函数及其FFT变换. 知识点 FFT变换,其实就是快速离散傅里叶变换,傅立叶变换是数字信号处理领域一种很重要的算法. ...
- python 图像的离散傅立叶变换
图像(MxN)的二维离散傅立叶变换可以将图像由空间域变换到频域中去,空间域中用x,y来表示空间坐标,频域由u,v来表示频率,二维离散傅立叶变换的公式如下: 在python中,numpy库的fft模块有 ...
- 图像傅里叶变换(快速傅里叶变换FFT)
学习DIP第7天,图像傅里叶变换 转载请标明出处:http://blog.csdn.net/tonyshengtan,欢迎大家转载,发现博客被某些论坛转载后,图像无法正常显示,无法正常表达本人观点,对 ...
- Discrete cosine transform(离散余弦转换)
A discrete cosine transform (DCT) expresses a finite sequence of data points in terms of a sum of co ...
随机推荐
- JDBC——使用JDBC连接MySQL数据库
在JDBC--什么是JDBC一文中我们已经介绍了JDBC的基本原理. 这篇文章我们聊聊如何使用JDBC连接MySQL数据库. 一.基本操作 首先我们需要一个数据库和一张表: CREATE DATABA ...
- SpringBoot--防止重复提交(锁机制---本地锁、分布式锁)
防止重复提交,主要是使用锁的形式来处理,如果是单机部署,可以使用本地缓存锁(Guava)即可,如果是分布式部署,则需要使用分布式锁(可以使用zk分布式锁或者redis分布式锁),本文的分布式锁以red ...
- C#/VB.NET 在PDF中添加文件包(Portfolio)
PDF文件包(Portfolio)允许用户将多种不同类型的文件如Word.Excel.PDF.PowerPoint和图片等集合到一个PDF文件中,用户可以打开.更改PDF文件包中的单个文件.添加文件包 ...
- could not resolve property(无法解析属性)
could not resolve property(无法解析属性) 顾名思义在写hql语句的时候,属性写错了! 请检查大小写,是实体类的,不是数据库表的! 一个一个检查,仔细看!
- Spring系列.Bean简介
Bean属性配置 Spring在读取配置文件中bean的metadata后会构造一个个BeanDefination对象.后面Spring会根据这些BeanDefinition创建对象.在配置一个bea ...
- 初探numpy——广播和数组操作函数
numpy广播(Broadcast) 若数组a,b形状相同,即a.shape==b.shape,那么a+b,a*b的结果就是对应数位的运算 import numpy as np a=np.array( ...
- 洛谷 P1347 【排序】
这篇题解没有用拓补排序 (嗐 菜就直说) 个人感觉这道题拓补排序没有变种\(Floyd\)好写吧,思维难度也低一点(亲眼目睹机房dalao这道题拓补排序调了很久). 吐槽结束,开始正题~ 对于这道题为 ...
- spark | 手把手教你用spark进行数据预处理
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是spark专题的第七篇文章,我们一起看看spark的数据分析和处理. 过滤去重 在机器学习和数据分析当中,对于数据的了解和熟悉都是最基 ...
- 树莓派搭建Nexus2私服
使用树莓派搭建Nexus2私服需要的材料有: 树莓派3B+(或者4B) 移动硬盘一个 1. 下载nexus2.x安装包 由于nexus2.x官方的启动环境并不支持arm架构的树莓派,所以这里采用tom ...
- Mariadb之半同步复制集群配置
首先我们来了解下在mariadb/mysql数据库主从复制集群中什么是同步,什么是异步,什么是半同步:所谓同步就是指主节点发生写操作事件,它不会立刻返回,而是等到从节点接收到主节点发送过来的写操作事件 ...