图像处理的方法主要包括两大类,分别是在空间域的处理方法和频率域的处理方法。第一次大作业实现基于点运算的灰度处理是在空间域的处理,而这次大作业主要实现了图像从空间域到频率域的变换。频率域的图像信息对于图像的特征提取、空间频域滤波、图像恢复有着重要的价值,所以这种变换有着重要的意义。
自从傅里叶变换提出之后,其在物理学、电子类学科、信号处理、概率论等领域中得到了广泛的应用。对应于计算机离散的特性,傅里叶变换表现为离散傅里叶变换DFT(Discreate Fourier Transform)
。到了1965年由J.W.库利和T.W.图基提出基2的快速傅里叶变换FFT(Fast Fourier Transform)
,这种算法使得计算机计算离散傅里叶变换时复数乘法的次数显著减小,一维傅里叶变换的时间复杂度由降低到,使得图像的实时处理成为可能。
在傅里叶级数展开时如果被展开的函数只有实部,即其傅里叶级数中只包含余弦项,可导出离散余弦变换DCT(Discreate Cosine Transform)
,近年来由于专用集成电路设计的发展,DCT
在图像编码中得到了广泛的应用,同时也成为了JPEG、MPEG等国际公认编码中的重要算法,此外在视频压缩中最常用的变换方法亦为DCT
。
基于以上考虑,这次大作业主要实现二维空间域到频率域的快速傅里叶变换FFT
,以及离散余弦变换DCT
的算法,同上次作业相同,此次开发语言仍使用C#
。
傅里叶变换与离散余弦变换技术报告概述目录一、快速傅里叶变换(FFT)1. 数学原理1. 一维傅里叶变换1. 一维连续傅里叶变换2. 一维离散傅里叶变换2.二维离散傅里叶变换(DFT)3. 一维快速傅里叶变换(FFT)1. 一维快速傅里叶变换算法的实现2. 一维快速傅里叶逆变换算法的实现4. 二维FFT及其的逆变换的思想及实现2. C#实现0. 图像的扩展1. fft2. ifft3. fft24. ifft25. fftshift3. 效果展示二、离散余弦变换(DCT)1. 数学原理1. 离散余弦变换VS.傅里叶变换2. 一维离散余弦变换3. 二维离散余弦变换4. 离散余弦变换的快速实现1. 一维DCT及其逆变换算法的快速实现2. 二维DCT及其逆变换算法的快速实现2. C#实现0. 图像的扩展1. dct2. idct3. dct24. idct23. 效果展示参考资料附录确定蝶式流程图对偶顶点的算法
设是一个连续的信号,或者仅有有限个间断点、极值点,满足Dirichlet
条件,且满足
则傅里叶变换存在,定义为:
且傅里叶反变换为:
由欧拉公式有:
其中表示空间域为实函数,而表示频率域为复函数,且由实部和虚部组成。满足:
称为信号的概率密度函数,在信号处理中常常习惯把 称为幅度谱或者频谱,而把称为相位谱或简称相谱,其中两种谱的导出公式为:
对于计算机上处理的离散信号来说,采用离散傅里叶变换,其中次离散采样序列可以表示为 ,令为离散实域变量,令为离散频域变量,则一维离散傅里叶变换(DFT)可以表示为:
且离散傅里叶反变换可以表示为:
对于二维的图像信号,其离散傅里叶变换可以表示为:
且离散傅里叶反变换可以表示为:
与一维情况类似,频谱和相谱的导出公式为:
二维离散傅里叶变换的基本性质有:
- 可分离性
- 平移性
- 周期性
- 共轭对称性
- 旋转不变性
根据傅里叶变换的性质,进一步推导快速傅里叶变换(FFT),令,根据式(1.4)和式(1.5),对于点序列的一维傅里叶变换对为:
离散傅里叶变换有大量的重复运算,令矩阵:
令,,则一维离散傅里叶正变换可写为矩阵形式,即:
由于具有周期性,的因子取值有如下特点:
对于的DFT
可以写成如下矩阵形式:
经过一系列的初等变换可得:
可以画出蝶式流程图:
其中是级,表示当前算法是第几趟执行,对于具有个采样点的蝶式流程图具有级,其中。例如:上图有个采样点,共级,从左到右依次为级、级、级。
此外因子具有如下分布规律:
由于故由上面可以看出规律为:
对于上图级因子分布规律为:
从面的分析可以得出以下几个结论,用于指导我们实现FFT
算法。
- 蝶式流程图的最大级数与采样点个数的关系为
- 对于每一级计算的下标都不同,由于傅里叶级数具有周期性,故可以统一变换成为下表均为采样点个数的形式,所以仅需要计算个,即仅需计算。
- 频域和实域的对应点映射关系发生了变化,对应点的计算由PPT上的二进制算法给出,在这个报告的附录中会做一个简要的介绍。
- 通过蝶形运算计算由实域变换到频域相应的值
由公式(1.7)和(1.8)我们发现,求FFT
逆变换的算法步骤如下:
- 先求出的共轭代入FFT正变换的公式。得到。
- 再将结果求共轭并除以系数即可得到所求的。
- IFFT实现了信号从频域到实域的转换。
- FFT算法是基2的快速算法,而图像的长宽并不一定是基2的,先对图像进行扩展。
- 根据傅里叶变换的性质,二维的傅里叶变换可以通过两步的一维傅里叶变换实现。
- 可以先在方向上实现一维的傅里叶变换,再将结果在方向上实现另一维的傅里叶变换。
- IFFT2的实现与上述描述类似。
二维的快速傅里叶变换实现了如下的个函数(为了与MATLAB
保持一致,采用相应的名称),分别为一维的快速傅里叶变换及其逆变换,二维的快速傅里叶变换和其逆变换,最后一个函数实现了把低频的频谱从四个角移到中心:
- public Complex[] fft(Complex[] rawArray,int N)
- public Complex[] ifft(Complex[] rawArray, int N)
- public Complex[] fft2(Bitmap currentBitmap)
- public byte[] ifft2(Complex[] fftBuffer)
- public byte[] fftshift(byte[] imgBuf)
同时实现了个图像绘制的相关函数,其中第一个函数把相继执行FFT2
和IFFT2
的图显示出来,进而与原图对比,后两个函数分别画出FFT2
变换后的频率谱和相位谱:
- public Bitmap GetIfft2Bitmap(byte[] imgBuffer)
- public Bitmap GetAmplitudeBitmap(byte[] imgBuffer)
- public Bitmap GetPhaseBitmap(byte[] imgBuffer)
根据前面的描述可知,我们实现的FFT
算法是基的快速算法,而图像的长宽并不一定是基的所以要对图像进行扩展使得算法能够得以执行。
if (currentBitmap != null)
{
int i = 1;
//宽度扩展
while (i < currentBitmap.Width)
i *= 2;
imageWidth = i;
//高度扩展
i = 1;
while (i < currentBitmap.Height)
i *= 2;
imageHeight = i;
}
在第一步描述的fft
算法流程具体实现步骤如下:
计算最大级数
int stageNum=Convert.ToInt32(Math.Log(N, 2));//求fft的级数
计算个
Complex[] W = new Complex[N / 2];
//计算W(i,N) 算一半
for (int i = 0; i < N / 2; i++)
{
double angle = -i * 2.0 * Math.PI / N;
W[i] = new Complex((float)Math.Cos(angle), (float)Math.Sin(angle));
}
计算频域和实域的对应点(算法讨论见附录)
Complex[] tmpBuffer1 = new Complex[N];
Complex[] tmpBuffer2 = new Complex[N];
//复制待处理数组(浅拷贝)
rawArray.CopyTo(tmpBuffer1, 0);
//确定数组元素的对偶节点并重新排序
for (int i = 0; i < N; i++)
{
int originalPos = i;
int postPos = 0;
for (int j = 0; j < stageNum; j++)
{
//逆向添加每一位,postPos初始为0
postPos <<= 1;
//bit test 取得末位,并添加到postPos的末位
postPos |= (originalPos - ((originalPos >> 1) << 1));
//正向遍历每一位
originalPos >>= 1;
}
tmpBuffer2[postPos] = tmpBuffer1[i];
}
tmpBuffer2.CopyTo(tmpBuffer1, 0);
蝶形运算计算由实域变换到频域相应的值
//蝶形运算
for (int stage = 0; stage < stageNum; stage++)
{
int interval = 1 << stage; //组间间隔
int groupNum = 1 << (stageNum - stage - 1); //总的组数
int groupSize = N / groupNum; //组的大小
for (int group = 0; group < groupNum; group++)
{
int groupStart = groupSize * group; //本组前半部分的第一个元素
int groupMid = groupStart + groupSize / 2; //本组后半部分的第一个元素
int groupTail = groupStart + groupSize; //本组的上界
for (int r = 0; r < interval; r++) //本组后半部分每一个元素是上次输出元素的W(groupNum*r,N)倍(复数乘积)
{
tmpBuffer1[groupMid + r] = tmpBuffer1[groupMid + r] * W[groupNum * r];
}
for (int k = groupStart; k < groupMid; k++) //本组前半部分为复数加
{
tmpBuffer2[k] = tmpBuffer1[k] + tmpBuffer1[k + groupSize / 2];
}
for (int k = groupMid; k < groupTail; k++) //本组后半部分为复数减
{
tmpBuffer2[k] = tmpBuffer1[k - groupSize / 2] - tmpBuffer1[k];
}
}
tmpBuffer2.CopyTo(tmpBuffer1, 0);
}
先求出的共轭并代入FFT
正变换的公式。得到。
Complex[] tmpBuffer = new Complex[N];
rawArray.CopyTo(tmpBuffer, 0);
//求F(u)的共轭
for (int i = 0; i < N; i++)
{
tmpBuffer[i].Imaginary = -tmpBuffer[i].Imaginary;
}
//调用快速傅里叶变换
tmpBuffer = fft(tmpBuffer, N);
再将结果()求共轭并除以系数即可得到所求的。
//求结果的共轭并除以系数N
for (int i = 0; i < N; i++)
{
tmpBuffer[i].Real = tmpBuffer[i].Real / N;
tmpBuffer[i].Imaginary = -tmpBuffer[i].Imaginary / N;
}
先在方向上实现一维的快速傅里叶变换
Complex[] tmpBuffer1;
Complex[] tmpBuffer2;
tmpBuffer1 = new Complex[imageHeight];
tmpBuffer2 = new Complex[imageHeight];
for (int u = 0; u < imageWidth; u++)
{
for (int v = 0; v < imageHeight; v++)
{
tmpBuffer1[v] = fftBuffer[v * imageWidth + u];
}
tmpBuffer2 = fft(tmpBuffer1, imageHeight);
for (int v = 0; v < imageHeight; v++)
{
fftBuffer[v * imageWidth + u] = tmpBuffer2[v];
}
}
再在方向上实现一维的快速傅里叶变换
tmpBuffer1 = new Complex[imageWidth];
tmpBuffer2 = new Complex[imageWidth];
for (int u = 0; u < imageHeight; u++)
{
for (int v = 0; v < imageWidth; v++)//性能瓶颈
{
tmpBuffer1[v] = fftBuffer[u * imageWidth + v];
}
tmpBuffer2 = fft(tmpBuffer1, imageWidth);
for (int v = 0; v < imageWidth; v++)//性能瓶颈
{
fftBuffer[u * imageWidth + v] = tmpBuffer2[v];
}
}
计算幅度谱
//将幅度谱图以图像形式存入imgAmplitudeBuffer
double t;
for (int i = 0; i < imageHeight; i++)
{
for (int j = 0; j < imageWidth; j++)
{
t = fftBuffer[i * imageWidth + j].Abs();
t = t / 500;
if (t > 255)
imgAmplitudeBuffer[i * imageWidth + j] = 255;
else
imgAmplitudeBuffer[i * imageWidth + j] = (byte)(t + 0.5);
}
}
计算相位谱
//将相位谱图以图像形式存入imgBuffer
for (int i = 0; i < imageHeight; i++)
{
for (int j = 0; j < imageWidth; j++)
{
t = fftBuffer[i * imageWidth + j].Angle() + 2 * Math.PI; //0≤t≤4π
t = t * 255 / (4 * Math.PI);
if (t > 255)
imgPhaseBuffer[i * imageWidth + j] = 255;
else
imgPhaseBuffer[i * imageWidth + j] = (byte)(t + 0.5);
}
}
先在方向上实现一维的快速傅里叶逆变换
Complex[] tmpBuffer1;
Complex[] tmpBuffer2;
tmpBuffer1 = new Complex[imageHeight];
tmpBuffer2 = new Complex[imageHeight];
for (int u = 0; u < imageWidth; u++)
{
for (int v = 0; v < imageHeight; v++)
{
tmpBuffer1[v] = fftBuffer[v * imageWidth + u];
}
tmpBuffer2 = ifft(tmpBuffer1, imageHeight);
for (int v = 0; v < imageHeight; v++)
{
fftBuffer[v * imageWidth + u] = tmpBuffer2[v];
}
}
再在方向上实现一维的快速傅里叶逆变换
tmpBuffer1 = new Complex[imageWidth];
tmpBuffer2 = new Complex[imageWidth];
for (int u = 0; u < imageHeight; u++)
{
for (int v = 0; v < imageWidth; v++)//性能瓶颈
{
tmpBuffer1[v] = fftBuffer[u * imageWidth + v];
}
tmpBuffer2 = ifft(tmpBuffer1, imageWidth);
for (int v = 0; v < imageWidth; v++)//性能瓶颈
{
fftBuffer[u * imageWidth + v] = tmpBuffer2[v];
}
}
提取恢复后的图像
byte[] imgBuffer = new byte[imageWidth * imageHeight];
//将变换后的图像存入imgBuffer
double t;
for (int i = 0; i < imageHeight; i++)
{
for (int j = 0; j < imageWidth; j++)
{
t = fftBuffer[i * imageWidth + j].Abs();
if (t > 255)
imgBuffer[i * imageWidth + j] = 255;
else
imgBuffer[i * imageWidth + j] = (byte)(t + 0.5);
}
}
把低频的频谱从四个角移到中心
int i0, j0;
byte[] tmpBuffer = new byte[imgBuf.Length];
imgBuf.CopyTo(tmpBuffer, 0);
for (int i = 0; i < imageHeight; i++)
{
for (int j = 0; j < imageWidth; j++)
{
if (i < imageHeight / 2)
i0 = i + imageHeight / 2;
else
i0 = i - imageHeight / 2;
if (j < imageWidth / 2)
j0 = j + imageWidth / 2;
else
j0 = j - imageWidth / 2;
imgBuf[i * imageWidth + j] = tmpBuffer[i0 * imageWidth + j0];
}
}
主要的绘制代码如下:
if (firstPainted)
{
fftBuffer = fft2(currentBitmap);
//对fftBuffer进行深拷贝
for (int i = 0; i < fftBuffer.Length; i++)
{
fftTmpBuffer[i] = new Complex(fftBuffer[i]);
}
currentAmplitudeBitmap = GetAmplitudeBitmap(fftshift(imgAmplitudeBuffer));
currentPhaseBitmap = GetPhaseBitmap(fftshift(imgPhaseBuffer));
currentIfft2Bitmap = GetIfft2Bitmap(ifft2(fftTmpBuffer));
firstPainted = false;
}
由于对图像进行了扩展,所以幅度谱和相位谱的大小跟原来的图略有差别,在执行完ifft2
之后得到的图像虚部都非常的小,取原图像大小的实部进行图像绘制,结果如下:
在图像压缩领域中,傅里叶变换的一个不足之处在于,它的参数都是复数,在数据的描述上相当于实数的两倍,而且不易计算,因此,人们希望有一种计算的数据量不太大而且压缩效果又很好的变换,在此思想的推动下,出现了离散余弦变换。离散余弦变换与傅里叶变换相比仅采用实部而没有虚部。
实域函数的一维离散余弦变换定义为:
一维离散余弦变换的反变换定义为:
其中,为归一化加权系数,由下式定义:
将一维离散余弦变换扩展到二维离散余弦变换,其定义为:
二维离散余弦变换的反变换定义为:
二维离散余弦变换是可分离的,因此二维的离散余弦变换及其逆变换可以逐次应用一维DCT
算法加以计算。
DCT
算法可以通过FFT
来实现,下面通过对一维离散余弦变换公式的推导得出其与傅里叶变换之间的关系。
由公式(2.1)可得:
对实域数据作如下延拓:
则的离散余弦变换可以写为:
对比式(1.7)和(2.8)发现为点的离散傅里叶变换。
故一维离散余弦变换的算法可以梳理如下:
- 将实域数据按照公式(2.7)延拓至长度为点的实域数据。
- 计算点的离散傅里叶变换,采用FFT来实现,每个结果与做复数乘法。
- 取上一步计算结果的实部并乘以归一化加权系数。
同理对于离散余弦变换IDCT 先在频率空间将做如下延拓:
则离散余弦反变换可以写为:
对比式(1.8)和(2.10)发现为函数在点上的离散傅里叶逆变换。
故一维离散余弦逆变换的算法可以梳理如下:
- 将频域数据按照公式(2.9)延拓至长度为点的频域数据。
- 将原始数据与做复数乘法,得到中间函数。
- 计算点的离散傅里叶逆变换,采用IFFT来实现。
- 取上一步计算结果的实部并乘以,之后与常数相加得到最终结果。
- DCT算法是基2的算法,而图像的长宽并不一定是基2的,先对图像进行扩展。
- 根据离散余弦变换的性质,二维的离散余弦变换可以通过两步的一维离散余弦变换实现。
- 可以现在方向上实现一维的离散余弦变换,再将结果在方向上实现另一维的离散余弦变换。
- IDCT2的实现与上述描述类似。
二维的离散余弦变换实现了如下的个函数(为了与MATLAB
保持一致,采用相应的名称),分别为一维的快速傅里叶变换及其逆变换(与上述fft
略有区别,这一fft
在函数中对于数据进行了延拓,ifft
类似进行了延拓,具体实现之后会提到),一维的离散余弦变换及其逆变换,以及二维的离散余弦变换及其逆变换:
- public Complex[] fft(Complex[] rawArray,int N)
- public Complex[] ifft(Complex[] rawArray, int N)
- public double[] dct(Complex[] rawArray, int N)
- public double[] idct(Complex[] rawArray, int N)
- public double[] dct2(Bitmap currentBitmap)
- public byte[] idct2(double[] dctBuffer)
同时实现了个图像绘制的相关函数,其中前一个函数把相继执行了DCT2
和IDCT2
的图显示出来,进而与原图对比,后一个函数画出DCT2
变换后的频率谱:
- public Bitmap GetIdct2Bitmap(byte[] imgBuffer)
- public Bitmap GetAmplitudeBitmap(byte[] imgBuffer)
根据前面的描述可知,我们实现的DCT
算法是基的算法,而图像的长宽并不一定是基的所以要对图像进行扩展使得算法能够得以执行。
//判断图像是否需要扩展
if (needExpand())
{
isExpanded = true;
int i = 1;
//宽度扩展
while (i < currentBitmap.Width)
i *= 2;
imageWidth = i;
//高度扩展
i = 1;
while (i < currentBitmap.Height)
i *= 2;
imageHeight = i;
}
else
{
isExpanded = false;
imageWidth = currentBitmap.Width;
imageHeight = currentBitmap.Height;
}
将实域数据按照公式(2.7)延拓至长度为点的实域数据(在fft
内实现)。
rawArray.CopyTo(tmpBuffer1,0);
for (int i = N / 2; i < N; i++)
{
tmpBuffer1[i] = new Complex(0, 0);
}
计算点的离散傅里叶变换,采用FFT
来实现,每个结果与做复数乘法,将计算结果的实部乘以归一化加权系数。
Complex[] fftBufer = null;
double[] dctBuffer = new double[N];
double au = Math.Sqrt(1.0 / N);
fftBufer = fft(rawArray, 2 * N);
dctBuffer[0] = au * fftBufer[0].Real;
au *= Math.Sqrt(2.0);
for (int i = 1; i < N; i++)
{
double angle = -i * Math.PI / N;
Complex W = new Complex((float)Math.Cos(angle), (float)Math.Sin(angle));
dctBuffer[i] = au * (W * fftBufer[i]).Real;
}
将频域数据按照公式(2.9)延拓至长度为点的频域数据(在ifft
中实现)。
rawArray.CopyTo(tmpBuffer1,0);
for (int i = N / 2; i < N; i++)
{
tmpBuffer1[i] = new Complex(0, 0);
}
将原始数据与做复数乘法,得到中间函数。
double tmpNum = Math.Sqrt(2.0 / N);
for (int i = 0; i < N; i++)
{
double angle = i * Math.PI / (2.0 * N);
Complex W = new Complex((float)Math.Cos(angle), (float)Math.Sin(angle));
tmpBuffer[i] = rawArray[i] * W;
}
计算点的离散傅里叶逆变换,采用IFFT
来实现。
ifftBufer = ifft(tmpBuffer, 2 * N);
取上一步计算结果的实部并乘以,之后与常数相加得到最终结果。
double d0 = (Math.Sqrt(1.0 / N) - Math.Sqrt(2.0 / N)) * ifftBufer[0].Real;
for (int i = 1; i < N; i++)
{
dctBuffer[i] = d0 + tmpNum * ifftBufer[i].Real * 2.0 * N;
}
先在方向上实现一维的离散余弦变换
Complex[] tmpBuffer1;
double[] tmpBuffer2;
tmpBuffer1 = new Complex[imageHeight];
tmpBuffer2 = new double[imageHeight];
for (int u = 0; u < imageWidth; u++)
{
for (int v = 0; v < imageHeight; v++)
{
tmpBuffer1[v] = new Complex((float)dctBuffer[v * imageWidth + u],0);
}
tmpBuffer2 = dct(tmpBuffer1, imageHeight);
for (int v = 0; v < imageHeight; v++)
{
dctBuffer[v * imageWidth + u] = tmpBuffer2[v];
}
}
再在方向上实现一维的离散余弦变换
tmpBuffer1 = new Complex[imageWidth];
tmpBuffer2 = new double[imageWidth];
for (int u = 0; u < imageHeight; u++)
{
for (int v = 0; v < imageWidth; v++)//性能瓶颈
{
tmpBuffer1[v] = new Complex((float)dctBuffer[u * imageWidth + v],0);
}
tmpBuffer2 = dct(tmpBuffer1, imageWidth);
for (int v = 0; v < imageWidth; v++)//性能瓶颈
{
dctBuffer[u * imageWidth + v] = tmpBuffer2[v];
}
}
计算幅度谱
//将幅度谱图以图像形式存入imgAmplitudeBuffer
double t;
for (int i = 0; i < imageHeight; i++)
{
for (int j = 0; j < imageWidth; j++)
{
t = Math.Abs(dctBuffer[i * imageWidth + j]);
if (t > 255)
imgAmplitudeBuffer[i * imageWidth + j] = 255;
else
imgAmplitudeBuffer[i * imageWidth + j] = (byte)(t + 0.5);
}
}
先在方向上实现一维的离散余弦逆变换
tmpBuffer1 = new Complex[imageHeight];
tmpBuffer2 = new double[imageHeight];
for (int u = 0; u < imageWidth; u++)
{
for (int v = 0; v < imageHeight; v++)
{
tmpBuffer1[v] = new Complex((float)dctBuffer[v * imageWidth + u],0);
}
tmpBuffer2 = idct(tmpBuffer1, imageHeight);
for (int v = 0; v < imageHeight; v++)
{
dctBuffer[v * imageWidth + u] = tmpBuffer2[v];
}
}
再在方向上实现一维的离散余弦逆变换
tmpBuffer1 = new Complex[imageWidth];
tmpBuffer2 = new double[imageWidth];
for (int u = 0; u < imageHeight; u++)
{
for (int v = 0; v < imageWidth; v++)//性能瓶颈
{
tmpBuffer1[v] = new Complex((float)dctBuffer[u * imageWidth + v],0);
}
tmpBuffer2 = idct(tmpBuffer1, imageWidth);
for (int v = 0; v < imageWidth; v++)//性能瓶颈
{
dctBuffer[u * imageWidth + v] = tmpBuffer2[v];
}
}
提取恢复后的图像
byte[] imgBuffer = new byte[imageWidth * imageHeight];
//将幅度谱图以图像形式存入imgBuffer
double t;
for (int i = 0; i < imageHeight; i++)
{
for (int j = 0; j < imageWidth; j++)
{
t = dctBuffer[i * imageWidth + j]*255.0/400.0;
if (t > 255)
imgBuffer[i * imageWidth + j] = 255;
else
imgBuffer[i * imageWidth + j] = (byte)(t + 0.5);
}
}
主要的绘制代码如下:
if (firstPainted)//防止窗口resize时候重新计算
{
dctBuffer = dct2(currentBitmap);
//对dctBuffer进行深拷贝
dctTmpBuffer = (double[])dctBuffer.Clone();
currentAmplitudeBitmap = GetAmplitudeBitmap(imgAmplitudeBuffer);
currentIdct2Bitmap = GetIdct2Bitmap(idct2(dctTmpBuffer));
firstPainted = false;
}
由于对图像进行了扩展,所以幅度谱的大小跟原来的图略有差别,结果如下:
- 将节点的变为二进制数
- 将二进制数按比特位倒转
- 将二进制数变换为十进制数,即为重新排序的位置,例: