VTK随笔八:VTK图像处理(边缘检测、图像平滑、频域处理)

一、边缘检测

1、梯度算子

         梯度算子对应于图像一阶导数。图像一阶导数一般是通过差分运算来近似的。VTK中可以用 vtklmageGradient 类计算图像梯度。

        注意:图像梯度是一个矢量,具有方向和大小,因此vtkImageGradient的计算结果是一个梯度场,即每个像素值都是一个梯度矢量。显示梯度图像时需要计算每个像素点的梯度大小,即模值。

示例代码:

#include <QApplication>
#include <vtkSmartPointer.h>
#include <vtkImageData.h>
#include <vtkRenderer.h>
#include <vtkImageActor.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <QVTKOpenGLNativeWidget.h>
#include <vtkJPEGReader.h>
#include <vtkImageGradient.h>
#include <vtkImageMagnitude.h>
#include <vtkImageShiftScale.h>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    vtkSmartPointer<vtkJPEGReader> reader = vtkSmartPointer<vtkJPEGReader>::New();
    reader->SetFileName("D:/data/lena-gray.jpg");
    reader->Update();

    vtkSmartPointer<vtkImageGradient> gradientFilter = vtkSmartPointer<vtkImageGradient>::New();
    gradientFilter->SetInputConnection(reader->GetOutputPort());
    gradientFilter->SetDimensionality(2);

    vtkSmartPointer<vtkImageMagnitude> magnitudeFilter = vtkSmartPointer<vtkImageMagnitude>::New();
    magnitudeFilter->SetInputConnection(gradientFilter->GetOutputPort());
    magnitudeFilter->Update();

    double range[2];
    magnitudeFilter->GetOutput()->GetScalarRange(range);

    vtkSmartPointer<vtkImageShiftScale> ShiftScale = vtkSmartPointer<vtkImageShiftScale>::New();
    ShiftScale->SetOutputScalarTypeToUnsignedChar();
    ShiftScale->SetScale( 255 / range[1] );
    ShiftScale->SetInputConnection(magnitudeFilter->GetOutputPort());
    ShiftScale->Update();

    vtkSmartPointer<vtkImageActor> originalActor = vtkSmartPointer<vtkImageActor>::New();
    originalActor->SetInputData(reader->GetOutput());

    vtkSmartPointer<vtkImageActor> gradActor = vtkSmartPointer<vtkImageActor>::New();
    gradActor->SetInputData(ShiftScale->GetOutput());

    double originalViewport[4] = {0.0, 0.0, 0.5, 1.0};
    double gradviewport[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, 1.0, 1.0);

    vtkSmartPointer<vtkRenderer> gradRenderer = vtkSmartPointer<vtkRenderer>::New();
    gradRenderer->SetViewport(gradviewport);
    gradRenderer->AddActor(gradActor);
    gradRenderer->ResetCamera();
    gradRenderer->SetBackground(1.0, 1.0, 1.0);

    vtkSmartPointer<vtkGenericOpenGLRenderWindow> renderWindow = vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New();
    renderWindow->AddRenderer(originalRenderer);
    renderWindow->AddRenderer(gradRenderer);

    QVTKOpenGLNativeWidget w;
    w.resize(640,320);
    w.setRenderWindow(renderWindow);
    w.setWindowTitle("ImageGradientExample");
    w.show();

    return a.exec();
}

效果如下: 

 

        索贝尔(Sobel)算子也是一种常用的梯度算子。其计算稍微复杂些,采用3x3 的模板。计算时模板在图像上移动,并在每个位置上计算对应中心像素的梯度值。

VTK中用 vtkSobel2D计算图像的 Sobel算子: 

利用 Sobel 算子计算图像的梯度图像示例代码:

#include <QApplication>
#include <vtkSmartPointer.h>
#include <vtkImageData.h>
#include <vtkRenderer.h>
#include <vtkImageActor.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <QVTKOpenGLNativeWidget.h>
#include <vtkJPEGReader.h>
#include <vtkImageSobel2D.h>
#include <vtkImageExtractComponents.h>
#include <vtkImageMathematics.h>
#include <vtkImageShiftScale.h>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    vtkSmartPointer<vtkJPEGReader> reader = vtkSmartPointer<vtkJPEGReader>::New();
    reader->SetFileName("D:/data/lena-gray.jpg");
    reader->Update();

    vtkSmartPointer<vtkImageSobel2D> sobelFilter = vtkSmartPointer<vtkImageSobel2D>::New();
    sobelFilter->SetInputConnection(reader->GetOutputPort());

    vtkSmartPointer<vtkImageExtractComponents> extractXFilter = vtkSmartPointer<vtkImageExtractComponents>::New();
    extractXFilter->SetComponents(0);
    extractXFilter->SetInputConnection(sobelFilter->GetOutputPort());
    extractXFilter->Update();

    double xRange[2];
    extractXFilter->GetOutput()->GetScalarRange(xRange);

    vtkSmartPointer<vtkImageMathematics> xImageAbs = vtkSmartPointer<vtkImageMathematics>::New();
    xImageAbs->SetOperationToAbsoluteValue();
    xImageAbs->SetInputConnection(extractXFilter->GetOutputPort());
    xImageAbs->Update();

    vtkSmartPointer<vtkImageShiftScale> xShiftScale = vtkSmartPointer<vtkImageShiftScale>::New();
    xShiftScale->SetOutputScalarTypeToUnsignedChar();
    xShiftScale->SetScale( 255 / xRange[1] );
    xShiftScale->SetInputConnection(xImageAbs->GetOutputPort());
    xShiftScale->Update();

    vtkSmartPointer<vtkImageExtractComponents> extractYFilter = vtkSmartPointer<vtkImageExtractComponents>::New();
    extractYFilter->SetComponents(1);
    extractYFilter->SetInputConnection(sobelFilter->GetOutputPort());
    extractYFilter->Update();

    double yRange[2];
    extractYFilter->GetOutput()->GetScalarRange(yRange);

    vtkSmartPointer<vtkImageMathematics> yImageAbs = vtkSmartPointer<vtkImageMathematics>::New();
    yImageAbs->SetOperationToAbsoluteValue();
    yImageAbs->SetInputConnection(extractYFilter->GetOutputPort());
    yImageAbs->Update();

    vtkSmartPointer<vtkImageShiftScale> yShiftScale = vtkSmartPointer<vtkImageShiftScale>::New();
    yShiftScale->SetOutputScalarTypeToUnsignedChar();
    yShiftScale->SetScale( 255 / yRange[1] );
    yShiftScale->SetInputConnection(yImageAbs->GetOutputPort());
    yShiftScale->Update();

    vtkSmartPointer<vtkImageActor> originalActor = vtkSmartPointer<vtkImageActor>::New();
    originalActor->SetInputData(reader->GetOutput());

    vtkSmartPointer<vtkImageActor> xActor = vtkSmartPointer<vtkImageActor>::New();
    xActor->SetInputData(xShiftScale->GetOutput());

    vtkSmartPointer<vtkImageActor> yActor = vtkSmartPointer<vtkImageActor>::New();
    yActor->SetInputData(yShiftScale->GetOutput());

    double originalViewport[4] = {0.0, 0.0, 0.33, 1.0};
    double xViewport[4] = {0.33, 0.0, 0.66, 1.0};
    double yViewport[4] = {0.66, 0.0, 1.0, 1.0};

    vtkSmartPointer<vtkRenderer> originalRenderer = vtkSmartPointer<vtkRenderer>::New();
    originalRenderer->SetViewport(originalViewport);
    originalRenderer->AddActor(originalActor);
    originalRenderer->ResetCamera();
    originalRenderer->SetBackground(1.0, 1.0, 1.0);

    vtkSmartPointer<vtkRenderer> xRenderer = vtkSmartPointer<vtkRenderer>::New();
    xRenderer->SetViewport(xViewport);
    xRenderer->AddActor(xActor);
    xRenderer->ResetCamera();
    xRenderer->SetBackground(1.0, 1.0, 1.0);

    vtkSmartPointer<vtkRenderer> yRenderer = vtkSmartPointer<vtkRenderer>::New();
    yRenderer->SetViewport(yViewport);
    yRenderer->AddActor(yActor);
    yRenderer->ResetCamera();
    yRenderer->SetBackground(1.0, 1.0, 1.0);

    vtkSmartPointer<vtkGenericOpenGLRenderWindow> renderWindow = vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New();
    renderWindow->AddRenderer(originalRenderer);
    renderWindow->AddRenderer(xRenderer);
    renderWindow->AddRenderer(yRenderer);

    QVTKOpenGLNativeWidget w;
    w.resize(900,300);
    w.setRenderWindow(renderWindow);
    w.setWindowTitle("SobelExample");
    w.show();

    return a.exec();
}

 效果如下:

2、Canny算子 

        Canny 算子是 John Canny 于20 世纪 80年代提出的一种多级边缘检测算法。John Canny研究了最优边缘的特性,即检测到的边缘要尽可能接近实际的边缘,同时,要尽量降低噪声对边缘检测的干扰。其计算步骤如下:

1)对源图像进行高斯平滑以消除图像中的噪声。

2)采用差分法近似计算图像每一个像素的梯度,并计算梯度的值和方向。

3)对梯度进行“非极大值抑制”。

4)双闽值法检测边缘和连接边缘。

示例代码:

