【QT+VTK 学习笔记】13:VTK图形处理2 法向量、曲率计算、网格平滑

前言

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

学习资料

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

2.1法向量计算

法向量的定义
法向量,是空间解析几何的一个概念,垂直于平面的直线所表示的向量为该平面的法向量。法向量适用于解析几何。由于空间内有无数个直线垂直于已知平面,因此一个平面都存在无数个法向量(包括两个单位法向量)。
在这里插入图片描述
直线的法向量是与方向向量相垂直的向量。
三维平面的法向量是指垂直于该平面的三维向量。曲面在某点P处的法向量为垂直于该点切平面的向量。对于一个网格模型,其每一个点和单元都可以计算一个法向量,在三维计 算机图形学中法向量一个重要的应用是光照和阴影计算。对于网格模型,模型是由一定数量的面片(单元)来逼近的,面片越多,则模型越精细;反之,则越粗糙。
vtkPolyDataNormals
VTK中计算法向量的Filter为vtkPolyDataNormals该类针对单元为三角形或者多边形 类型的vtkPolyData数据进行计算。
由于法向量分为点法向量和单元法向量,
可以通过函数 SetComputeCellNormals()和SetComputePointNormals()
来设置需要计算的法向量类型。默认情 况下计算点法向量,关闭单元法向量计算。

2.2符号化 Glyphing

模型的法向量数据是向量数据,因此法向量不能像前面讲到的通过颜色映射来显示。但是可以通过符号化(Glyphing)技术将法向量图形化显示如下图右边的面具效果。
在这里插入图片描述

计算法向量前后的模型显示效果图。从图中可以看出,加入法向量后,模型显 示得更加平滑,而未计算法向量的模型看起来比较粗糙。

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


#include <vtkSmartPointer.h>
#include <vtkActor.h>
#include <vtkPolyDataMapper.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkXMLPolyDataReader.h>
#include <vtkPolyDataReader.h>
#include <vtkPLYReader.h>
#include <vtkFloatArray.h>
#include <vtkPointData.h>
#include <vtkPolyDataNormals.h>
#include <vtkGlyph3D.h>
#include <vtkArrowSource.h>
#include <vtkSphereSource.h>
#include <vtkMaskPoints.h>
#include <vtkProperty.h>

