天天看点

VTK笔记-多边形数据转换图像数据-vtkPolyData转换为vtkImageDatavtkPolyDataToImageStencilvtkImageStencilvtkImageStencilToImage示例

  VTK中vtkImageData类和vtkPolyData类使用频率极高,vtkImageData类和vtkPolyData类派生自vtkDataSet类,是数据集的一种。

  vtkPolyData数据由几何结构数据、拓扑结构数据和属性数据组成。几何结构数据主要是组成模型的点集,拓扑结构数据是点按一定关系组成的单元数据,属性数据与几何结构数据和拓扑结构数据想关联,可以标量、向量、张量,可以用来间接表示图像的颜色。vtkPolyData可以表示多种几何数据,用来表示顶点、线、多边形、三角形带在内的几何结构,即三维实体。通常使用者在界面或者交互器上拾取的坐标点积、勾勒的多边形以及闭合三角带形成的多面体都可以使用vtkPolyData来表示和渲染,vtkPolyData占据了VTK的数据集中很大一部分。

  VTK库是图形图像可视化库,那么除了多边形的图形vtkPolyData数据,还有常用到的图像数据,图像数据主要是使用了vtkImageData类。vtkImageData类表示的是一种在拓扑上和几何上都是规则的几何结构,包括了体素数据和像素数据,所以vtkImageData类可以表示二维和三维的图像。使用vtkImageData作为输入和输出的类大多都以vtkImage作为前缀,主要是用来处理图像。

VTK笔记-多边形数据转换图像数据-vtkPolyData转换为vtkImageDatavtkPolyDataToImageStencilvtkImageStencilvtkImageStencilToImage示例

  在项目实践中,经常会碰到几何多边形vtkPolyData数据转换为图像vtkImageData数据。比如导入几何多边形格式的文件然后需要按照图像体渲染方式输出MIP图像或者DDR图像;有比如在图像数据上圈选病灶进行分割图像,其中圈选可以生成多边形数据,然后就需要转换为图像数据的掩膜对图像数据剪裁;以上这些场景都是用到了几何多边形转换图像数据,如何转换是个问题,下面记录下我搜集到的资料;

vtkPolyDataToImageStencil

  vtkPolyDataToImageStencil类可以将vtkPolyData数据转换为图像模具(Stencil)。vtkPolyData数据可以是闭合曲面网格或一系列多段线等高线(每个切片一个等高线)。需要注意的是:如果提供了等高线,则等高线必须与Z平面对齐。不支持其他轮廓方向。

VTK笔记-多边形数据转换图像数据-vtkPolyData转换为vtkImageDatavtkPolyDataToImageStencilvtkImageStencilvtkImageStencilToImage示例

接口

  使用SetInputData设定要转换为模具的多边形数据。

virtual void SetInputData(vtkPolyData*);
vtkPolyData *GetInput();
           

  使用vtkImageStencilSource类提供的三个函数设置输出图像的三维尺寸、像素间距和起点坐标;

这三个参数分别是SetOutputWholeExtent,SetOutputSpacing和SetOutputOrigin;如果没有指定起始坐标,则起始坐标默认值为(0,0,0);如果没有设置像素间距,则像素间距默认为(1,1,1);

vtkSetVector3Macro(OutputOrigin, double);
vtkGetVector3Macro(OutputOrigin, double);
vtkSetVector3Macro(OutputSpacing, double);
vtkGetVector3Macro(OutputSpacing, double);
vtkSetVector6Macro(OutputWholeExtent, int);
vtkGetVector6Macro(OutputWholeExtent, int);
           

vtkImageStencil

  vtkImageStencil类通过cookie cuter操作合并图像。

  vtkImageStencil将使用一个模具将两个图像组合在一起。模具应以vtkImageStencilData的形式提供。

  cookie cuter是英语俚语“千篇一律”的意思。从字面上讲,cookie-cutter 就是一个金属形状的圈圈,把曲奇饼切成想要的形状,由于曲奇饼很软、金属模具很硬,所以 cookie-cutter 切出来的东西都是一模一样的形状,这也就是为什么它用来形容“千篇一律”的原因。这里的“cookie cuter操作”是指使用模具在原有的图像数据上获取重叠的图像数据,变相是一种图像的融合操作;