#include <QApplication>
#include <vtkSmartPointer.h>
#include <vtkImageData.h>
#include <vtkRenderer.h>
#include <vtkImageActor.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <QVTKOpenGLNativeWidget.h>
#include <vtkJPEGReader.h>
#include <vtkImageCast.h>
#include <vtkImageGaussianSmooth.h>
#include <vtkImageGradient.h>
#include <vtkImageShiftScale.h>
#include <vtkImageMagnitude.h>
#include <vtkImageNonMaximumSuppression.h>
#include <vtkImageConstantPad.h>
#include <vtkImageToStructuredPoints.h>
#include <vtkLinkEdgels.h>
#include <vtkThreshold.h>
#include <vtkGeometryFilter.h>
#include <vtkSubPixelPositionEdgels.h>
#include <vtkStripper.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    vtkSmartPointer<vtkJPEGReader> reader = vtkSmartPointer<vtkJPEGReader>::New();
    reader->SetFileName("D:/data/lena-gray.jpg");
    reader->Update();

    //图片类型转换
    vtkSmartPointer<vtkImageCast> ic = vtkSmartPointer<vtkImageCast>::New();
    ic->SetOutputScalarTypeToFloat();
    ic->SetInputConnection(reader->GetOutputPort());
    ic->Update();

    //高斯算法实现图片平滑
    vtkSmartPointer<vtkImageGaussianSmooth> gs = vtkSmartPointer<vtkImageGaussianSmooth>::New();
    gs->SetInputData(ic->GetOutput());
    gs->SetDimensionality(2);
    gs->SetRadiusFactors(1, 1, 0);
    gs->Update();

    //计算图像的梯度和模值
    vtkSmartPointer<vtkImageGradient> imgGradient = vtkSmartPointer<vtkImageGradient>::New();
    imgGradient->SetInputData(gs->GetOutput());
    imgGradient->SetDimensionality(2);
    imgGradient->Update();

    vtkSmartPointer<vtkImageMagnitude> imgMagnitude = vtkSmartPointer<vtkImageMagnitude>::New();
    imgMagnitude->SetInputData(imgGradient->GetOutput());
    imgMagnitude->Update();

    //vtkImageNonMaximumSuppression将图片中的非局部峰值设置为0,输入和输出类型都是ImageData
    //其中输入有两个,模型图像和向量图像,一个典型的应用就是输入梯度模值图像梯度图像对梯度做非极大值抑制
    vtkSmartPointer<vtkImageNonMaximumSuppression> nonMax = vtkSmartPointer<vtkImageNonMaximumSuppression>::New();
    nonMax->SetMagnitudeInputData(imgMagnitude->GetOutput());
    nonMax->SetVectorInputData(imgGradient->GetOutput());
    nonMax->SetDimensionality(2);
    nonMax->Update();

    //vtkImageConstantPad增加图像的大小,将梯度图像像素的组分修改为3,方便下面vtkImageToStructuredPoints使用。
    vtkSmartPointer<vtkImageConstantPad> pad = vtkSmartPointer<vtkImageConstantPad>::New();
    pad->SetInputConnection(imgGradient->GetOutputPort());
    pad->SetOutputNumberOfScalarComponents(3);
    pad->SetConstant(0);
    pad->Update();

    //vtkImageToStructuredPoints将VTKImageData格式转换为规则点集。该类的输入类型VTKImageData,
    //另外还有一个可选的RGB三组分向量图像输入;输入类型是VTKStructuredData类型,导航输入向量图像时,向量图像像素数据回转为输入图像的对应点
    vtkSmartPointer<vtkImageToStructuredPoints> i2sp1 = vtkSmartPointer<vtkImageToStructuredPoints>::New();
    i2sp1->SetInputData(nonMax->GetOutput());
    i2sp1->SetVectorInputData(pad->GetOutput());
    i2sp1->Update();

    //vtkLinkEdgels类根据点的相邻关系连接成连续的折线Polyline。其内部阈值变量GradientThreshold,
    //可以用来排除输入点中梯度值小于该阈值的点。当使用vtkLinkEdgels进行Canny算子的双阈值边缘检测时,GradientThreshold可以用作较小的阈值。设置该阈值的函数是SetGradientThreshold(2)。
    vtkSmartPointer<vtkLinkEdgels> imgLink = vtkSmartPointer<vtkLinkEdgels>::New();
    imgLink->SetInputData(i2sp1->GetOutput());
    imgLink->SetGradientThreshold(2);
    imgLink->Update();

    vtkSmartPointer<vtkThreshold> thresholdEdgels = vtkSmartPointer<vtkThreshold>::New();
    thresholdEdgels->SetInputData(imgLink->GetOutput());
    thresholdEdgels->SetUpperThreshold(10);
    thresholdEdgels->AllScalarsOff();
    thresholdEdgels->Update();

    vtkSmartPointer<vtkGeometryFilter> gf = vtkSmartPointer<vtkGeometryFilter>::New();
    gf->SetInputConnection(thresholdEdgels->GetOutputPort());

    vtkSmartPointer<vtkImageToStructuredPoints> i2sp = vtkSmartPointer<vtkImageToStructuredPoints>::New();
    i2sp->SetInputData(imgMagnitude->GetOutput());
    i2sp->SetVectorInputData(pad->GetOutput());
    i2sp->Update();

    vtkSmartPointer<vtkSubPixelPositionEdgels> spe = vtkSmartPointer<vtkSubPixelPositionEdgels>::New();
    spe->SetInputConnection(gf->GetOutputPort());
    spe->SetGradMapsData(i2sp->GetStructuredPointsOutput());

    vtkSmartPointer<vtkStripper> strip = vtkSmartPointer<vtkStripper>::New();
    strip->SetInputConnection(spe->GetOutputPort());

    vtkSmartPointer<vtkPolyDataMapper> dsm = vtkSmartPointer<vtkPolyDataMapper>::New();
    dsm->SetInputConnection(strip->GetOutputPort());
    dsm->ScalarVisibilityOff();

    vtkSmartPointer<vtkActor> planeActor = vtkSmartPointer<vtkActor>::New();
    planeActor->SetMapper(dsm);
    planeActor->GetProperty()->SetAmbient(1.0);
    planeActor->GetProperty()->SetDiffuse(0.0);
    planeActor->GetProperty()->SetColor(1.0, 0.0, 0.0);

    vtkSmartPointer<vtkImageActor> originalActor = vtkSmartPointer<vtkImageActor>::New();
    originalActor->SetInputData(reader->GetOutput());

    double originalviewport[4] = { 0.0, 0.0, 0.5, 1.0 };
    double gradviewport[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, 1.0, 1.0);

    vtkSmartPointer<vtkRenderer> gradRenderer = vtkSmartPointer<vtkRenderer>::New();
    gradRenderer->SetViewport(gradviewport);
    gradRenderer->AddActor(planeActor);
    gradRenderer->ResetCamera();
    gradRenderer->SetBackground(1.0, 1.0, 1.0);

    vtkSmartPointer<vtkGenericOpenGLRenderWindow> renderWindow = vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New();
    renderWindow->AddRenderer(originalRenderer);
    renderWindow->AddRenderer(gradRenderer);

    QVTKOpenGLNativeWidget w;
    w.resize(900,300);
    w.setRenderWindow(renderWindow);
    w.setWindowTitle("CannyExample");
    w.show();

    return a.exec();
}

 效果如下:

 3、拉普拉斯算子

        拉普拉斯算子是一个二阶边缘算子,即梯度的散度。拉普拉斯算子也是通过模板实现。 

