5.6.1 快速傅立葉變換(FFT+RFFT)
1.影象頻域處理的意義
在影象處理和分析中,經常會將影象從影象空間轉換到其他空間中,並利用這些空間的特點進行對轉換後圖像進行分析處理,然後再將處理後的影象轉換到影象空間中,這稱之為影象變換。
在一些影象處理和分析中通過空間變換往往會取得更有效的結果。影象頻域處理是指將影象從影象空間轉換到頻域空間進行處理的過程。最常用的頻域轉換是傅立葉變換。
傅立葉變換的計算量較大,人們為了提高速度,提出了快速傅立葉變換,並得到了廣泛的應用。本篇部落格主要來學習VTK中的快速傅立葉變換。
2.快速傅立葉變換及其逆變換
快速傅立葉變換(Fast Fourier Transform),簡稱做FFT。它是根據離散傅氏變換的奇、偶、虛、實等特性,對離散傅立葉變換的演算法進行改進獲得的。傅立葉變換是可逆的,其逆變換為RFFT。
FFT在數字影象處理中有著廣泛的應用,例如數字影象頻域濾波,去噪,增強等等。 目前VTK中兩變換都已經實現,對應的類分別為vtkImageFFT和vtkImageRFFT。
vtkImageFFT和vtkImageRFFT的輸入為實數或者複數資料,輸出都為複數資料。因此,vtkImageFFT與vtkImageRFFT的輸出結果不能直接顯示,因為VTK會將其當做彩色影象顯示,需要通過vtkImageExtractComponents提取某一組分影象顯示。
VTK頻率域的影象處理步驟如下:
下面程式碼演示了怎樣對影象進行傅立葉變換,以及將傅立葉變換結果進行逆變換:
#include <vtkSmartPointer.h> #include <vtkJPEGReader.h> #include <vtkImageFFT.h> #include <vtkImageExtractComponents.h> #include <vtkImageData.h> #include <vtkImageShiftScale.h> #include <vtkImageRFFT.h> #include <vtkImageActor.h> #include <vtkrenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkInteractorStyleImage.h> int main() { vtkSmartPointer<vtkJPEGReader> reader = vtkSmartPointer<vtkJPEGReader>::New(); reader->SetFileName("data:\\lena.jpg"); reader->Update(); //FFT////////////////////////////////////////////////// vtkSmartPointer<vtkImageFFT> imgFFT = vtkSmartPointer<vtkImageFFT>::New(); imgFFT->SetInputConnection(reader->GetOutputPort()); imgFFT->SetDimensionality(2); imgFFT->Update(); //其輸出為一個畫素型別為複數的vtkImageData資料,即每個畫素值為兩個組分(Component):複數實部和虛部 // the resualts are complex, we shoule extract one component to display vtkSmartPointer<vtkImageExtractComponents> imgComponent = vtkSmartPointer<vtkImageExtractComponents>::New(); imgComponent->SetInputConnection(imgFFT->GetOutputPort()); imgComponent->SetComponents(0);//指定提取實部 // norm image range double Range[2]; imgComponent->GetOutput()->GetScalarRange(Range); vtkSmartPointer<vtkImageShiftScale> imgfftShiftScale = vtkSmartPointer<vtkImageShiftScale>::New(); imgfftShiftScale->SetOutputScalarTypeToUnsignedChar(); //vtkImageActor類僅支援unsigned char資料型別的影象,所以進行資料型別轉換 imgfftShiftScale->SetScale(255.0 / (Range[1] - Range[0])); imgfftShiftScale->SetShift(-Range[0]); imgfftShiftScale->SetInputConnection(imgComponent->GetOutputPort()); imgfftShiftScale->Update(); //RFFT///////////////////////////////////////////////////// vtkSmartPointer<vtkImageRFFT> imgRFFT = vtkSmartPointer<vtkImageRFFT>::New(); imgRFFT->SetInputConnection(imgFFT->GetOutputPort()); imgRFFT->SetDimensionality(2); imgRFFT->Update(); vtkSmartPointer<vtkImageExtractComponents> imgComponetNew = vtkSmartPointer<vtkImageExtractComponents>::New(); imgComponetNew->SetInputConnection(imgRFFT->GetOutputPort()); imgComponetNew->SetComponents(0); double RangeNew[2]; imgComponetNew->GetOutput()->GetScalarRange(RangeNew); vtkSmartPointer<vtkImageShiftScale> imgrfftShiftScale = vtkSmartPointer<vtkImageShiftScale>::New(); imgrfftShiftScale->SetOutputScalarTypeToUnsignedChar(); imgrfftShiftScale->SetScale(255.0 / (RangeNew[1] - RangeNew[0])); imgrfftShiftScale->SetInputConnection(imgComponetNew->GetOutputPort()); imgrfftShiftScale->Update(); /////////////////////////////////////////////////////////////// //謹記vtkImageActor僅能夠顯示UnsignedChar型別資料 vtkSmartPointer<vtkImageActor> origActor = vtkSmartPointer<vtkImageActor>::New(); origActor->SetInputData(reader->GetOutput()); vtkSmartPointer<vtkImageActor> fftActor = vtkSmartPointer<vtkImageActor>::New(); fftActor->SetInputData(imgFFT->GetOutput()); vtkSmartPointer<vtkImageActor> rfftActor = vtkSmartPointer<vtkImageActor>::New(); rfftActor->SetInputData(imgrfftShiftScale->GetOutput()); /////////////////////// double origView[4] = { 0, 0, 0.33, 1 }; double fftView[4] = { 0.33, 0, 0.66, 1 }; double rfftView[4] = { 0.66, 0, 1, 1 }; vtkSmartPointer<vtkRenderer> origRender = vtkSmartPointer<vtkRenderer>::New(); origRender->SetViewport(origView); origRender->AddActor(origActor); origRender->ResetCamera(); origRender->SetBackground(1, 1, 1); vtkSmartPointer<vtkRenderer> fftRender = vtkSmartPointer<vtkRenderer>::New(); fftRender->SetViewport(fftView); fftRender->AddActor(fftActor); fftRender->ResetCamera(); fftRender->SetBackground(1, 1, 1); vtkSmartPointer<vtkRenderer> rfftRender = vtkSmartPointer<vtkRenderer>::New(); rfftRender->SetViewport(rfftView); rfftRender->AddActor(rfftActor); rfftRender->ResetCamera(); rfftRender->SetBackground(1, 1, 1); ///// vtkSmartPointer<vtkRenderWindow> rw = vtkSmartPointer<vtkRenderWindow>::New(); rw->AddRenderer(origRender); rw->AddRenderer(fftRender); rw->AddRenderer(rfftRender); rw->SetWindowName("Frequncy_FFT_RFFT"); rw->SetSize(960, 320); vtkSmartPointer<vtkRenderWindowInteractor> rwi = vtkSmartPointer<vtkRenderWindowInteractor>::New(); vtkSmartPointer<vtkInteractorStyleImage> style = vtkSmartPointer<vtkInteractorStyleImage>::New(); rwi->SetInteractorStyle(style); rwi->SetRenderWindow(rw); rwi->Initialize(); rwi->Start(); return 0; }
執行結果如下:
首先建立一個JPEG影象reader來讀取一副灰度影象。然後定義一個vtkImageFFT指標,直接接收reader的輸出即原影象資料作為輸入進行二維快速傅立葉變換。其輸出為一個畫素型別為複數的vtkImageData資料,即每個畫素值為兩個組分(Component):複數實部和虛部。因此如果直接顯示這個vtkImageData,會發現是一個彩色影象。如果需要顯示頻域影象,需要通過vtkImageExtractComponents類來提取某一個組分影象來顯示。上例中通過定義vtkImageExtractComponents類指標,利用函式SetComponents(0)指定提取實部影象顯示;由於vtkImageActor類僅支援unsigned char資料型別的影象,利用vtkImageCast類的SetOutputScalarTypeToUnsignedChar()指定輸出型別為unsigned char,將FFT結果影象轉換為需要的型別
對於逆變換的過程也是類似。首先定義vtkImageRFFT指標,並接收輸入為vtkImageFFT指標的輸出,呼叫Update執行函式完成快速傅立葉逆變換。vtkImageRFFT的輸出同樣為一副複數影象,通常不能直接顯示或者進行其他操作。對於傅立葉逆變換的影象中虛數部分值為0,實數部分影象即為重建的原始影象。因此再次利用vtkImageExtractComponents提取實數部分影象,並通過SetOutputScalarTypeToUnsignedChar()將影象轉換為unsigned char型別進行顯示。
參考資料:
1.《The Visualization Toolkit – AnObject-Oriented Approach To 3D Graphics (4th Edition)》
2. 張曉東, 羅火靈. VTK圖形影象開發進階[M]. 機械工業出版社, 2015.
所用軟體:vtk7.0+visual studio 2013
注:此文知識學習筆記,僅記錄完整程式和實現結果,具體原理參見:
https://blog.csdn.net/www_doling_net/article/details/8541534
https://blog.csdn.net/shenziheng1/article/category/6114053/4