1. 程式人生 > >頻域處理:傅立葉變換及小波變換

頻域處理:傅立葉變換及小波變換

頻域處理:傅立葉變換及小波變換

引言

影象處理–>頻域處理–>傅立葉變換、小波變換。用另一種方法來觀察世界的話,你會發現世界是永恆不變的。

1、傅立葉變換

傅立葉變換

尤拉公式所描繪的,是一個隨著時間變化,在複平面上做圓周運動的點,隨著時間的改變,在時間軸上就成了一條螺旋線。如果只看它的實數部分,也就是螺旋線在左側的投影,就是一個最基礎的餘弦函式。而右側的投影則是一個正弦函式。
由尤拉公式知道:正弦波的疊加

,也可以理解為螺旋線的疊加在實數空間的投影。而螺旋線的疊加:

複數就是一個能把平面進行均勻縮放和旋轉的乘子。
通過時域到頻域的變換,得到了一個從側面看的頻譜,但是這個頻譜並沒有包含時域中全部的資訊。因為頻譜只代表每一個對應的正弦波的振幅是多少,而沒有提到相位。基礎的正弦波A.sin(wt+θ)中,振幅,頻率,相位缺一不可,不同相位決定了波的位置,所以對於頻域分析,僅僅有頻譜(振幅譜)是不夠的,還需要一個相位譜。
糾正一個概念:時間差並不是相位差。如果將全部週期看作2Pi或者360度的話,相位差則是時間差在一個週期中所佔的比例。我們將時間差除週期再乘2Pi,就得到了相位差。
在完整的立體圖中,將投影得到的時間差依次除以所在頻率的週期,就得到了最下面的相位譜。所以,頻譜是從側面看,相位譜是從下面看。

,

頻率內低外高,圖二隻留下中間部分也就是中間部分也就是主體部分,細節部分去除;高通相反。

短時傅立葉變換(Short-time Fourier Transform,STFT)也叫加窗傅立葉變換,顧名思義,就是因為傅立葉變換的時域太長了,所以要弄短一點,這樣就有了區域性性。
定義:把整個時域過程分解成無數個等長的小過程,每個小過程近似平穩,再傅立葉變換,就知道在哪個時間點上出現了什麼頻率了。”這就是短時傅立葉變換。
時域上分成一段一段做FFT,就可以知道頻率成分隨著時間的變化情況。

時頻圖

2、小波變換

小波變換
對於加窗傅立葉變換讓人頭疼的就是視窗的大小問題,如果讓視窗的大小可以改變,就完美了。小波就是基於這個思路,但是不同的是。STFT是給訊號加窗,分段做FFT;而小波變換並沒有採用窗的思想,更沒有做傅立葉變換。小波直接把傅立葉變換的基給換了——將無限長的三角函式基換成了有限長的會衰減的小波基。這樣不僅能夠獲取頻率,還可以定位到時間。即是做傅立葉變換隻能得到一個頻譜,做小波變換卻可以得到一個時頻譜。

從公式可以看出,不同於傅立葉變換,變數只有頻率ω,小波變換有兩個變數:尺度a(scale)和平移量 τ(translation)。尺度a控制小波函式的伸縮,平移量 τ控制小波函式的平移。尺度就對應於頻率(反比),平移量 τ就對應於時間。

當伸縮、平移到這麼一種重合情況時,也會相乘得到一個大的值。這時候和傅立葉變換不同的是,這不僅可以知道訊號有這樣頻率的成分,而且知道它在時域上存在的具體位置。而當每個尺度下都平移著和訊號乘過一遍後,就知道訊號在每個位置都包含哪些頻率成分,就可以對非穩定訊號做時頻分析。

3、程式

fft:

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('./opencv.png',0)
f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)
magnitude_spectrum = 20*np.log(np.abs(fshift))
plt.subplot(121),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(magnitude_spectrum, cmap = 'gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()

opencv+dft

#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

using namespace std;
using namespace cv;

int main()
{
    Mat I = imread("lena.jpg", IMREAD_GRAYSCALE);       //讀入影象灰度圖

    //判斷影象是否載入成功
    if (I.empty())
    {
        cout << "影象載入失敗!" << endl;
        return -1;
    }
    else
        cout << "影象載入成功!" << endl << endl;

    Mat padded;                 //以0填充輸入影象矩陣
    int m = getOptimalDFTSize(I.rows);
    int n = getOptimalDFTSize(I.cols);

    //填充輸入影象I,輸入矩陣為padded,上方和左方不做填充處理
    copyMakeBorder(I, padded, 0, m - I.rows, 0, n - I.cols, BORDER_CONSTANT, Scalar::all(0));

    Mat planes[] = { Mat_<float>(padded), Mat::zeros(padded.size(),CV_32F) };
    Mat complexI;
    merge(planes, 2, complexI);     //將planes融合合併成一個多通道陣列complexI

    dft(complexI, complexI);        //進行傅立葉變換

    //計算幅值,轉換到對數尺度(logarithmic scale)
    //=> log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
    split(complexI, planes);        //planes[0] = Re(DFT(I),planes[1] = Im(DFT(I))
                                    //即planes[0]為實部,planes[1]為虛部
    magnitude(planes[0], planes[1], planes[0]);     //planes[0] = magnitude
    Mat magI = planes[0];

    magI += Scalar::all(1);
    log(magI, magI);                //轉換到對數尺度(logarithmic scale)

    //如果有奇數行或列,則對頻譜進行裁剪
    magI = magI(Rect(0, 0, magI.cols&-2, magI.rows&-2));

    //重新排列傅立葉影象中的象限,使得原點位於影象中心
    int cx = magI.cols / 2;
    int cy = magI.rows / 2;

    Mat q0(magI, Rect(0, 0, cx, cy));       //左上角影象劃定ROI區域
    Mat q1(magI, Rect(cx, 0, cx, cy));      //右上角影象
    Mat q2(magI, Rect(0, cy, cx, cy));      //左下角影象
    Mat q3(magI, Rect(cx, cy, cx, cy));     //右下角影象

    //變換左上角和右下角象限
    Mat tmp;
    q0.copyTo(tmp);
    q3.copyTo(q0);
    tmp.copyTo(q3);

    //變換右上角和左下角象限
    q1.copyTo(tmp);
    q2.copyTo(q1);
    tmp.copyTo(q2);

    //歸一化處理,用0-1之間的浮點數將矩陣變換為可視的影象格式
    normalize(magI, magI, 0, 1, CV_MINMAX);

    imshow("輸入影象", I);
    imshow("頻譜圖", magI);
    waitKey(0);


    return 0;
}

小波 變換.