示例代码:

#include <QApplication>
#include <vtkSmartPointer.h>
#include <vtkImageData.h>
#include <vtkRenderer.h>
#include <vtkImageActor.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <QVTKOpenGLNativeWidget.h>
#include <vtkJPEGReader.h>
#include <vtkImageCast.h>
#include <vtkImageGaussianSmooth.h>
#include <vtkImageLaplacian.h>
#include <vtkImageShiftScale.h>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    vtkSmartPointer<vtkJPEGReader> reader = vtkSmartPointer<vtkJPEGReader>::New();
    reader->SetFileName("D:/data/lena-gray.jpg");
    reader->Update();

    vtkSmartPointer<vtkImageLaplacian> lapFilter = vtkSmartPointer<vtkImageLaplacian>::New();
    lapFilter->SetInputConnection(reader->GetOutputPort());
    lapFilter->SetDimensionality(2);

    double range[2];
    lapFilter->GetOutput()->GetScalarRange(range);

    vtkSmartPointer<vtkImageShiftScale> ShiftScale = vtkSmartPointer<vtkImageShiftScale>::New();
    ShiftScale->SetOutputScalarTypeToUnsignedChar();
    ShiftScale->SetScale( 255 / (range[1]-range[0]) );
    ShiftScale->SetShift(-range[0]);
    ShiftScale->SetInputConnection(lapFilter->GetOutputPort());
    ShiftScale->Update();

    vtkSmartPointer<vtkImageActor> originalActor = vtkSmartPointer<vtkImageActor>::New();
    originalActor->SetInputData(reader->GetOutput());

    vtkSmartPointer<vtkImageActor> gradActor = vtkSmartPointer<vtkImageActor>::New();
    gradActor->SetInputData(ShiftScale->GetOutput());

    double originalViewport[4] = {0.0, 0.0, 0.5, 1.0};
    double gradviewport[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, 1.0, 1.0);

    vtkSmartPointer<vtkRenderer> gradRenderer = vtkSmartPointer<vtkRenderer>::New();
    gradRenderer->SetViewport(gradviewport);
    gradRenderer->AddActor(gradActor);
    gradRenderer->ResetCamera();
    gradRenderer->SetBackground(1.0, 1.0, 1.0);

    vtkSmartPointer<vtkGenericOpenGLRenderWindow> renderWindow = vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New();
    renderWindow->AddRenderer(originalRenderer);
    renderWindow->AddRenderer(gradRenderer);

    QVTKOpenGLNativeWidget w;
    w.resize(640,320);
    w.setRenderWindow(renderWindow);
    w.setWindowTitle("LaplacianExample");
    w.show();

    return a.exec();
}

效果如下:

 

        由于拉普拉斯算子对噪声比较敏感,为了减少噪声影响,可以先对图像做高斯滤波平滑再计算拉普拉斯算子,称为LoG算子(LaplacianofGaussian)。