//测试文件:../data/fran_cut.vtk
int main(int argc, char *argv[])
{
    vtkNew<vtkPolyDataReader> reader;
    reader->SetFileName("C:/Users/jbyyy/Desktop/work/QTDEMO/jbyyy/VTK/学习资料/VTK图形图像开发进阶+张晓东+PDF+源码/VTK图形图像开发进阶随书代码/VTK图形图像开发进阶随书代码/Examples/Chap06/data/fran_cut.vtk");
    reader->Update();

    /*
    VTK中计算法向量的Filter为vtkPolyDataNormals»该类针对单元为三角形或者多边形 类型的vtkPolyData数据进行计算。
    由于法向量分为点法向量和单元法向量,
    可以通过函数 SetComputeCell Normals()和SetComputePointNormals。
    来设置需要计算的法向量类型。默认情况下计算点法向量,关闭单元法向量计算。
    */
    vtkNew<vtkPolyDataNormals> normfilter;
    normfilter->SetInputData(reader->GetOutput());
    normfilter->SetComputePointNormals(1);      //开启计算点法向量
    normfilter->SetComputeCellNormals(0);       //关闭单元法向量计算
    normfilter->SetAutoOrientNormals(1);        //设置自动调整法向量
    //vtkPolyDataNormals默认开启对锐边缘(Sharp Edge)的处理。
    //如果检测到存在锐边缘,则会将其分裂,因此模型的数据可能会发生变化。
    //可以通过vtkPolyDataNormals::SetSplitting。函数关闭该功能
    normfilter->SetSplitting(0);
    normfilter->Update();

    //模型上随机生成10+1个点
    vtkNew<vtkMaskPoints> mask;
    mask->SetInputData(normfilter->GetOutput());
    mask->SetMaximumNumberOfPoints(10);
    mask->RandomModeOn();
    mask->Update();

    //在随机生成的点上 加上箭头方向
    vtkNew<vtkArrowSource> arrow;
    arrow->Update();
    vtkNew<vtkGlyph3D> glyph;
    glyph->SetInputData(mask->GetOutput());     //设置点数据位置
    glyph->SetSourceData(arrow->GetOutput());   //设置源数据
    glyph->SetVectorModeToUseNormal();  //知道要使用法向量数据来控制glyph图形的方向
    glyph->SetScaleFactor(0.05);        //箭头图形长度
    glyph->Update();

    //原始图
    vtkNew<vtkPolyDataMapper> originmapper;
    originmapper->SetInputData(reader->GetOutput());
    vtkNew<vtkActor> originactor;
    originactor->SetMapper(originmapper);
    //法向量计算后
    vtkNew<vtkPolyDataMapper> normedmapper;
    normedmapper->SetInputData(normfilter->GetOutput());
    vtkNew<vtkActor> normedactor;
    normedactor->SetMapper(normedmapper);
    //显示几个法向量的图
    vtkNew<vtkPolyDataMapper> glyphmapper;
    glyphmapper->SetInputData(glyph->GetOutput());

    vtkNew<vtkActor> glyphactor;
    glyphactor->SetMapper(glyphmapper);
    glyphactor->GetProperty()->SetColor(1., 0.,0.);

    double originalviewport[4] = {0.0, 0.0, 0.33, 1.0};
    double normviewport[4] = {0.33, 0.0, 0.66, 1.0};
    double glphviewport[4] = {0.66, 0.0, 1.0, 1.0};

    vtkNew<vtkRenderer> originalrenderer;
    originalrenderer->SetViewport(originalviewport);
    originalrenderer->AddActor(originactor);
    originalrenderer->ResetCamera();
    originalrenderer->SetBackground(1.0, 1.0, 1.0);

    vtkNew<vtkRenderer> normedrenderer;
    normedrenderer->SetViewport(normviewport);
    normedrenderer->AddActor(normedactor);
    normedrenderer->ResetCamera();
    normedrenderer->SetBackground(1.0, 1.0, 1.0);

    vtkNew<vtkRenderer> glyphRenderer;
    glyphRenderer->SetViewport(glphviewport);
    glyphRenderer->AddActor(glyphactor);
    glyphRenderer->AddActor(normedactor);
    glyphRenderer->ResetCamera();
    glyphRenderer->SetBackground(1.0, 1.0, 1.0);

    vtkNew<vtkRenderWindow> renderwindow;
    renderwindow->AddRenderer(originalrenderer);
    renderwindow->AddRenderer(normedrenderer);
    renderwindow->AddRenderer(glyphRenderer);
    renderwindow->SetSize(640, 320);
    renderwindow->Render();
    renderwindow->SetWindowName("PolyDataNormal");

    vtkNew<vtkRenderWindowInteractor> renderwindowinteractor;
    renderwindowinteractor->SetRenderWindow(renderwindow);
    renderwindowinteractor->Initialize();
    renderwindowinteractor->Start();

    return EXIT_SUCCESS;
}

2.3曲率计算

VTK中的vtkCurvatures类实现了四种计算网格模型点曲率的计算方法。该类接受一个 vtkPolyData数据,将计算得到的曲率数据作为网格模型的点的属性数据存入返回的 vtkPolyData中。
实例:一个网格模型的曲率计算,并通过颜色映 射表来显示模型表面的曲率
在这里插入图片描述

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

#include <vtkSmartPointer.h>
#include <vtkCurvatures.h>
#include <vtkPolyDataReader.h>
#include <vtkLookupTable.h>
#include <vtkColorTransferFunction.h>
#include <vtkColorSeries.h>
#include <vtkPointData.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkScalarBarActor.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkRenderWindowInteractor.h>

#include <iostream>
using namespace std;

