【QT+VTK 学习笔记】10:VTK边缘检测

这篇博客介绍了使用VTK库进行图像边缘检测的几种方法,包括图像梯度、Sobel算子和Canny算子。通过示例代码展示了如何计算图像梯度、提取边缘,并利用高斯平滑、非极大值抑制等步骤实现Canny边缘检测。此外,还提及了拉普拉斯算子在边缘检测中的应用。
摘要由CSDN通过智能技术生成

前言

“VTK图形图像开发进阶_张晓东_罗火灵”的学习笔记。
东灵工作室 教程系列导航:http://blog.csdn.net/www_doling_net/article/details/8763686

学习资料

VTK官网学习地址:https://vtk.org/doc/nightly/html/

边缘检测

图像中不连续的灰度值会产生边缘,图像的边缘检测是基于边界的图像分割方法的基础, 例如,分水岭算法通常是分割原图的梯度图像,而梯度实际上也是反映图像的边缘信息。图像边缘常用图像一阶导数和二阶导数来检测。

梯度算子

梯度算子对应于图像一阶导数。图像一阶导数一般是通过差分运算来近似的。VTK中可 以用vtklmageGradient类计算图像梯度。注意:图像梯度是一个矢量,具有方向和大小,因此vtklmageGradient的计算结果是一个梯度场,即每个像素值都是一个梯度矢量。
在这里插入图片描述
注意:彩色图像不能直接用来计算梯度,需要先转换为灰度图像。

#include <QApplication>
#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2)
VTK_MODULE_INIT(vtkInteractionStyle)
VTK_MODULE_INIT(vtkRenderingFreeType)

#include <vtkSmartPointer.h>
#include <vtkImageData.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkInteractorStyleImage.h>
#include <vtkRenderer.h>
#include <vtkImageActor.h>
#include <vtkJPEGReader.h>
#include <vtkImageGradient.h>
#include <vtkImageMagnitude.h>
#include <vtkImageCast.h>
#include <vtkImageShiftScale.h>

//测试图像:../data/lena-gray.jpg
int main(int argc, char* argv[])
{
    vtkSmartPointer<vtkJPEGReader> reader = vtkSmartPointer<vtkJPEGReader>::New();
    reader->SetFileName("C:/Users/jbyyy/Desktop/work/QTDEMO/jbyyy/VTK/学习资料/VTK图形图像开发进阶+张晓东+PDF+源码/VTK图形图像开发进阶随书代码/VTK图形图像开发进阶随书代码/Examples/Chap05/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);
    std::cout<<"获取图像值范围 :"<<range[0] << "~" <<range[1]<<std::endl;

    //可以指定偏移和比例参数来对输入图像数据进行类型转换操作。
    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<vtkRenderWindow> renderWindow =
        vtkSmartPointer<vtkRenderWindow>::New();
    renderWindow->AddRenderer(originalRenderer);
    renderWindow->AddRenderer(gradRenderer);
    renderWindow->SetSize( 640, 320 );
    renderWindow->Render();
    renderWindow->SetWindowName("ImageGradientExample");

    vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
        vtkSmartPointer<vtkRenderWindowInteractor>::New();
    vtkSmartPointer<vtkInteractorStyleImage> style =
        vtkSmartPointer<vtkInteractorStyleImage>::New();

    renderWindowInteractor->SetInteractorStyle(style);
    renderWindowInteractor->SetRenderWindow(renderWindow);
    renderWindowInteractor->Initialize();
    renderWindowInteractor->Start();

    return EXIT_SUCCESS;
}

Sobel算子提取图像边缘
Sobel算子也是一种常用的梯度算子。Sobel算子计算稍微复杂,它采用3x3的模板。计算时模板在图像上移动,并在每个位置上计算对应中心像素的梯度值。
在这里插入图片描述

VTK中vtkSobel2D计算图像的sobel算子

在这里插入图片描述

#include <QApplication>
#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2)
VTK_MODULE_INIT(vtkInteractionStyle)
VTK_MODULE_INIT(vtkRenderingFreeType)

#include <vtkSmartPointer.h>
#include <vtkJPEGReader.h>
#include <vtkImageSobel2D.h>
#include <vtkImageExtractComponents.h>
#include <vtkImageMathematics.h>
#include <vtkImageData.h>
#include <vtkImageShiftScale.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("C:/Users/jbyyy/Desktop/work/QTDEMO/jbyyy/VTK/学习资料/VTK图形图像开发进阶+张晓东+PDF+源码/VTK图形图像开发进阶随书代码/VTK图形图像开发进阶随书代码/Examples/Chap05/data/lena.jpg");
    reader->Update();

    //sobel算子计算图像的梯度图像
    vtkSmartPointer<vtkImageSobel2D> sobelFilter = vtkSmartPointer<vtkImageSobel2D>::New();
    sobelFilter->SetInputConnection(reader->GetOutputPort());//包含横向和竖向边缘

    //提取X向边缘成分
    vtkSmartPointer<vtkImageExtractComponents> xSobel =
        vtkSmartPointer<vtkImageExtractComponents>::New();
    xSobel->SetComponents(0);//提取第一成分即X向梯度
    xSobel->SetInputConnection(sobelFilter->GetOutputPort());
    xSobel->Update();

    //计算Sobel算子的值可能存在负值,因此利用vtklmageMathematics对各分量图像计算绝对值
    vtkSmartPointer<vtkImageMathematics> absFilter =
        vtkSmartPointer<vtkImageMathematics>::New();
    absFilter->SetOperationToAbsoluteValue();//将属性设置为绝对值模式
    absFilter->SetInputConnection(xSobel->GetOutputPort());
    absFilter->Update();

    double xRange[2];
    absFilter->GetOutput()->GetScalarRange(xRange);
    //由vtklmageShiftScale将图像的数值范围调节到[0, 255]再显示
    vtkSmartPointer<vtkImageShiftScale> xShiftScale =
        vtkSmartPointer<vtkImageShiftScale>::New();
    xShiftScale->SetOutputScalarTypeToUnsignedChar();//强制类型转换 方便显示
    xShiftScale->SetScale(255 / xRange[1]);//设置属性
    xShiftScale->SetInputConnection(absFilter->GetOutputPort());
    xShiftScale->Update();

    //提取Y向边缘成分
    vtkSmartPointer<vtkImageExtractComponents> ySobel =
        vtkSmartPointer<vtkImageExtractComponents>::New();
    ySobel->SetComponents(1);
    ySobel->SetInputConnection(sobelFilter->GetOutputPort());
    ySobel->Update();

    vtkSmartPointer<vtkImageMathematics> absYsobel =
        vtkSmartPointer<vtkImageMathematics>::New();
    absYsobel->SetOperationToAbsoluteValue();
    absYsobel->SetInputConnection(ySobel->GetOutputPort());
    absYsobel->Update();

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

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

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

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

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

    double origView[4] = { 0, 0, 0.33, 1 };
    double xSobelView[4] = { 0.33, 0, 0.66, 1 };
    double ySobelView[4] = { 0.66, 0, 1, 1 };
    vtkSmartPointer<vtkRenderer> origRender =
        vtkSmartPointer<vtkRenderer>::New();
    origRender->SetViewport(origView);
    origRender->AddActor(origActor);
    origRender->ResetCamera();
    origRender->SetBackground(1, 0, 0);

    vtkSmartPointer<vtkRenderer> xSobelRender =
        vtkSmartPointer<vtkRenderer>::New();
    xSobelRender->SetViewport(xSobelView);
    xSobelRender->AddActor(xSobelActor);
    xSobelRender->ResetCamera();
    xSobelRender->SetBackground(0, 1, 0);

    vtkSmartPointer<vtkRenderer> ySobelRender =
        vtkSmartPointer<vtkRenderer>::New();
    ySobelRender->SetViewport(ySobelView);
    ySobelRender->AddActor(ySobelActor);
    ySobelRender->ResetCamera();
    ySobelRender->SetBackground(0, 0, 1);
    //
    vtkSmartPointer<vtkRenderWindow> rw =
        vtkSmartPointer<vtkRenderWindow>::New();
    rw->AddRenderer(origRender);
    rw->AddRenderer(xSobelRender);
    rw->AddRenderer(ySobelRender);
    rw->SetSize(960, 320);
    rw->SetWindowName("Edge by Soebl");

    vtkSmartPointer<vtkRenderWindowInteractor> rwi =
        vtkSmartPointer<vtkRenderWindowInteractor>::New();
    vtkSmartPointer<vtkInteractorStyleImage> style =
        vtkSmartPointer<vtkInteractorStyleImage>::New();
    rwi->SetInteractorStyle(style);
    rwi->SetRenderWindow(rw);
    rwi->Initialize();
    rwi->Start();
    return 0;
}