二、图像平滑 

        图像平滑常用于图像的预处理中,如计算梯度时先对图像进行平滑处理,可以减少噪声对梯度的影响。图像平滑一般是通过模板卷积运算实现。模板可以看作一个大小为nxn的小图像,例如 3x3、5x5 等,模板的每个像素都对应一个系数值。模板卷积运算的过程先是将模板中心依次与图像每个像素重合,然后通过模板各个系数与图像对应像素相乘来计算模板对应像素的加权平均值,最后将运算结果赋给图像中模板中心对应的像素。 

1、均值滤波

        均值滤波是一种常用的平滑方法,其对应的模板各个像素的值相同且和为1。在 VTK中没有直接实现均值滤波的类,但是可以通过图像卷积运算来实现均值滤波。卷积运算通过vtkImageConvolve 类实现。

示例代码: 

#include <QApplication>
#include <vtkSmartPointer.h>
#include <vtkImageData.h>
#include <vtkRenderer.h>
#include <vtkImageActor.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <QVTKOpenGLNativeWidget.h>
#include <vtkJPEGReader.h>
#include <vtkImageCast.h>
#include <vtkImageConvolve.h>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    vtkSmartPointer<vtkJPEGReader> reader = vtkSmartPointer<vtkJPEGReader>::New();
    reader->SetFileName("D:/data/lena-gray.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<vtkGenericOpenGLRenderWindow> renderWindow = vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New();
    renderWindow->AddRenderer(originalRenderer);
    renderWindow->AddRenderer(convolvedRenderer);

    QVTKOpenGLNativeWidget w;
    w.resize(640,320);
    w.setRenderWindow(renderWindow);
    w.setWindowTitle("MeanFilterExample");
    w.show();

    return a.exec();
}

运行效果:

 

2、高斯平滑

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

示例代码:

#include <QApplication>
#include <vtkSmartPointer.h>
#include <vtkImageData.h>
#include <vtkRenderer.h>
#include <vtkImageActor.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <QVTKOpenGLNativeWidget.h>
#include <vtkJPEGReader.h>
#include <vtkImageCast.h>
#include <vtkImageGaussianSmooth.h>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    vtkSmartPointer<vtkJPEGReader> reader = vtkSmartPointer<vtkJPEGReader>::New();
    reader->SetFileName("D:/data/lena-gray.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, 1.0, 1.0);

    vtkSmartPointer<vtkRenderer> gradientMagnitudeRenderer = vtkSmartPointer<vtkRenderer>::New();
    gradientMagnitudeRenderer->SetViewport(smoothedViewport);
    gradientMagnitudeRenderer->AddActor(smoothedActor);
    gradientMagnitudeRenderer->ResetCamera();
    gradientMagnitudeRenderer->SetBackground(1.0, 1.0, 1.0);

    vtkSmartPointer<vtkGenericOpenGLRenderWindow> renderWindow = vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New();
    renderWindow->AddRenderer(originalRenderer);
    renderWindow->AddRenderer(gradientMagnitudeRenderer);

    QVTKOpenGLNativeWidget w;
    w.resize(640,320);
    w.setRenderWindow(renderWindow);
    w.setWindowTitle("GaussianFilterExample");
    w.show();

    return a.exec();
}

运行效果:

 

3、中值滤波

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

示例代码:

#include <QApplication>
#include <vtkSmartPointer.h>
#include <vtkImageData.h>
#include <vtkRenderer.h>
#include <vtkImageActor.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <QVTKOpenGLNativeWidget.h>
#include <vtkJPEGReader.h>
#include <vtkImageHybridMedian2D.h>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    vtkSmartPointer<vtkJPEGReader> reader = vtkSmartPointer<vtkJPEGReader>::New();
    reader->SetFileName("D:/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, 1.0, 1.0);

    vtkSmartPointer<vtkRenderer> hybridMedianRenderer = vtkSmartPointer<vtkRenderer>::New();
    hybridMedianRenderer->SetViewport(hybridMedianViewport);
    hybridMedianRenderer->AddActor(hybridMedianActor);
    hybridMedianRenderer->ResetCamera();
    hybridMedianRenderer->SetBackground(1.0, 1.0, 1.0);

    vtkSmartPointer<vtkGenericOpenGLRenderWindow> renderWindow = vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New();
    renderWindow->AddRenderer(originalRenderer);
    renderWindow->AddRenderer(hybridMedianRenderer);

    QVTKOpenGLNativeWidget w;
    w.resize(640,320);
    w.setRenderWindow(renderWindow);
    w.setWindowTitle("MedianFilterExample");
    w.show();

    return a.exec();
}

运行效果:

 

        vtkImageHybridMedian2D类的使用,不需要用户设置任何参数。该方法能够有效保持图像边缘,并对椒盐噪声有较好的抑制作用。若要实现三维图像的中值滤波,则使用vtkImageHybridMedian3D类。