//测试文件:../data/fran_cut.vtk
int main(int argc, char *argv[])
{
    vtkSmartPointer<vtkPolyDataReader> reader =
        vtkSmartPointer<vtkPolyDataReader>::New();
    reader->SetFileName("C:/Users/jbyyy/Desktop/work/QTDEMO/jbyyy/VTK/学习资料/VTK图形图像开发进阶+张晓东+PDF+源码/VTK图形图像开发进阶随书代码/VTK图形图像开发进阶随书代码/Examples/Chap06/data/fran_cut.vtk");
    reader->Update();

    vtkSmartPointer<vtkCurvatures> curvaturesFilter =
        vtkSmartPointer<vtkCurvatures>::New();
    curvaturesFilter->SetInputConnection(reader->GetOutputPort());
    //curvaturesFilter->SetCurvatureTypeToMinimum();
    curvaturesFilter->SetCurvatureTypeToMaximum();          //计算最大主曲率
    //curvaturesFilter->SetCurvatureTypeToGaussian();
    //curvaturesFilter->SetCurvatureTypeToMean();
    curvaturesFilter->Update();

    //获取高斯曲率数据
    //四种曲率属性数据分别对应属性名字为 Minimum_Curvature、Maximum_Curvature、Gauss_Curvature和Mean_Curvature
    vtkDataArray *gauss = static_cast<vtkDataArray*>(curvaturesFilter->GetOutput()->GetPointData()->GetArray("Gauss_Curvature"));

    double scalarRange[2];
    curvaturesFilter->GetOutput()->GetScalarRange(scalarRange);

    vtkSmartPointer<vtkLookupTable> lut =
        vtkSmartPointer<vtkLookupTable>::New();
    lut->SetHueRange(0.0,0.6);
    lut->SetAlphaRange(1.0,1.0);
    lut->SetValueRange(1.0,1.0);
    lut->SetSaturationRange(1.0,1.0);
    lut->SetNumberOfTableValues(256);
    lut->SetRange(scalarRange);         //设置范围
    lut->Build();

    vtkSmartPointer<vtkPolyDataMapper> mapper =
        vtkSmartPointer<vtkPolyDataMapper>::New();
    mapper->SetInputData(curvaturesFilter->GetOutput());
    mapper->SetLookupTable(lut);
    mapper->SetScalarRange(scalarRange);

    vtkSmartPointer<vtkActor> actor =
        vtkSmartPointer<vtkActor>::New();
    actor->SetMapper(mapper);

    //vtkScalarBarActor类,该类支持将一个颜色映射表转换为一个Actor对象
    //将颜色表以图形的形式显示,并支持设置图形相应的名字和显示的数据Label个数
    vtkSmartPointer<vtkScalarBarActor> scalarBar =
        vtkSmartPointer<vtkScalarBarActor>::New();
    scalarBar->SetLookupTable(mapper->GetLookupTable());
    scalarBar->SetTitle(curvaturesFilter->GetOutput()->GetPointData()->GetScalars()->GetName());
    scalarBar->SetNumberOfLabels(5);

    vtkSmartPointer<vtkRenderer> renderer =
        vtkSmartPointer<vtkRenderer>::New();
    renderer->AddActor(actor);
    renderer->AddActor2D(scalarBar);
    renderer->SetBackground(1.0, 1.0, 1.0);

    vtkSmartPointer<vtkRenderWindow> renderWindow =
        vtkSmartPointer<vtkRenderWindow>::New();
    renderWindow->AddRenderer(renderer);
    renderWindow->SetSize(640, 480);
    renderWindow->Render();
    renderWindow->SetWindowName("PolyDataCurvature");

    vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
        vtkSmartPointer<vtkRenderWindowInteractor>::New();
    renderWindowInteractor->SetRenderWindow(renderWindow);

    renderWindow->Render();
    renderWindowInteractor->Start();
    return EXIT_SUCCESS;
}

网格平滑

现代扫描技术的发展使得获取点云数据不再困难,通过曲线重建技术可以获取表面网格来表示各种复杂的实体。但是点云数据中往往存在噪声,这样得到的重建网格通常都需要进行平滑处理。

