vtk里面从分割好的体数据获取等值面的方法基本上都是基于MarchingCube的方法,有很多相关函数,一般从标记图像中获取等值面的方法为vtkDiscreteMarchingCubes。图像经过等值面提取后,会生成包含大量边的polyData,此时数据量很大且可能不够平滑,一般会对网络进行平滑和抽取,对应类:vtkWindowedSincPolyDataFilter,vtkDecimatePro。其中,vtkDecimatePro只能对三角片组成的网络处理,因此在进行抽取前,可以用vtkTriangleFilter对网络进行三角化。
一个等值面提取的程序如下:
#include <vtkSmartPointer.h>
#include "vtkCamera.h"
#include "vtkColorTransferFunction.h"
#include "vtkDICOMImageReader.h"
#include "vtkImageData.h"
#include "vtkPiecewiseFunction.h"
#include "vtkProperty.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkVolume.h"
#include "vtkVolumeProperty.h"
#include "vtkSmartVolumeMapper.h"
#include "vtkInteractorStyleTrackballCamera.h"
#include <vtkActor.h>
#include <vtkMarchingCubes.h>
#include <vtkStripper.h>
#include <vtkPolyDataMapper.h>
#include <vtkVolumeMapper.h>
#include <vtkDecimatePro.h>
#include <vtkPolyDataNormals.h>
#include <vtkSmoothPolyDataFilter.h>
#include <vtkTriangleFilter.h>
#include <vtkDataSetReader.h>
#include <vtkQuadricDecimation.h>
#include <vtkQuadricClustering.h>
#include <vtkImageMarchingCubes.h>
#include <vtkDiscreteMarchingCubes.h>
#include <vtkWindowedSincPolyDataFilter.h>
using namespace std;
int main()
{
string DataPath = "E:\\数据集\\3Dircadb1\\3Dircadb1.8\\MASKS_DICOM\\liver";
//Read Volume
vtkSmartPointer<vtkDICOMImageReader> dicomReader = vtkSmartPointer<vtkDICOMImageReader>::New();
dicomReader->SetDirectoryName(DataPath.c_str());
dicomReader->Update();
vtkSmartPointer<vtkImageData> LiverVolume = vtkSmartPointer<vtkImageData>::New();
LiverVolume = dicomReader->GetOutput();
vtkSmartPointer<vtkSmartVolumeMapper> LiverVolumeMapper = vtkSmartPointer<vtkSmartVolumeMapper>::New();
LiverVolumeMapper->SetInputConnection(dicomReader->GetOutputPort());
//set volume property
vtkSmartPointer<vtkColorTransferFunction> ColorTf = vtkSmartPointer<vtkColorTransferFunction>::New();
ColorTf->AddRGBSegment(0, 0, 0, 0, 255, 1, 1, 1);
vtkSmartPointer< vtkPiecewiseFunction> OpacityFun = vtkSmartPointer<vtkPiecewiseFunction>::New();
OpacityFun->AddPoint(0, 0);
OpacityFun->AddPoint(255, 1);
vtkSmartPointer<vtkVolumeProperty> VolumPro = vtkSmartPointer<vtkVolumeProperty>::New();
VolumPro->SetColor(ColorTf);
VolumPro->SetScalarOpacity(OpacityFun);
VolumPro->SetInterpolationTypeToLinear();
vtkSmartPointer<vtkVolume> LiverVolumActor = vtkSmartPointer<vtkVolume>::New();
LiverVolumActor->SetMapper(LiverVolumeMapper);
LiverVolumActor->SetProperty(VolumPro);
//get mesh
vtkSmartPointer<vtkDiscreteMarchingCubes> ImageMC = vtkSmartPointer<vtkDiscreteMarchingCubes>::New();
ImageMC->SetInputConnection(dicomReader->GetOutputPort());
ImageMC->SetValue(0, 255);
ImageMC->Update();
vtkSmartPointer<vtkWindowedSincPolyDataFilter> DataSmooth = vtkSmartPointer<vtkWindowedSincPolyDataFilter>::New();
DataSmooth->SetInputConnection(ImageMC->GetOutputPort());
DataSmooth->SetFeatureAngle(120);
DataSmooth->SetNumberOfIterations(55);
DataSmooth->SetPassBand(0.001);
DataSmooth->BoundarySmoothingOff();
DataSmooth->FeatureEdgeSmoothingOff();
DataSmooth->NonManifoldSmoothingOn();
DataSmooth->NormalizeCoordinatesOn();
DataSmooth->Update();
vtkSmartPointer<vtkTriangleFilter> LiverTriangle = vtkSmartPointer<vtkTriangleFilter>::New();
LiverTriangle->SetInputConnection(DataSmooth->GetOutputPort());
//reduce mesh
vtkSmartPointer<vtkDecimatePro> ReduceMesh = vtkSmartPointer<vtkDecimatePro>::New();
ReduceMesh->SetInputConnection(LiverTriangle->GetOutputPort());
ReduceMesh->SetTargetReduction(0.95);
ReduceMesh->PreserveTopologyOn();
vtkSmartPointer<vtkWindowedSincPolyDataFilter> LiverSmooth = vtkSmartPointer<vtkWindowedSincPolyDataFilter>::New();
LiverSmooth->SetInputConnection(ReduceMesh->GetOutputPort());
LiverSmooth->SetNumberOfIterations(10);
LiverSmooth->SetFeatureAngle(120);
vtkSmartPointer<vtkDecimatePro> ReduceMesh2 = vtkSmartPointer<vtkDecimatePro>::New();
ReduceMesh2->SetInputConnection(LiverSmooth->GetOutputPort());
ReduceMesh2->SetTargetReduction(0.3);
ReduceMesh2->PreserveTopologyOn();
vtkSmartPointer<vtkWindowedSincPolyDataFilter> LiverSmooth2 = vtkSmartPointer<vtkWindowedSincPolyDataFilter>::New();
LiverSmooth2->SetInputConnection(ReduceMesh->GetOutputPort());
LiverSmooth2->SetNumberOfIterations(15);
LiverSmooth2->SetFeatureAngle(120);
vtkSmartPointer<vtkPolyDataNormals> LiverNormal = vtkSmartPointer<vtkPolyDataNormals>::New();
LiverNormal->SetInputConnection(LiverSmooth2->GetOutputPort());
vtkSmartPointer<vtkPolyDataMapper> LiverPolyMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
LiverPolyMapper->SetInputConnection(LiverNormal->GetOutputPort());
vtkSmartPointer<vtkActor> LiverPolyAct = vtkSmartPointer<vtkActor>::New();
LiverPolyAct->SetMapper(LiverPolyMapper);
LiverPolyAct->GetProperty()->SetColor(0.93, 0.9, 0.6);
LiverPolyAct->GetProperty()->SetDiffuse(0.7);
LiverPolyAct->GetProperty()->SetSpecular(0.4);
//render tools
vtkSmartPointer<vtkRenderer> VolumeRender = vtkSmartPointer<vtkRenderer>::New();
VolumeRender->AddVolume(LiverVolumActor);
VolumeRender->SetViewport(0, 0, 0.5, 1);
VolumeRender->ResetCamera();
vtkSmartPointer<vtkRenderer> MeshRender = vtkSmartPointer<vtkRenderer>::New();
MeshRender->AddActor(LiverPolyAct);
MeshRender->SetViewport(0.5, 0, 1, 1);
MeshRender->SetActiveCamera(VolumeRender->GetActiveCamera());
MeshRender->SetBackground(1, 1, 1);
vtkSmartPointer<vtkRenderWindow> RendWin = vtkSmartPointer<vtkRenderWindow>::New();
RendWin->SetSize(1200, 400);
RendWin->AddRenderer(VolumeRender);
RendWin->AddRenderer(MeshRender);
vtkSmartPointer<vtkRenderWindowInteractor> RendWinIt = vtkSmartPointer<vtkRenderWindowInteractor>::New();
RendWinIt->SetRenderWindow(RendWin);
RendWin->Render();
RendWinIt->Start();
}
结果如下:左边是体数据绘制,右边是提取的等值面经过抽取和平滑后的结果