4、各向异性滤波 

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

        VTK中用vtkImageAnisotropicDiffusion2D 类和 vtkImageAnisotropicDiffusion3D 类分别实现对二维和三维图像的各向异性扩散滤波。

示例代码:

#include <QApplication>
#include <vtkSmartPointer.h>
#include <vtkImageData.h>
#include <vtkRenderer.h>
#include <vtkImageActor.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <QVTKOpenGLNativeWidget.h>
#include <vtkJPEGReader.h>
#include <vtkImageAnisotropicDiffusion2D.h>
#include <vtkCamera.h>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    vtkSmartPointer<vtkJPEGReader> reader = vtkSmartPointer<vtkJPEGReader>::New();
    reader->SetFileName("D:/data/lena-gray.jpg");
    reader->Update();

    vtkSmartPointer<vtkImageAnisotropicDiffusion2D> diffusion = vtkSmartPointer<vtkImageAnisotropicDiffusion2D>::New();
    diffusion->SetInputConnection(reader->GetOutputPort());
    diffusion->SetNumberOfIterations(10);
    diffusion->SetDiffusionThreshold(20);
    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<vtkGenericOpenGLRenderWindow> renderWindow = vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New();
    renderWindow->AddRenderer(leftRenderer);
    renderWindow->AddRenderer(rightRenderer);

    QVTKOpenGLNativeWidget w;
    w.resize(640,320);
    w.setRenderWindow(renderWindow);
    w.setWindowTitle("AnistropicFilteringExample");
    w.show();

    return a.exec();
}

运行效果:

三、频域处理 

1、快速傅里叶变换

        快速傅里叶变换(Fast Fourier Transform,FFT)是根据离散傅里叶变换的奇、偶、虚、实等特性,对离散傅里叶变换的算法进行改进获得的。FFT是可逆的,其逆变换为RFFT。FFT在数字图像处理中有着广泛的应用,例如数字图像频域滤波、去噪、增强等。目前,在 VTK中FFT和 RFFT这两种变换都已经实现,对应的类分别为vkmageFFT和vtkImageRFFT。

        vtkmageFFT和 vtkImageRFFT 的输入为实数或复数数据,输出均为复数数据。因此,vtklmageFFT与 vtklmageRFFT的输出结果不能直接显示,因为 VTK 会将其当作彩色图像显示,需要通过 vtkImageExtractComponents 类提取某一组分进行图像显示。

示例代码:

#include <QApplication>
#include <vtkSmartPointer.h>
#include <vtkImageData.h>
#include <vtkRenderer.h>
#include <vtkImageActor.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <QVTKOpenGLNativeWidget.h>
#include <vtkJPEGReader.h>
#include <vtkImageFFT.h>
#include <vtkImageExtractComponents.h>
#include <vtkImageShiftScale.h>
#include <vtkImageRFFT.h>
#include <vtkImageCast.h>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    vtkSmartPointer<vtkJPEGReader> reader = vtkSmartPointer<vtkJPEGReader>::New();
    reader->SetFileName("D:/data/lena-gray.jpg");
    reader->Update();

    vtkSmartPointer<vtkImageFFT> fftFilter = vtkSmartPointer<vtkImageFFT>::New();
    fftFilter->SetInputConnection(reader->GetOutputPort());
    fftFilter->SetDimensionality(2);
    fftFilter->Update();

    vtkSmartPointer<vtkImageExtractComponents> fftExtractReal = vtkSmartPointer<vtkImageExtractComponents>::New();
    fftExtractReal->SetInputConnection(fftFilter->GetOutputPort());
    fftExtractReal->SetComponents(0);

    double range[2];
    fftExtractReal->GetOutput()->GetScalarRange(range);

    vtkSmartPointer<vtkImageShiftScale> ShiftScale = vtkSmartPointer<vtkImageShiftScale>::New();
    ShiftScale->SetOutputScalarTypeToUnsignedChar();
    ShiftScale->SetScale( 255.0 / (range[1]-range[0]) );
    ShiftScale->SetShift(-range[0]);
    ShiftScale->SetInputConnection(fftExtractReal->GetOutputPort());
    ShiftScale->Update();

    vtkSmartPointer<vtkImageRFFT> rfftFilter = vtkSmartPointer<vtkImageRFFT>::New();
    rfftFilter->SetInputConnection(fftFilter->GetOutputPort());
    rfftFilter->SetDimensionality(2);
    rfftFilter->Update();

    vtkSmartPointer<vtkImageExtractComponents> ifftExtractReal = vtkSmartPointer<vtkImageExtractComponents>::New();
    ifftExtractReal->SetInputConnection(rfftFilter->GetOutputPort());
    ifftExtractReal->SetComponents(0);

    vtkSmartPointer<vtkImageCast> rfftCastFilter = vtkSmartPointer<vtkImageCast>::New();
    rfftCastFilter->SetInputConnection(ifftExtractReal->GetOutputPort());
    rfftCastFilter->SetOutputScalarTypeToUnsignedChar();
    rfftCastFilter->Update();

    vtkSmartPointer<vtkImageActor> originalActor = vtkSmartPointer<vtkImageActor>::New();
    originalActor->SetInputData(reader->GetOutput());

    vtkSmartPointer<vtkImageActor> fftActor = vtkSmartPointer<vtkImageActor>::New();
    fftActor->SetInputData(ShiftScale->GetOutput());

    vtkSmartPointer<vtkImageActor> rfftActor = vtkSmartPointer<vtkImageActor>::New();
    rfftActor->SetInputData(rfftCastFilter->GetOutput());

    double originalViewport[4] = {0.0, 0.0, 0.33, 1.0};
    double fftViewport[4] = {0.33, 0.0, 0.66, 1.0};
    double rfftViewport[4] = {0.66, 0.0, 1.0, 1.0};

    vtkSmartPointer<vtkRenderer> originalRenderer = vtkSmartPointer<vtkRenderer>::New();
    originalRenderer->SetViewport(originalViewport);
    originalRenderer->AddActor(originalActor);
    originalRenderer->ResetCamera();
    originalRenderer->SetBackground(1.0, 1.0, 1.0);

    vtkSmartPointer<vtkRenderer> fftRenderer = vtkSmartPointer<vtkRenderer>::New();
    fftRenderer->SetViewport(fftViewport);
    fftRenderer->AddActor(fftActor);
    fftRenderer->ResetCamera();
    fftRenderer->SetBackground(1.0, 1.0, 1.0);

    vtkSmartPointer<vtkRenderer> rfftRenderer = vtkSmartPointer<vtkRenderer>::New();
    rfftRenderer->SetViewport(rfftViewport);
    rfftRenderer->AddActor(rfftActor);
    rfftRenderer->ResetCamera();
    rfftRenderer->SetBackground(1.0, 1.0, 1.0);

    vtkSmartPointer<vtkGenericOpenGLRenderWindow> renderWindow = vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New();
    renderWindow->AddRenderer(originalRenderer);
    renderWindow->AddRenderer(fftRenderer);
    renderWindow->AddRenderer(rfftRenderer);

    QVTKOpenGLNativeWidget w;
    w.resize(900,300);
    w.setRenderWindow(renderWindow);
    w.setWindowTitle("FFTAndRFFTExample");
    w.show();

    return a.exec();
}

