5.5 影象濾波(均值、高斯、中值、各項異性濾波)
5.5.1 均值濾波
均值濾波是一種經常用到的平滑方法,其對應的模板各個畫素的值為1。在VTK中沒有直接實現均值濾波的類,但是我們可以通過影象卷積運算來實現。卷積運算通過vtkImageConvolve類實現。通過vtkImageConvolve類,只需要設定相應的卷積模板,便可以實現多種空域影象濾波。
下面程式碼說明了怎樣使用vtkImageConvolve類來實現影象的均值濾波:
//中值濾波 #include <vtkSmartPointer.h> #include <vtkJPEGReader.h> #include <vtkImageCast.h> //影象資料型別轉換為計算型別 #include <vtkImageData.h> #include <vtkImageConvolve.h> //影象卷積執行 #include <vtkImageShiftScale.h> //設定畫素值範圍 //#include <vtkImageMandelbrotSource.h> #include <vtkImageActor.h> #include <vtkRenderer.h> #include <vtkImageMandelbrotSource.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkInteractorStyleImage.h> int main() { vtkSmartPointer<vtkJPEGReader> reader = vtkSmartPointer<vtkJPEGReader>::New(); reader->SetFileName("data\\lena-noise.jpg"); reader->Update(); vtkSmartPointer<vtkImageCast> originalCastFilter = vtkSmartPointer<vtkImageCast>::New(); originalCastFilter->SetInputConnection(reader->GetOutputPort()); //建管線 originalCastFilter->SetOutputScalarTypeToFloat(); //設定屬性 originalCastFilter->Update(); vtkSmartPointer<vtkImageConvolve> convolveFilter = vtkSmartPointer<vtkImageConvolve>::New(); convolveFilter->SetInputConnection(originalCastFilter->GetOutputPort()); //建管線 double kernel[25] = { 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04 }; convolveFilter->SetKernel5x5(kernel); convolveFilter->Update(); vtkSmartPointer<vtkImageCast> convCastFilter = vtkSmartPointer<vtkImageCast>::New(); convCastFilter->SetInputData(convolveFilter->GetOutput()); convCastFilter->SetOutputScalarTypeToUnsignedChar(); //轉換為影象資料 convCastFilter->Update(); /////////////////////////////////////////////////// vtkSmartPointer<vtkImageActor> originalActor = vtkSmartPointer<vtkImageActor>::New(); originalActor->SetInputData(reader->GetOutput()); vtkSmartPointer<vtkImageActor> convolvedActor = vtkSmartPointer<vtkImageActor>::New(); convolvedActor->SetInputData(convCastFilter->GetOutput()); //////////////////////// double leftViewport[4] = { 0.0, 0.0, 0.5, 1.0 }; double rightViewport[4] = { 0.5, 0.0, 1.0, 1.0 }; vtkSmartPointer<vtkRenderer> originalRenderer = vtkSmartPointer<vtkRenderer>::New(); originalRenderer->SetViewport(leftViewport); originalRenderer->AddActor(originalActor); originalRenderer->SetBackground(1.0, 1.0, 1.0); originalRenderer->ResetCamera(); vtkSmartPointer<vtkRenderer> convolvedRenderer = vtkSmartPointer<vtkRenderer>::New(); convolvedRenderer->SetViewport(rightViewport); convolvedRenderer->AddActor(convolvedActor); convolvedRenderer->SetBackground(1.0, 1.0, 1.0); convolvedRenderer->ResetCamera(); //////////////////////// vtkSmartPointer<vtkRenderWindow> rw = vtkSmartPointer<vtkRenderWindow>::New();; rw->AddRenderer(originalRenderer); rw->AddRenderer(convolvedRenderer); rw->SetSize(640, 320); rw->Render(); rw->SetWindowName("Smooth by MeanFilter"); vtkSmartPointer<vtkRenderWindowInteractor> rwi = vtkSmartPointer<vtkRenderWindowInteractor>::New(); vtkSmartPointer<vtkInteractorStyleImage> style = vtkSmartPointer<vtkInteractorStyleImage>::New(); rwi->SetInteractorStyle(style); rwi->SetRenderWindow(rw); rwi->Initialize(); rwi->Start(); return 0; }
執行結果如下:
首先vtkJPEGReader物件讀取一幅影象。考慮到進行卷積運算時資料範圍的變化和精度要求,需要先將影象畫素資料型別由unsigned char轉換到float型別,該變換通過vtkImageCast實現,對應的設定函式SetOutputScalarTypeToFloat()。接下來需要定義卷積運算元和卷積模板。vtkImageConvolve類實現影象的卷積運算,它需要兩個輸入。一個是需要進行卷積的影象,這裡為vtkJPEGReader讀取的影象資料,第二個是卷積模板陣列。SetKernel5x5()函式接收一個5x5的卷積模板陣列,即本例上定義的kernel陣列。執行Update()後即可完成卷積運算。需要注意的是,卷積模板對應的係數之和應該為1,否則需要對計算結果進行歸一化處理。另外該類中還定義了3x3和7x7的卷積模板設定函式,使用過程是一樣的。卷積完成以後,再次通過vtkImageCast將float資料型別轉換為unsigned char進行影象顯示。
5.5.2高斯濾波
高斯平滑的原理類似於均值濾波。均值濾波模板的係數都是一樣的,而高斯平滑則是需要根據畫素與模板中心的距離來定義權重。權重的計算方法是採用高斯分佈,離中心越遠,權重越小。
#include <vtkSmartPointer.h> #include <vtkJPEGReader.h> #include <vtkImageCast.h>//影象資料型別轉換 #include <vtkImageData.h> #include <vtkImageGaussianSmooth.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-noise.jpg"); reader->Update(); vtkSmartPointer<vtkImageGaussianSmooth> gaussianSmoothFilter = vtkSmartPointer<vtkImageGaussianSmooth>::New(); gaussianSmoothFilter->SetInputConnection(reader->GetOutputPort()); gaussianSmoothFilter->SetDimensionality(2); gaussianSmoothFilter->SetRadiusFactor(5); //設定模板範圍 gaussianSmoothFilter->SetStandardDeviation(3);//正態分佈/高斯分佈標準差 gaussianSmoothFilter->Update(); vtkSmartPointer<vtkImageActor> originalActor = vtkSmartPointer<vtkImageActor>::New(); originalActor->SetInputData(reader->GetOutput()); vtkSmartPointer<vtkImageActor> smoothedActor = vtkSmartPointer<vtkImageActor>::New(); smoothedActor->SetInputData(gaussianSmoothFilter->GetOutput()); double originalViewport[4] = { 0.0, 0.0, 0.5, 1.0 }; double smoothedViewport[4] = { 0.5, 0.0, 1.0, 1.0 }; vtkSmartPointer<vtkRenderer> originalRenderer = vtkSmartPointer<vtkRenderer>::New(); originalRenderer->SetViewport(originalViewport); originalRenderer->AddActor(originalActor); originalRenderer->ResetCamera(); originalRenderer->SetBackground(1.0, 0, 0); vtkSmartPointer<vtkRenderer> gradientMagnitudeRenderer = vtkSmartPointer<vtkRenderer>::New(); gradientMagnitudeRenderer->SetViewport(smoothedViewport); gradientMagnitudeRenderer->AddActor(smoothedActor); gradientMagnitudeRenderer->ResetCamera(); gradientMagnitudeRenderer->SetBackground(1.0, 1.0, 1.0); vtkSmartPointer<vtkRenderWindow> rw = vtkSmartPointer<vtkRenderWindow>::New(); rw->AddRenderer(originalRenderer); rw->AddRenderer(gradientMagnitudeRenderer); rw->SetSize(640, 320); rw->SetWindowName("Smooth by Gaussian"); vtkSmartPointer<vtkRenderWindowInteractor> rwi = vtkSmartPointer<vtkRenderWindowInteractor>::New(); vtkSmartPointer<vtkInteractorStyleImage> style = vtkSmartPointer<vtkInteractorStyleImage>::New(); rwi->SetInteractorStyle(style); rwi->SetRenderWindow(rw); rwi->Initialize(); rwi->Start(); return 0; }
執行結果如下所示:
vtkImageGaussianSmooth類預設是執行三維高斯濾波;
SetDimensionality()根據需要設定相應的維數;
SetRadiusFactor()用於設定高斯模板的大小,當超出該模板的範圍時,係數取0;
SetStandardDeviation()用於設定高斯分佈函式的標準差。
5.5.3 中值濾波
vtkImageHybridMedian2D實現了對二維影象的中值濾波。其實現原理是,採用一個5x5的模板,逐次將模板中心對應於影象的每個畫素上,將模板影象覆蓋的畫素的中值作為當前畫素的輸出值。
#include <vtkSmartPointer.h>
#include <vtkJPEGReader.h>
#include <vtkImageData.h>
#include <vtkImageCast.h>
#include <vtkImageHybridMedian2D.h>
#include <vtkImageActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkInteractorStyleImage.h>
int main(int argc, char* argv[])
{
vtkSmartPointer<vtkJPEGReader> reader =
vtkSmartPointer<vtkJPEGReader>::New();
reader->SetFileName("data\\lena-noise.jpg");
reader->Update();
vtkSmartPointer<vtkImageHybridMedian2D> hybridMedian =
vtkSmartPointer<vtkImageHybridMedian2D>::New();
hybridMedian->SetInputData(reader->GetOutput());
hybridMedian->Update();
///////////////////////////////////////////////////////
vtkSmartPointer<vtkImageActor> originalActor =
vtkSmartPointer<vtkImageActor>::New();
originalActor->SetInputData(reader->GetOutput());
vtkSmartPointer<vtkImageActor> hybridMedianActor =
vtkSmartPointer<vtkImageActor>::New();
hybridMedianActor->SetInputData(hybridMedian->GetOutput());
/////////////////////
double originalViewport[4] = { 0.0, 0.0, 0.5, 1.0 };
double hybridMedianViewport[4] = { 0.5, 0.0, 1.0, 1.0 };
vtkSmartPointer<vtkRenderer> originalRenderer =
vtkSmartPointer<vtkRenderer>::New();
originalRenderer->SetViewport(originalViewport);
originalRenderer->AddActor(originalActor);
originalRenderer->ResetCamera();
originalRenderer->SetBackground(1.0, 0, 0);
vtkSmartPointer<vtkRenderer> hybridMedianRenderer =
vtkSmartPointer<vtkRenderer>::New();
hybridMedianRenderer->SetViewport(hybridMedianViewport);
hybridMedianRenderer->AddActor(hybridMedianActor);
hybridMedianRenderer->ResetCamera();
hybridMedianRenderer->SetBackground(1.0, 1.0, 1.0);
//////////////////////////
vtkSmartPointer<vtkRenderWindow> rw =
vtkSmartPointer<vtkRenderWindow>::New();
rw->AddRenderer(originalRenderer);
rw->AddRenderer(hybridMedianRenderer);
rw->SetSize(640, 320);
rw->Render();
rw->SetWindowName("MedianFilterExample");
vtkSmartPointer<vtkRenderWindowInteractor> rwi =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
vtkSmartPointer<vtkInteractorStyleImage> style =
vtkSmartPointer<vtkInteractorStyleImage>::New();
rwi->SetInteractorStyle(style);
rwi->SetRenderWindow(rw);
rwi->Initialize();
rwi->Start();
return 0;
}
執行結果如下:
該類使用非常簡單,不需要使用者設定任何引數。該方法能夠有效的保持影象邊緣,並對於椒鹽噪聲有較好的抑制作用。對於三維影象,則使用vtkImageHybridMedian3D類。
5.5.4 各項異性濾波
高斯平滑方法在平滑噪聲的同時,模糊了影象的重要邊緣影象。
各向異性濾波是一種基於偏微分方程的濾波技術,建立於熱量的各向異性擴散理論。
各向異性濾波在影象的平坦區域選擇大尺度平滑,而邊緣區域則選擇小尺度的平滑,在抑制噪聲的同時保持了影象的邊緣資訊。
vtkImageAnisotropicDiffusion2D(vtkImageAnisotropicDiffusion3D)實現影象各向異性擴散濾波,程式碼如下:
#include <vtkSmartPointer.h>
#include <vtkJPEGReader.h>
#include <vtkImageCast.h>
#include <vtkImageAnisotropicDiffusion2D.h>
#include <vtkImageActor.h>
#include <vtkCamera.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-noise.jpg");
vtkSmartPointer<vtkImageAnisotropicDiffusion2D> diffusion =
vtkSmartPointer<vtkImageAnisotropicDiffusion2D>::New();
diffusion->SetInputConnection(reader->GetOutputPort());
diffusion->SetNumberOfIterations(200); //用於設定迭代次數
diffusion->SetDiffusionThreshold(5); //小於該閾值擴散
diffusion->Update();
/////////////////////////////////////////////////////////////////
vtkSmartPointer<vtkImageActor> originalActor =
vtkSmartPointer<vtkImageActor>::New();
originalActor->SetInputData(reader->GetOutput());
vtkSmartPointer<vtkImageActor> diffusionActor =
vtkSmartPointer<vtkImageActor>::New();
diffusionActor->SetInputData(diffusion->GetOutput());
////////////////////
double leftViewport[4] = { 0.0, 0.0, 0.5, 1.0 };
double rightViewport[4] = { 0.5, 0.0, 1.0, 1.0 };
vtkSmartPointer<vtkCamera> camera =
vtkSmartPointer<vtkCamera>::New();
vtkSmartPointer<vtkRenderer> leftRenderer =
vtkSmartPointer<vtkRenderer>::New();
leftRenderer->SetViewport(leftViewport);
leftRenderer->AddActor(originalActor);
leftRenderer->SetBackground(1.0, 1.0, 1.0);
leftRenderer->SetActiveCamera(camera);
leftRenderer->ResetCamera();
vtkSmartPointer<vtkRenderer> rightRenderer =
vtkSmartPointer<vtkRenderer>::New();
rightRenderer->SetViewport(rightViewport);
rightRenderer->SetBackground(1.0, 1.0, 1.0);
rightRenderer->AddActor(diffusionActor);
rightRenderer->SetActiveCamera(camera);
/////////////////////
vtkSmartPointer<vtkRenderWindow> rw =
vtkSmartPointer<vtkRenderWindow>::New();
rw->AddRenderer(leftRenderer);
rw->AddRenderer(rightRenderer);
rw->SetSize(540, 320);
rw->SetWindowName("Smooth by AnistropicFilter");
vtkSmartPointer<vtkRenderWindowInteractor> rwi =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
vtkSmartPointer<vtkInteractorStyleImage> style =
vtkSmartPointer<vtkInteractorStyleImage>::New();
rwi->SetInteractorStyle(style);
rwi->SetRenderWindow(rw);
rwi->Initialize();
rwi->Start();
return 0;
}
執行結果如下:
vtkImageAnisotropicDiffusion2D類通過迭代方法實現。
其中SetNumberOfIterations()用於設定迭代的次數;
各向異性擴散濾波原理是在梯度較小的畫素處進行較大幅度擴散,而在大梯度處則只進行細微的擴散。因此需要設定一個擴算的閾值DiffusionThreshold,這個閾值與影象的梯度有關。SetDiffusionThreshold()即是用來設定擴散閾值。該類中還有一個梯度標誌GradientMagnitudeThreshold,用來設定梯度運算元。當該標誌開時梯度通過中心差分方法計算;當標誌為關時,需要單獨處理每個相鄰畫素。當畫素與相鄰畫素梯度小於DiffusionThreshold時進行擴散處理。
參考資料:
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