VTK笔记-多边形数据转换图像数据-vtkPolyData转换为vtkImageDatavtkPolyDataToImageStencilvtkImageStencilvtkImageStencilToImage示例

接口

使用SetInputData设置输入的vtkImageData,模具数据是vtkImageStencilData指针类型;

使用SetStencilConnection设置模具数据;

virtual void SetStencilData(vtkImageStencilData *stencil);
vtkImageStencilData *GetStencil();
void SetStencilConnection(vtkAlgorithmOutput* outputPort)
{
  this->SetInputConnection(2, outputPort);
}
           

设置翻转类型ReverseStencil。可以根据ReverseStencil结合BackgroundValue来决定生成模具图像内的体素数值;

vtkTypeBool ReverseStencil;
vtkSetMacro(ReverseStencil, vtkTypeBool);
vtkBooleanMacro(ReverseStencil, vtkTypeBool);
vtkGetMacro(ReverseStencil, vtkTypeBool);
           

使用SetBackgroundInputData设置第二个输入。此图像将用于设置图像模具外的数据值。如果未设置,输出体素将被填充BackgroundValue值;BackgroundValue值由SetBackgroundValue方法设置;

virtual void SetBackgroundInputData(vtkImageData *input);
vtkImageData *GetBackgroundInput();
void SetBackgroundValue(double val) { this->SetBackgroundColor(val,val,val,val); };
double GetBackgroundValue() { return this->BackgroundColor[0]; };
           

vtkImageStencilToImage

  vtkImageStencilToImage类将图像模板转换为二进制图像。默认输出为8位图像,模具内的值为1,模具外的值为0。当与  vtkPolyDataToImageStencil类或vtkImplicitFunctionToImageStencil类结合使用时,可用于从网格或函数创建二进制图像。

VTK笔记-多边形数据转换图像数据-vtkPolyData转换为vtkImageDatavtkPolyDataToImageStencilvtkImageStencilvtkImageStencilToImage示例

接口

使用SetOutsideValue和SetInsideValue设置模具内外的值,分别对应了OutsideValue和InsideValue;InsideValue是模具内的值;OutsideValue是模具外的值;

使用SetOutputScalarType设置对外输出数据的类型;

double OutsideValue;
double InsideValue;
int OutputScalarType;
vtkSetMacro(OutsideValue, double);
vtkGetMacro(OutsideValue, double);
vtkSetMacro(InsideValue, double);
vtkGetMacro(InsideValue, double);
vtkSetMacro(OutputScalarType, int);
vtkGetMacro(OutputScalarType, int);
void SetOutputScalarTypeToFloat() { this->SetOutputScalarType(VTK_FLOAT); }
void SetOutputScalarTypeToDouble() { this->SetOutputScalarType(VTK_DOUBLE); }
void SetOutputScalarTypeToInt() { this->SetOutputScalarType(VTK_INT); }
void SetOutputScalarTypeToUnsignedInt() { this->SetOutputScalarType(VTK_UNSIGNED_INT); }
void SetOutputScalarTypeToLong() { this->SetOutputScalarType(VTK_LONG); }
void SetOutputScalarTypeToUnsignedLong() { this->SetOutputScalarType(VTK_UNSIGNED_LONG); }
void SetOutputScalarTypeToShort() { this->SetOutputScalarType(VTK_SHORT); }
void SetOutputScalarTypeToUnsignedShort() { this->SetOutputScalarType(VTK_UNSIGNED_SHORT); }
void SetOutputScalarTypeToUnsignedChar() { this->SetOutputScalarType(VTK_UNSIGNED_CHAR); }
void SetOutputScalarTypeToChar() { this->SetOutputScalarType(VTK_CHAR); }
           