运行效果:

 

2、低通滤波

        低通滤波是将频域图像中的高频部分滤除而保留低频部分。图像的边缘和噪声对应于频域图像中的高频部分,而低通滤波的作用即是减弱这部分的能量,从而达到图像平滑去噪的目的。 

巴特沃斯低通滤波器示例代码:

#include <QApplication>
#include <vtkSmartPointer.h>
#include <vtkImageData.h>
#include <vtkRenderer.h>
#include <vtkImageActor.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <QVTKOpenGLNativeWidget.h>
#include <vtkJPEGReader.h>
#include <vtkImageFFT.h>
#include <vtkImageExtractComponents.h>
#include <vtkImageButterworthLowPass.h>
#include <vtkImageRFFT.h>
#include <vtkImageCast.h>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    vtkSmartPointer<vtkJPEGReader> reader = vtkSmartPointer<vtkJPEGReader>::New();
    reader->SetFileName("D:/data/lena-gray.jpg");
    reader->Update();

    vtkSmartPointer<vtkImageFFT> fftFilter = vtkSmartPointer<vtkImageFFT>::New();
    fftFilter->SetInputConnection(reader->GetOutputPort());
    fftFilter->Update();

    vtkSmartPointer<vtkImageButterworthLowPass> lowPassFilter =  vtkSmartPointer<vtkImageButterworthLowPass>::New();
    lowPassFilter->SetInputConnection(fftFilter->GetOutputPort());
    lowPassFilter->SetXCutOff(0.05);
    lowPassFilter->SetYCutOff(0.05);
    lowPassFilter->Update();

    vtkSmartPointer<vtkImageRFFT> rfftFilter = vtkSmartPointer<vtkImageRFFT>::New();
    rfftFilter->SetInputConnection(lowPassFilter->GetOutputPort());
    rfftFilter->Update();

    vtkSmartPointer<vtkImageExtractComponents> ifftExtractReal = vtkSmartPointer<vtkImageExtractComponents>::New();
    ifftExtractReal->SetInputConnection(rfftFilter->GetOutputPort());
    ifftExtractReal->SetComponents(0);

    vtkSmartPointer<vtkImageCast> castFilter = vtkSmartPointer<vtkImageCast>::New();
    castFilter->SetInputConnection(ifftExtractReal->GetOutputPort());
    castFilter->SetOutputScalarTypeToUnsignedChar();
    castFilter->Update();

    vtkSmartPointer<vtkImageActor> originalActor = vtkSmartPointer<vtkImageActor>::New();
    originalActor->SetInputData(reader->GetOutput());

    vtkSmartPointer<vtkImageActor> erodedActor = vtkSmartPointer<vtkImageActor>::New();
    erodedActor->SetInputData(castFilter->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> leftRenderer = vtkSmartPointer<vtkRenderer>::New();
    leftRenderer->SetViewport(leftViewport);
    leftRenderer->SetBackground(1.0, 1.0, 1.0);
    leftRenderer->AddActor(originalActor);
    leftRenderer->ResetCamera();

    vtkSmartPointer<vtkRenderer> rightRenderer = vtkSmartPointer<vtkRenderer>::New();
    rightRenderer->SetViewport(rightViewport);
    rightRenderer->SetBackground(1.0, 1.0, 1.0);
    rightRenderer->AddActor(erodedActor);
    rightRenderer->ResetCamera();

    vtkSmartPointer<vtkGenericOpenGLRenderWindow> renderWindow = vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New();
    renderWindow->AddRenderer(leftRenderer);
    renderWindow->AddRenderer(rightRenderer);

    QVTKOpenGLNativeWidget w;
    w.resize(640, 320);
    w.setRenderWindow(renderWindow);
    w.setWindowTitle("ButterworthLowPassExample");
    w.show();

    return a.exec();
}

 效果如下:

