1.均值滤波

均值滤波是一种经常用到的平滑方法,其对应的模板各个像素的值为1。在VTK中没有直接实现均值滤波的类,但是我们可以通过图像卷积运算来实现。卷积运算通过vtkImageConvolve类实现。通过vtkImageConvolve类,只需要设置相应的卷积模板,便可以实现多种空域图像滤波。

下面代码说明了怎样使用vtkImageConvolve类来实现图像的均值滤波:
 

#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL);

#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("lena.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进行图像显示。 均值滤波的结果如下:

VTK_Learning_图像平滑_均值滤波器_高斯滤波器__中值滤波器_各向异性滤波_高斯滤波器

 2.高斯滤波

高斯平滑的原理类似于均值滤波。均值滤波模板的系数都是一样的,而高斯平滑则是需要根据像素与模板中心的距离来定义权重。权重的计算方法是采用高斯分布,离中心越远,权重越小。

下面是一个利用Gauss滤波进行图像平滑的实例:

#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL);

#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>
//#include <vtkImageEllipsoidSource.h>

int main()
{
vtkSmartPointer<vtkJPEGReader> reader =
vtkSmartPointer<vtkJPEGReader>::New();
reader->SetFileName("lena.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()用于设置高斯分布函数的标准差。 高斯平滑效果如下所示:

VTK_Learning_图像平滑_均值滤波器_高斯滤波器__中值滤波器_各向异性滤波_高斯滤波器_02

3.中值滤波

vtkImageHybridMedian2D实现了对二维图像的中值滤波。其实现原理是,采用一个5x5的模板,逐次将模板中心对应于图像的每个像素上,将模板图像覆盖的像素的中值作为当前像素的输出值。

#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL);

#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("lena.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类。

执行结果如下:

 

VTK_Learning_图像平滑_均值滤波器_高斯滤波器__中值滤波器_各向异性滤波_均值滤波器_03

4.各向异性扩散滤波

高斯平滑方法在平滑噪声的同时,模糊了图像的重要边缘图像。
各向异性滤波是一种基于偏微分方程的滤波技术,建立于热量的各向异性扩散理论。
各向异性滤波在图像的平坦区域选择大尺度平滑,而边缘区域则选择小尺度的平滑,在抑制噪声的同时保持了图像的边缘信息。
vtkImageAnisotropicDiffusion2D(vtkImageAnisotropicDiffusion3D)实现图像各向异性扩散滤波,代码如下:
 

#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL);

#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("lena.jpg");

vtkSmartPointer<vtkImageAnisotropicDiffusion2D> diffusion =
vtkSmartPointer<vtkImageAnisotropicDiffusion2D>::New();
diffusion->SetInputConnection(reader->GetOutputPort());
diffusion->SetNumberOfIterations(100);
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, 0, 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(640, 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时进行扩散处理。
下图是进行各向异性扩散滤波处理的结果:

VTK_Learning_图像平滑_均值滤波器_高斯滤波器__中值滤波器_各向异性滤波_高斯滤波器_04