拉普拉斯平滑是一种常用的网格平滑算法。该方法的原理比较简单,如下图所示:
在这里插入图片描述
将每个点用其邻域点的中心来代替。通过不断地迭代,可以得到较为光滑的网格。
VTKSmoothPolyDataFilter类实现了网格的拉普拉斯平滑算法,使用方法如下:
在这里插入图片描述
参考:https://blog.csdn.net/weixin_38293453/article/details/104689138

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

#include <vtkSmartPointer.h>
#include <vtkPolyDataReader.h>
#include <vtkSmoothPolyDataFilter.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkWindowedSincPolyDataFilter.h>
int main()
{
    vtkSmartPointer<vtkPolyDataReader> reader =
        vtkSmartPointer<vtkPolyDataReader>::New();
    reader->SetFileName("C:/Users/jbyyy/Desktop/work/QTDEMO/jbyyy/VTK/学习资料/VTK图形图像开发进阶+张晓东+PDF+源码/VTK图形图像开发进阶随书代码/VTK图形图像开发进阶随书代码/Examples/Chap06/data/fran_cut.vtk");
    reader->Update();

    vtkSmartPointer<vtkSmoothPolyDataFilter> smoothFilter =
        vtkSmartPointer<vtkSmoothPolyDataFilter>::New();
    smoothFilter->SetInputConnection(reader->GetOutputPort());
    smoothFilter->SetNumberOfIterations(300);   //迭代多少此 拉普拉斯平滑
    //其实在该类中还有多个变量来控制平滑过程,利用这些变量在一定程度上可以控制细节的损失。
    smoothFilter->BoundarySmoothingOff();       //控制是否对边界点进行平滑
    smoothFilter->FeatureEdgeSmoothingOn();     //是否对特征边上的点进行平滑
    smoothFilter->Update();

//虽然通过特征边平滑设置可以降低一部分细节损失,
//但并不能完全避免,且随着laplace平滑的不断迭代,
//模型会逐渐向网格的中心收缩。
//所以,vtkWindowSincPolyDataFilter是一种更好的选择
//该算法采用窗口Sinc函数实现网格平滑,能够最小程度地避免收缩
//    vtkSmartPointer<vtkWindowedSincPolyDataFilter> wndSincSmoothFilter =
//            vtkSmartPointer<vtkWindowedSincPolyDataFilter>::New();
//        wndSincSmoothFilter->SetInputConnection(reader->GetOutputPort());
//        wndSincSmoothFilter->SetNumberOfIterations(100);
//        wndSincSmoothFilter->Update();

    vtkSmartPointer<vtkPolyDataMapper> inputMapper =
        vtkSmartPointer<vtkPolyDataMapper>::New();
    inputMapper->SetInputConnection(reader->GetOutputPort());
    vtkSmartPointer<vtkActor> inputActor =
        vtkSmartPointer<vtkActor>::New();
    inputActor->SetMapper(inputMapper);

    vtkSmartPointer<vtkPolyDataMapper> smoothedMapper =
        vtkSmartPointer<vtkPolyDataMapper>::New();
    smoothedMapper->SetInputConnection(smoothFilter->GetOutputPort());
    vtkSmartPointer<vtkActor> smoothedActor =
        vtkSmartPointer<vtkActor>::New();
    smoothedActor->SetMapper(smoothedMapper);

    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->AddActor(inputActor);
    leftRenderer->SetBackground(0.2, 0, .5);
    leftRenderer->ResetCamera();

    vtkSmartPointer<vtkRenderer> rightRenderer =
        vtkSmartPointer<vtkRenderer>::New();
    rightRenderer->SetViewport(rightViewport);
    rightRenderer->AddActor(smoothedActor);
    rightRenderer->SetBackground(0.5, 0.5, 0);
    rightRenderer->ResetCamera();

    vtkSmartPointer<vtkRenderWindow> rw =
        vtkSmartPointer<vtkRenderWindow>::New();
    rw->AddRenderer(leftRenderer);
    rw->AddRenderer(rightRenderer);
    rw->SetSize(640, 320);
    rw->SetWindowName("PolyData Grid Smooth By LapLasian");

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

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值