3、高通滤波 

        高通滤波与低通滤波正好相反,是让频域图像的高频部分通过而抑制低频部分。图像的边缘对应高频分量,因此高通滤波的效果是图像锐化。 

巴特沃斯高通滤波器示例代码: 

#include <QApplication>
#include <vtkSmartPointer.h>
#include <vtkImageData.h>
#include <vtkRenderer.h>
#include <vtkImageActor.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <QVTKOpenGLNativeWidget.h>
#include <vtkJPEGReader.h>
#include <vtkImageFFT.h>
#include <vtkImageExtractComponents.h>
#include <vtkImageButterworthHighPass.h>
#include <vtkImageRFFT.h>
#include <vtkImageCast.h>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    vtkSmartPointer<vtkJPEGReader> reader = vtkSmartPointer<vtkJPEGReader>::New();
    reader->SetFileName("D:/data/lena-gray.jpg");
    reader->Update();

    vtkSmartPointer<vtkImageFFT> fftFilter = vtkSmartPointer<vtkImageFFT>::New();
    fftFilter->SetInputConnection(reader->GetOutputPort());
    fftFilter->Update();

    vtkSmartPointer<vtkImageButterworthHighPass> highPassFilter =  vtkSmartPointer<vtkImageButterworthHighPass>::New();
    highPassFilter->SetInputConnection(fftFilter->GetOutputPort());
    highPassFilter->SetXCutOff(0.1);
    highPassFilter->SetYCutOff(0.1);
    highPassFilter->Update();

    vtkSmartPointer<vtkImageRFFT> rfftFilter = vtkSmartPointer<vtkImageRFFT>::New();
    rfftFilter->SetInputConnection(highPassFilter->GetOutputPort());
    rfftFilter->Update();

    vtkSmartPointer<vtkImageExtractComponents> ifftExtractReal = vtkSmartPointer<vtkImageExtractComponents>::New();
    ifftExtractReal->SetInputConnection(rfftFilter->GetOutputPort());
    ifftExtractReal->SetComponents(0);

    vtkSmartPointer<vtkImageCast> castFilter = vtkSmartPointer<vtkImageCast>::New();
    castFilter->SetInputConnection(ifftExtractReal->GetOutputPort());
    castFilter->SetOutputScalarTypeToUnsignedChar();
    castFilter->Update();

    vtkSmartPointer<vtkImageActor> originalActor = vtkSmartPointer<vtkImageActor>::New();
    originalActor->SetInputData(reader->GetOutput());

    vtkSmartPointer<vtkImageActor> erodedActor = vtkSmartPointer<vtkImageActor>::New();
    erodedActor->SetInputData(castFilter->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> leftRenderer = vtkSmartPointer<vtkRenderer>::New();
    leftRenderer->SetViewport(leftViewport);
    leftRenderer->SetBackground(1.0, 1.0, 1.0);
    leftRenderer->AddActor(originalActor);
    leftRenderer->ResetCamera();

    vtkSmartPointer<vtkRenderer> rightRenderer = vtkSmartPointer<vtkRenderer>::New();
    rightRenderer->SetViewport(rightViewport);
    rightRenderer->SetBackground(1.0, 1.0, 1.0);
    rightRenderer->AddActor(erodedActor);
    rightRenderer->ResetCamera();

    vtkSmartPointer<vtkGenericOpenGLRenderWindow> renderWindow = vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New();
    renderWindow->AddRenderer(leftRenderer);
    renderWindow->AddRenderer(rightRenderer);

    QVTKOpenGLNativeWidget w;
    w.resize(640, 320);
    w.setRenderWindow(renderWindow);
    w.setWindowTitle("ButterworthHighPassExample");
    w.show();

    return a.exec();
}

 运行效果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值