VTK? Learning? Image smoothing? Mean filter? Gaussian filter? Median filter? Anisotropic filter

Keywords: less

1. Mean filtering

Mean filtering is a kind of smoothing method which is often used. The value of each pixel of the corresponding template is 1. stay VTK There is no class to implement mean filtering directly, but we can do it by image convolution. Convolution operation passes vtkImageConvolve Class implementation. adopt vtkImageConvolve Class, only need to set the corresponding convolution template, can realize a variety of spatial image filtering.

The following code shows how to use vtkImageConvolve Class to realize the mean filtering of images:
 

#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL);
 
#include <vtkSmartPointer.h>
#include <vtkJPEGReader.h>
#Include < vtkimagecast. H > / / convert image data type to calculation type
#include <vtkImageData.h>
#Include < vtkimageconvolute. H > / / image convolution operation
#Include < vtkimageshiftscale. H > / / set pixel value range
//#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()); //Build pipelines
	originalCastFilter->SetOutputScalarTypeToFloat(); //set a property
	originalCastFilter->Update();
 
	vtkSmartPointer<vtkImageConvolve> convolveFilter =
		vtkSmartPointer<vtkImageConvolve>::New();
	convolveFilter->SetInputConnection(originalCastFilter->GetOutputPort()); //Build pipelines
 
	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(); //Convert to image data
	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;
}

First, the vtkJPEGReader object reads an image. In consideration of the change of data range and precision requirements during convolution operation, it is necessary to first convert the image pixel data type from unsigned char to float type, which is realized by vtkImageCast, and the corresponding setting function SetOutputScalarTypeToFloat(). Next, we need to define convolution operator and convolution template. The vtkimageconvolute class implements image convolution, which requires two inputs. One is the image to be convoluted. Here is the image data read by vtkJPEGReader. The second is the array of convolution templates. The SetKernel5x5() function receives an array of 5x5 convolution templates, which is the kernel array defined in this example. Perform Update() to complete the convolution. It should be noted that the sum of the coefficients corresponding to the convolution template should be 1, otherwise the calculation results need to be normalized. In addition, the convolution template setting functions of 3 x 3 and 7 x 7 are defined in this class, and the use process is the same. After the convolution is completed, the float data type is converted to unsigned char for image display through vtkImageCast again.
The results of mean filtering are as follows:

2. Gaussian filtering

The principle of Gaussian smoothing is similar to mean filtering. The coefficients of the mean filtering template are the same, while the Gaussian smoothing needs to define the weight according to the distance between the pixel and the template center. The weight is calculated by Gaussian distribution. The farther away from the center, the smaller the weight.

Here's a utilization Gauss An example of image smoothing by filtering:

#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); //Set template range
	gaussianSmoothFilter->SetStandardDeviation(3);//Standard deviation of normal distribution / Gaussian distribution
	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;
}

The vtkimagegaussian smooth class performs 3D Gaussian filtering by default;
SetDimensionality() sets the corresponding dimension as required;
SetRadiusFactor() is used to set the size of the Gaussian template. When it is beyond the scope of the template, the coefficient is 0;
SetStandardDeviation() is used to set the standard deviation of the Gaussian distribution function.
The Gaussian smoothing effect is as follows:

3. Median filtering

vtkImageHybridMedian2D The median filtering of two-dimensional image is realized. The principle is to use a 5 x5 The center of the template corresponds to each pixel of the image one by one, and the middle value of the pixel covered by the template image is taken as the output value of the current pixel.

#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;
}

This class is very simple to use and does not require the user to set any parameters. This method can effectively keep the edge of the image, and has a good inhibition effect on salt and pepper noise. For 3D images, use the vtkImageHybridMedian3D class.

The results are as follows:

 

4. Anisotropic diffusion filtering

Gaussian smoothing method not only smoothes the noise, but also blurs the important edge image.
//Anisotropic filtering is a kind of filtering technology based on partial differential equation, which is based on the anisotropic diffusion theory of heat.
//Anisotropic filtering selects large-scale smoothing in the flat area of the image and small-scale smoothing in the edge area, which keeps the edge information of the image while suppressing the noise.
vtkImageAnisotropicDiffusion2D(vtkImageAnisotropicDiffusion3D)The code is as follows:
 

#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 below this threshold
	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;
}

The vtkImageAnisotropicDiffusion2D class is implemented by an iterative method.
Where SetNumberOfIterations() is used to set the number of iterations;
The principle of anisotropic diffusion filtering is that the pixels with small gradient are diffused greatly, while the pixels with large gradient are diffused only slightly. Therefore, it is necessary to set an extended threshold, which is related to the gradient of the image. SetDiffusionThreshold() is used to set the diffusion threshold. There is also a gradient flag in this class, GradientMagnitudeThreshold, which is used to set gradient operators. When the flag is on, the gradient is calculated by the center difference method; when the flag is off, each adjacent pixel needs to be processed separately. When the gradient between the pixel and the adjacent pixel is less than the diffussionthreshold, the diffusion processing is performed.
The following figure is the result of anisotropic diffusion filtering:

Published 152 original articles, won praise 11, visited 10000+
Private letter follow

Posted by blueman on Fri, 21 Feb 2020 02:14:08 -0800