vtkNew<vtkPolyDataToImageStencil> pol2stenc;
pol2stenc->SetInputData(geometry);
pol2stenc->SetOutputOrigin(origin);
pol2stenc->SetOutputSpacing(spacing);
pol2stenc->SetOutputWholeExtent(whiteImage->GetExtent());
pol2stenc->Update();
           

示例

下面使用一个例子说明vtkPolyData转换为vtkImageData;

1.实例化一个圆球对象sphereSource;

2.根据sphereSource的三维尺度,实例化一个图像数据vtkImageData对象whiteImage,whiteImage内的体素值为从各自体素的id值;

3.实例化vtkPolyDataToImageStencil类对象pol2stenc,输入模具来源数据sphereSource,使用whiteImage对象的三维尺寸值;

4.实例化vtkImageStencil对象imgstenc,输入图像数据whiteImage,模具数据pol2stenc,关闭翻转标识,设置模具外体素值为0;

5.渲染出结果;

#pragma once
#include <vtkImageData.h>
#include <vtkImageStencil.h>
#include <vtkMetaImageWriter.h>
#include <vtkNew.h>
#include <vtkPointData.h>
#include <vtkPolyData.h>
#include <vtkPolyDataToImageStencil.h>
#include <vtkSphereSource.h>
#include <vtkNamedColors.h>
#include <vtkInteractorStyleSwitch.h>
#include <vtkDataSetMapper.h>
#include <vtkCamera.h>

class Test_vtkPolyDataToImageStencil {
public:
	static void Test() {
		vtkNew<vtkSphereSource> sphereSource;
		sphereSource->SetRadius(250);
		sphereSource->SetPhiResolution(30);
		sphereSource->SetThetaResolution(30);
		sphereSource->SetCenter(250, 250, 250);
		auto pd = sphereSource->GetOutput();
		sphereSource->Update();

		vtkNew<vtkImageData> whiteImage;
		double bounds[6];
		pd->GetBounds(bounds);
		double spacing[3]; // desired volume spacing
		spacing[0] = 1;
		spacing[1] = 1;
		spacing[2] = 1;
		whiteImage->SetSpacing(spacing);

		// compute dimensions
		int dim[3];
		for (int i = 0; i < 3; i++){
			dim[i] = static_cast<int>(ceil((bounds[i * 2 + 1] - bounds[i * 2]) / spacing[i]));
		}
		whiteImage->SetDimensions(dim);
		whiteImage->SetExtent(0, dim[0] - 1, 0, dim[1] - 1, 0, dim[2] - 1);

		double origin[3];
		origin[0] = bounds[0] + spacing[0] / 2;
		origin[1] = bounds[2] + spacing[1] / 2;
		origin[2] = bounds[4] + spacing[2] / 2;
		whiteImage->SetOrigin(origin);
		whiteImage->AllocateScalars(VTK_UNSIGNED_CHAR, 1);

		// fill the image with foreground voxels:
		unsigned char inval = 255;
		unsigned char outval = 0;
		vtkIdType count = whiteImage->GetNumberOfPoints();
		for (vtkIdType i = 0; i < count; ++i){
			whiteImage->GetPointData()->GetScalars()->SetTuple1(i, 255);
		}

		// polygonal data --> image stencil:
		vtkNew<vtkPolyDataToImageStencil> pol2stenc;
		pol2stenc->SetInputData(pd);
		pol2stenc->SetOutputOrigin(origin);
		pol2stenc->SetOutputSpacing(spacing);
		pol2stenc->SetOutputWholeExtent(whiteImage->GetExtent());
		pol2stenc->Update();

		// cut the corresponding white image and set the background:
		vtkNew<vtkImageStencil> imgstenc;
		imgstenc->SetInputData(whiteImage);
		imgstenc->SetStencilConnection(pol2stenc->GetOutputPort());
		imgstenc->ReverseStencilOff();
		imgstenc->SetBackgroundValue(outval);
		imgstenc->Update();			   

		vtkNew<vtkNamedColors> colors;
		vtkColor3d backgroundColor = colors->GetColor3d("Wheat");
		vtkColor3d checkerColor = colors->GetColor3d("Tomato");
		vtkColor3d fillColor = colors->GetColor3d("Banana");
		vtkNew<vtkRenderer> renderer;
		renderer->SetBackground(backgroundColor.GetData());

		vtkNew<vtkRenderWindow> renderWindow;
		renderWindow->AddRenderer(renderer);
		renderWindow->SetSize(640, 480);

		vtkNew<vtkRenderWindowInteractor> interactor;
		vtkNew<vtkInteractorStyleSwitch> style;
		interactor->SetInteractorStyle(style);
		interactor->SetRenderWindow(renderWindow);
		vtkNew<vtkDataSetMapper> imageMapper;
		vtkNew<vtkActor> imageActor;
		imageActor->SetMapper(imageMapper);
		renderer->AddViewProp(imageActor);
		imageMapper->SetInputConnection(imgstenc->GetOutputPort());
		
		renderer->ResetCamera();
		renderer->GetActiveCamera()->Azimuth(120);
		renderer->GetActiveCamera()->Elevation(30);
		renderer->ResetCameraClippingRange();
		renderWindow->SetWindowName("ClipVolume");
		renderWindow->Render();

		interactor->Start();
	}
};
           