Canny算子

最优边缘的特性,即检测到的边缘要尽可能跟实际的边缘接近并尽可能的多,同时,要尽量降低噪声对边缘检测的干扰。Canny算子的计算步骤如下
1)对源图像进行高斯平滑以消除图像中噪声
2)采用差分法近似计算图像每一个像素的梯度,并计算梯度的模值和方向
3)对梯度进行"非极大抑制":图像边缘点梯度值通常在梯度方向是极大值,因此检测边缘需要将非极大值赋值0来抑制非边缘点。检测方法就是在一个局部窗口内,如果中心像素点的梯度不比梯度方向上相邻两个像素值大,那么该中心像素点梯度值赋0。
4)双阈值法检测边缘和连接边缘。取两个梯度阈值high和low,将梯度图像中小于high的像素赋0得到边缘图像L1,该图像能够接近图像边缘但是可能会存在间断点;将梯度图像中小于low的像素赋0得到边缘图像L2,该图中受噪声影响比较大,但是边缘信息更多。在连接边缘时,以L1为基础,对非零点进行边缘跟踪,如果追踪过程中出现中断,则从L2对应像素点及其邻域来寻找可以连接的边缘,直至结束。
在VTK中没有实现一个专门的类来做Canny边缘检测,但我们可以根据以上步骤来实现。
在这里插入图片描述

#include <QApplication>
#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2)
VTK_MODULE_INIT(vtkInteractionStyle)
VTK_MODULE_INIT(vtkRenderingFreeType)


#include <vtkSmartPointer.h>
#include <vtkImageData.h>
#include <vtkImageShiftScale.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkInteractorStyleImage.h>
#include <vtkRenderer.h>
#include <vtkImageActor.h>
#include <vtkJPEGReader.h>
#include <vtkImageCast.h>
#include <vtkImageGaussianSmooth.h>
#include <vtkImageGradient.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 <vtkCamera.h>
#include <vtkProperty.h>
#include <vtkStripper.h>
#include <vtkPolyDataMapper.h>
#include <vtkBMPReader.h>
#include <vtkImageLuminance.h>
//测试图像:../data/lena-gray.jpg
/*
vtkLinkEdgels (006E0448): No data to transform (or wrong data type)!
ERROR: In VTK\VTK-8.2.0\Filters\General\vtkSubPixelPositionEdgels.cxx, line 75
vtkSubPixelPositionEdgels (039006C0): No data to fit!
解决方案加入Update()
*/
int main(int argc, char* argv[])
{
    //读取图像数据
    vtkSmartPointer<vtkBMPReader> reader =
            vtkSmartPointer<vtkBMPReader>::New();
    reader->SetFileName("C:/Users/jbyyy/Desktop/work/QTDEMO/jbyyy/VTK/学习资料/VTK图形图像开发进阶+张晓东+PDF+源码/VTK图形图像开发进阶随书代码/VTK图形图像开发进阶随书代码/Examples/Chap05/data/lena.bmp");
    reader->Update();

    int dims[3];   //获取图像大小
    reader->GetOutput()->GetDimensions(dims);
    int nbOfComp;
    nbOfComp = reader->GetOutput()->GetNumberOfScalarComponents();	//获取像素的元组组分数(是灰度图、梯度图、RGB图)
    vtkAlgorithmOutput *data = reader->GetOutputPort();

    if(nbOfComp >= 3 )
    {
        vtkImageLuminance *luminanceFilter =
                vtkImageLuminance::New();
        luminanceFilter->SetInputData(reader->GetOutput());
        luminanceFilter->Update();
        data = luminanceFilter->GetOutputPort();
        std::cout << "输入图像为rbg图像 需要先转化成灰度图像:" << dims[0] <<" "<< dims[1] <<" "<< dims[2] <<" "<< nbOfComp << std::endl;
    }

    //转成float类型
    vtkSmartPointer<vtkImageCast> ic =
            vtkSmartPointer<vtkImageCast>::New();
    ic->SetOutputScalarTypeToFloat();
    ic->SetInputConnection(data);
    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();

    //计算矢量模—2-范数
    vtkSmartPointer<vtkImageMagnitude> imgMagnitude =
            vtkSmartPointer<vtkImageMagnitude>::New();
    imgMagnitude->SetInputData(imgGradient->GetOutput());
    imgMagnitude->Update();

    //将图像的非局部峰值设置为0
    vtkSmartPointer<vtkImageNonMaximumSuppression> nonMax =
            vtkSmartPointer<vtkImageNonMaximumSuppression>::New();
    nonMax->SetMagnitudeInputData(imgMagnitude->GetOutput());   //需要输入模值图像和矢量图像
    nonMax->SetVectorInputData(imgGradient->GetOutput());
    nonMax->SetDimensionality(2);
    nonMax->Update();


    //增加图像的大小
    vtkSmartPointer<vtkImageConstantPad> pad =
            vtkSmartPointer<vtkImageConstantPad>::New();
    pad->SetInputConnection(imgGradient->GetOutputPort());
    pad->SetOutputNumberOfScalarComponents(3);//设置输出图像的组分数 这里的作用是将梯 度图像像素的组分修改为3,方便后续的vtklmageToStructuredPoints使用
    pad->SetConstant(0);    //设置输出图像中扩大的区域像素值
    pad->Update();

    //3)vtklmageToStructuredPoints«该类将vtklmageData格式转换为规则点集。
    //该类的输入类型是vtklmageData,另外还有一个可选的RGB三组分向量图像输入
    //其输出类型是 vtkStructuredPointSo当输入矢量图像时,矢量图像像素数据会转为输出图像的对应点的属性。
    vtkSmartPointer<vtkImageToStructuredPoints> i2sp1 =
            vtkSmartPointer<vtkImageToStructuredPoints>::New();
    i2sp1->SetInputData(nonMax->GetOutput());
    i2sp1->SetVectorInputData(pad->GetOutput());
    i2sp1->Update();

    //据点的相邻关系将点连接成连续的折线(Polyline)
    vtkSmartPointer<vtkLinkEdgels> imgLink =
            vtkSmartPointer<vtkLinkEdgels>::New();
    imgLink->SetInputData(i2sp1->GetOutput());
    imgLink->SetGradientThreshold(2);
    imgLink->Update();

    //获取输入任意类型数据的满足阈值条件的单元数据
    /*
    当属性为多组分数据时,还需要设置阈值比较时 使用哪个组分的数据。
    其中提供了三种模式选择:所有组分都满足阈值条件、任意一个组分 满足阈值条件和用户指定的组分满足阈值条件。
    当使用点属性数据时,如果设置了 AllScalars, 那么单元满足阈值条件的前提是其所有点的属性都满足阈值条件。
    这里将阙值设置为10,即 Canny中双阈值的较大阈值。
    */
    vtkSmartPointer<vtkThreshold> thresholdEdgels =
            vtkSmartPointer<vtkThreshold>::New();
    thresholdEdgels->SetInputData(imgLink->GetOutput());
    thresholdEdgels->ThresholdByUpper(10);
    thresholdEdgels->AllScalarsOff();
    thresholdEdgels->Update();

    //将数据转换为几何数据,其输出类型为vtkPolyData。该
    //类从vtkThreshold的输出中提取图像边缘的几何数据。
    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());

    /*
    vtkStrippero该类用来将输入的多边形、三角形或者线段生成三角形条带或者折线段。
    注意:输入的多边形数据必须是三角形,否则不会进行带化处理。
    因此,处理多边形数据时, 可以先用vtkTriangleFilter类进行三角化后再使用这个类。
    如果输入中存在孤立点,也不会进 行任何处理。默认情况下,该Filter处理后会丢弃掉属性数据。
    */
    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<vtkRenderWindow> renderWindow =
            vtkSmartPointer<vtkRenderWindow>::New();
    renderWindow->SetSize(900, 300);
    renderWindow->AddRenderer(originalRenderer);
    renderWindow->AddRenderer(gradRenderer);
    renderWindow->Render();
    renderWindow->SetWindowName("CannyExample");

    vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
            vtkSmartPointer<vtkRenderWindowInteractor>::New();
    vtkSmartPointer<vtkInteractorStyleImage> style =
            vtkSmartPointer<vtkInteractorStyleImage>::New();

    renderWindowInteractor->SetInteractorStyle(style);
    renderWindowInteractor->SetRenderWindow(renderWindow);
    renderWindowInteractor->Initialize();
    renderWindowInteractor->Start();
	return EXIT_SUCCESS;
}