VTK笔记-多边形数据转换图像数据-vtkPolyData转换为vtkImageDatavtkPolyDataToImageStencilvtkImageStencilvtkImageStencilToImage示例

设置开启翻转像素,则模具外的数据就会填充为1,模具内的数据填充为0;

VTK笔记-多边形数据转换图像数据-vtkPolyData转换为vtkImageDatavtkPolyDataToImageStencilvtkImageStencilvtkImageStencilToImage示例

增加高斯模糊,做体渲染;

#pragma once
#include "vtk_include.h"
#include <vtkImageData.h>
#include <vtkImageStencil.h>
#include <vtkMetaImageWriter.h>
#include <vtkNew.h>
#include <vtkPointData.h>
#include <vtkPolyData.h>
#include <vtkPolyDataToImageStencil.h>
#include <vtkSphereSource.h>
#include <vtkImageGaussianSmooth.h>
class Test_vtkPolyDataToImageStencil {
public:
	static void Test() {
		vtkNew<vtkSphereSource> sphereSource;
		sphereSource->SetRadius(20);
		sphereSource->SetPhiResolution(30);
		sphereSource->SetThetaResolution(30);
		sphereSource->SetCenter(250, 250, 250);
		auto pd = sphereSource->GetOutput();
		sphereSource->Update();

		vtkNew<vtkImageData> whiteImage;
		double bounds[6];
		pd->GetBounds(bounds);
		double spacing[3]; // desired volume spacing
		spacing[0] = 0.5;
		spacing[1] = 0.5;
		spacing[2] = 0.5;
		whiteImage->SetSpacing(spacing);

		// compute dimensions
		int dim[3];
		for (int i = 0; i < 3; i++){
			dim[i] = static_cast<int>(ceil((bounds[i * 2 + 1] - bounds[i * 2]) / spacing[i]));
		}
		whiteImage->SetDimensions(dim);
		whiteImage->SetExtent(0, dim[0] - 1, 0, dim[1] - 1, 0, dim[2] - 1);

		double origin[3];
		origin[0] = bounds[0] + spacing[0] / 2;
		origin[1] = bounds[2] + spacing[1] / 2;
		origin[2] = bounds[4] + spacing[2] / 2;
		whiteImage->SetOrigin(origin);
		whiteImage->AllocateScalars(VTK_UNSIGNED_CHAR, 1);

		// fill the image with foreground voxels:
		unsigned char inval = 255;
		unsigned char outval = 0;
		vtkIdType count = whiteImage->GetNumberOfPoints();
		for (vtkIdType i = 0; i < count; ++i){
			whiteImage->GetPointData()->GetScalars()->SetTuple1(i, i);
		}

		// polygonal data --> image stencil:
		vtkNew<vtkPolyDataToImageStencil> pol2stenc;
		pol2stenc->SetInputData(pd);
		pol2stenc->SetOutputOrigin(origin);
		pol2stenc->SetOutputSpacing(spacing);
		pol2stenc->SetOutputWholeExtent(whiteImage->GetExtent());
		pol2stenc->Update();

		// cut the corresponding white image and set the background:
		vtkNew<vtkImageStencil> imgstenc;
		imgstenc->SetInputData(whiteImage);
		imgstenc->SetStencilConnection(pol2stenc->GetOutputPort());
		imgstenc->ReverseStencilOff();
		imgstenc->SetBackgroundValue(outval);
		imgstenc->Update();			   

		vtkImageGaussianSmooth *gaosi = vtkImageGaussianSmooth::New();

		//高斯平滑处理
		gaosi->SetInputData(imgstenc->GetOutput());
		gaosi->SetDimensionality(3);
		gaosi->SetRadiusFactors(2, 2, 1);
		gaosi->Update();

	
		vtkNew<vtkGPUVolumeRayCastMapper> gpuMapper;
		gpuMapper->SetInputData(gaosi->GetOutput());
		gpuMapper->SetMaskTypeToBinary();
		gpuMapper->SetMaskInput(NULL);

		vtkPiecewiseFunction * psfunction = vtkPiecewiseFunction::New();

		//透明度传递函数
		psfunction->AddPoint(0.0, 0.0);
		psfunction->AddPoint(40.0, 0.0);
		psfunction->AddPoint(50.0, 0.3);
		psfunction->AddPoint(110.0, 0.4);
		psfunction->AddPoint(120, 0.5);
		psfunction->AddPoint(130, 0.6);
		psfunction->AddPoint(190, 0.8);
		psfunction->AddPoint(255, 1.0);

		vtkColorTransferFunction * colfunction = vtkColorTransferFunction::New();

		//颜色传递函数
		colfunction->AddRGBPoint(0.0, 0.5, 0.3, 0.2);
		colfunction->AddRGBPoint(50.0, 0.8, 0.5, 0.5);
		colfunction->AddRGBPoint(110.0, 0.6, 0.2, 0.3);
		colfunction->AddRGBPoint(190.0, 0.81, 0.27, 0.1);
		colfunction->AddRGBPoint(255.0, 0.5, 0.9, 0.9);

		vtkVolumeProperty * volpro = vtkVolumeProperty::New();

		//体数据属性
		volpro->SetColor(colfunction);
		volpro->SetScalarOpacity(psfunction);
		volpro->ShadeOn();
		volpro->SetInterpolationTypeToLinear();
		volpro->SetAmbient(0.2);
		volpro->SetDiffuse(0.9);
		volpro->SetSpecular(0.2);
		volpro->SetSpecularPower(10);

		vtkVolume * data = vtkVolume::New();

		//体数据映射器和属性
		data->SetMapper(gpuMapper);
		data->SetProperty(volpro);

		vtkRenderer *render = vtkRenderer::New();
		vtkRenderWindow *renwin = vtkRenderWindow::New();
		vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::New();

		renwin->AddRenderer(render);
		renwin->SetInteractor(iren);

		//绘制器
		render->AddVolume(data);
		render->SetBackground(0, 0, 0);
		render->ResetCameraClippingRange();

		//绘制窗口
		renwin->AddRenderer(render);

		renwin->Render();
		iren->Initialize();
		iren->Start();
	}
};
           
VTK笔记-多边形数据转换图像数据-vtkPolyData转换为vtkImageDatavtkPolyDataToImageStencilvtkImageStencilvtkImageStencilToImage示例

继续阅读