拉普拉斯算子

拉普拉斯算子是一个二阶边缘算子,即梯度的散度。拉普拉斯算子也是通过模板实现。 常用的拉普拉斯算子模板定义如下图所示。
在这里插入图片描述
拉普拉斯算子计算图像的二阶导数,对于图像噪声比较敏感。拉普拉斯算子的结果为标 量,表示边缘的宽度。但是它常产生双像素宽边缘,而且不能提供方向信息,因此较少直接 用于边缘检测。
在这里插入图片描述

#include <QApplication>
#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2)
VTK_MODULE_INIT(vtkInteractionStyle)
VTK_MODULE_INIT(vtkRenderingFreeType)


#include <vtkSmartPointer.h>
#include <vtkImageData.h>
#include <vtkImageCanvasSource2D.h>
#include <vtkImageShiftScale.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkInteractorStyleImage.h>
#include <vtkRenderer.h>
#include <vtkImageActor.h>
#include <vtkJPEGReader.h>
#include <vtkImageLaplacian.h>

//测试图像:../data/lena-gray.jpg
int main(int argc, char* argv[])
{
    vtkSmartPointer<vtkJPEGReader> reader =
        vtkSmartPointer<vtkJPEGReader>::New();
    reader->SetFileName("C:/Users/jbyyy/Desktop/work/QTDEMO/jbyyy/VTK/学习资料/VTK图形图像开发进阶+张晓东+PDF+源码/VTK图形图像开发进阶随书代码/VTK图形图像开发进阶随书代码/Examples/Chap05/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<vtkRenderWindow> renderWindow =
        vtkSmartPointer<vtkRenderWindow>::New();
    renderWindow->AddRenderer(originalRenderer);
    renderWindow->AddRenderer(gradRenderer);
    renderWindow->SetSize(640, 320);
    renderWindow->Render();
    renderWindow->SetWindowName("LaplacianExample");

    vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
        vtkSmartPointer<vtkRenderWindowInteractor>::New();
    vtkSmartPointer<vtkInteractorStyleImage> style =
        vtkSmartPointer<vtkInteractorStyleImage>::New();

    renderWindowInteractor->SetInteractorStyle(style);
    renderWindowInteractor->SetRenderWindow(renderWindow);
    renderWindowInteractor->Initialize();
    renderWindowInteractor->Start();

    return EXIT_SUCCESS;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值