目录
VTK 9.3.0 基于 Visual Studio 2017 开发体绘制实现教程
VTK 9.3.0 基于 Visual Studio 2017 开发体绘制实现教程
本文将详细介绍如何使用 VTK 9.3.0 在 Visual Studio 2017 中进行体绘制的实现过程。通过本文的学习,你将掌握如何使用 VTK 进行医学图像的体绘制以及如何添加交互功能。
准备工作
在开始之前,请确保你已经完成以下步骤:
- 安装 Visual Studio 2017。
- 下载并安装 VTK 9.3.0 库,并确保你能够在 Visual Studio 中正确配置它。
环境配置
- 开源库:VTK 9.3.0
- 开发工具:Visual Studio 2017
- 开发语言:C++
实现过程
以下是实现体绘制的详细代码:
class vtkImageInteractionCallback : public vtkCommand
{
public:
static vtkImageInteractionCallback* New()
{
return new vtkImageInteractionCallback();
}
vtkImageInteractionCallback()
: ImageReslice(nullptr), Slicing(0) {}
void SetImageReslice(vtkImageReslice* reslice) { this->ImageReslice = reslice; }
virtual void Execute(vtkObject* caller, unsigned long eventId, void* callData) override
{
vtkRenderWindowInteractor* interactor = vtkRenderWindowInteractor::SafeDownCast(caller);
if (!interactor) return;
int x, y;
interactor->GetEventPosition(x, y);
if (eventId == vtkCommand::MouseMoveEvent)
{
if (this->Slicing)
{
this->ProcessSlicing(interactor, x, y);
}
}
else if (eventId == vtkCommand::LeftButtonPressEvent)
{
this->Slicing = 1;
}
else if (eventId == vtkCommand::LeftButtonReleaseEvent)
{
this->Slicing = 0;
}
}
protected:
void ProcessSlicing(vtkRenderWindowInteractor* interactor, int x, int y)
{
// 获取当前切片的中心位置
double sliceCenter[3];
this->ImageReslice->GetOutput()->GetCenter(sliceCenter);
// 获取鼠标移动的增量
int lastX = interactor->GetLastEventPosition()[0];
int lastY = interactor->GetLastEventPosition()[1];
int deltaY = y - lastY;
// 根据移动的方向和增量调整切片位置
double newSlicePosition = sliceCenter[2] + deltaY * 0.1; // 比例因子可以调整
sliceCenter[2] = newSlicePosition;
// 设置新的切片位置
this->ImageReslice->SetResliceAxesOrigin(sliceCenter);
interactor->Render(); // 渲染更新后的图像
}
vtkImageReslice* ImageReslice;
int Slicing;
};
void initImageActor(double* Matrix, double* center, vtkSmartPointer<vtkImageCast> pImageCast,
vtkSmartPointer<vtkImageReslice> imageReslice, vtkSmartPointer<vtkImageActor> actor)
{
vtkSmartPointer<vtkMatrix4x4> AxialResliceMatrix = vtkSmartPointer<vtkMatrix4x4>::New();
AxialResliceMatrix->DeepCopy(Matrix);
AxialResliceMatrix->SetElement(0, 3, center[0]);
AxialResliceMatrix->SetElement(1, 3, center[1]);
AxialResliceMatrix->SetElement(2, 3, center[2]);
imageReslice->SetInputConnection(pImageCast->GetOutputPort());
imageReslice->SetOutputDimensionality(2);
imageReslice->SetResliceAxes(AxialResliceMatrix);
imageReslice->SetInterpolationModeToLinear();
imageReslice->Update();
actor->GetMapper()->SetInputConnection(imageReslice->GetOutputPort());
actor->SetPosition(0, 0, 0);
}
void addImageInteractionCallback(vtkRenderWindowInteractor* interactor, vtkImageReslice* imageReslice)
{
vtkSmartPointer<vtkImageInteractionCallback> callback = vtkSmartPointer<vtkImageInteractionCallback>::New();
callback->SetImageReslice(imageReslice);
vtkSmartPointer<vtkInteractorStyleImage> imagestyle = vtkSmartPointer<vtkInteractorStyleImage>::New();
interactor->SetInteractorStyle(imagestyle);
imagestyle->AddObserver(vtkCommand::MouseMoveEvent, callback);
imagestyle->AddObserver(vtkCommand::LeftButtonPressEvent, callback);
imagestyle->AddObserver(vtkCommand::LeftButtonReleaseEvent, callback);
}
int main()
{
vtkSmartPointer<vtkImageReslice> pImageResliceX = vtkSmartPointer<vtkImageReslice>::New();
vtkSmartPointer<vtkImageReslice> pImageResliceY = vtkSmartPointer<vtkImageReslice>::New();
vtkSmartPointer<vtkImageReslice> pImageResliceZ = vtkSmartPointer<vtkImageReslice>::New();
vtkSmartPointer<vtkDICOMImageReader> reader = vtkSmartPointer<vtkDICOMImageReader>::New();
reader->SetDirectoryName("D:\\image\\images\\011\\CT\\20200115\\67728\\1.3.46.670589.33.1.63714685715192329600004.5577472948825480582");
reader->Update();
int extent[6];
double spacing[3];
double origin[3];
reader->GetOutput()->GetExtent(extent);
reader->GetOutput()->GetSpacing(spacing);
reader->GetOutput()->GetOrigin(origin);
double center[3];
center[0] = origin[0] + spacing[0] * 0.5 * (extent[0] + extent[1]);
center[1] = origin[1] + spacing[1] * 0.5 * (extent[2] + extent[3]);
center[2] = origin[2] + spacing[2] * 0.5 * (extent[4] + extent[5]);
double Axial[16] = {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1 };
double Coronal[16] = {
1, 0, 0, 0,
0, 0, -1, 0,
0, 1, 0, 0,
0, 0, 0, 1 };
double Sagittal[16] = {
0, 0, 1, 0,
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 0, 1 };
vtkSmartPointer<vtkImageCast> pImageCast = vtkSmartPointer<vtkImageCast>::New();
pImageCast->SetInputConnection(reader->GetOutputPort());
pImageCast->SetOutputScalarTypeToUnsignedChar();
pImageCast->ClampOverflowOn();
pImageCast->Update();
vtkSmartPointer<vtkImageActor> pImageActorX = vtkSmartPointer<vtkImageActor>::New();
vtkSmartPointer<vtkImageActor> pImageActorY = vtkSmartPointer<vtkImageActor>::New();
vtkSmartPointer<vtkImageActor> pImageActorZ = vtkSmartPointer<vtkImageActor>::New();
initImageActor(Axial, center, pImageCast, pImageResliceX, pImageActorX);
initImageActor(Coronal, center, pImageCast, pImageResliceY, pImageActorY);
initImageActor(Sagittal, center, pImageCast, pImageResliceZ, pImageActorZ);
vtkSmartPointer<vtkRenderer> pRendererX = vtkSmartPointer<vtkRenderer>::New();
vtkSmartPointer<vtkRenderer> pRendererY = vtkSmartPointer<vtkRenderer>::New();
vtkSmartPointer<vtkRenderer> pRendererZ = vtkSmartPointer<vtkRenderer>::New();
vtkSmartPointer<vtkRenderer> pRenderer = vtkSmartPointer<vtkRenderer>::New();
vtkSmartPointer<vtkRenderWindow> pRenderWindow = vtkSmartPointer<vtkRenderWindow>::New();
pRendererX->AddActor(pImageActorX);
pRendererY->AddActor(pImageActorY);
pRendererZ->AddActor(pImageActorZ);
// 设置渲染器背景颜色
pRendererX->SetBackground(0, 0, 0);
pRendererY->SetBackground(0, 0, 0);
pRendererZ->SetBackground(0, 0, 0);
pRenderer->SetBackground(0.1, 0.2, 0.4);
// 为每个渲染器设置视口
double ltView[4] = { 0, 0, 0.5, 0.5 };
double rtView[4] = { 0.5, 0, 1, 0.5 };
double lbView[4] = { 0, 0.5, 0.5, 1 };
double rbView[4] = { 0.5, 0.5, 1, 1 };
pRenderer->SetViewport(rtView);
pRendererX->SetViewport(lbView);
pRendererY->SetViewport(rbView);
pRendererZ->SetViewport(ltView);
pRenderWindow->AddRenderer(pRendererX);
pRenderWindow->AddRenderer(pRendererY);
pRenderWindow->AddRenderer(pRendererZ);
pRenderWindow->AddRenderer(pRenderer);
// 设置体积渲染
vtkSmartPointer<vtkPiecewiseFunction> volumeScalarOpacity = vtkSmartPointer<vtkPiecewiseFunction>::New();
volumeScalarOpacity->AddPoint(0, 0.0);
volumeScalarOpacity->AddPoint(80, 0.0);
volumeScalarOpacity->AddPoint(400, 1.0);
vtkSmartPointer<vtkColorTransferFunction> volumeColor = vtkSmartPointer<vtkColorTransferFunction>::New();
volumeColor->AddRGBPoint(0.0, 0.0, 0.0, 0.0);
volumeColor->AddRGBPoint(80.0, 1.0, 1.0, 1.0);
volumeColor->AddRGBPoint(400.0, 1.0, 1.0, 1.0);
vtkSmartPointer<vtkVolumeProperty> volumeProperty = vtkSmartPointer<vtkVolumeProperty>::New();
volumeProperty->SetColor(volumeColor);
volumeProperty->SetScalarOpacity(volumeScalarOpacity);
volumeProperty->ShadeOn();
volumeProperty->SetInterpolationTypeToLinear();
vtkSmartPointer<vtkGPUVolumeRayCastMapper> volumeMapper = vtkSmartPointer<vtkGPUVolumeRayCastMapper>::New();
volumeMapper->SetInputConnection(reader->GetOutputPort());
vtkSmartPointer<vtkVolume> volume = vtkSmartPointer<vtkVolume>::New();
volume->SetMapper(volumeMapper);
volume->SetProperty(volumeProperty);
pRenderer->AddVolume(volume);
vtkSmartPointer<vtkRenderWindowInteractor> pRenderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
pRenderWindow->SetInteractor(pRenderWindowInteractor);
pRenderWindow->SetSize(800, 800);
// 为横断面视窗添加交互回调
vtkSmartPointer<vtkRenderWindowInteractor> interactorX = vtkSmartPointer<vtkRenderWindowInteractor>::New();
interactorX->SetRenderWindow(pRenderWindow);
addImageInteractionCallback(interactorX, pImageResliceX);
vtkSmartPointer<vtkRenderWindowInteractor> interactorY = vtkSmartPointer<vtkRenderWindowInteractor>::New();
interactorY->SetRenderWindow(pRenderWindow);
addImageInteractionCallback(interactorY, pImageResliceY);
vtkSmartPointer<vtkRenderWindowInteractor> interactorZ = vtkSmartPointer<vtkRenderWindowInteractor>::New();
interactorZ->SetRenderWindow(pRenderWindow);
addImageInteractionCallback(interactorZ, pImageResliceZ);
// 为体绘制窗口添加交互回调
vtkSmartPointer<vtkRenderWindowInteractor> interactorVolume = vtkSmartPointer<vtkRenderWindowInteractor>::New();
interactorVolume->SetRenderWindow(pRenderWindow);
vtkSmartPointer<vtkInteractorStyleTrackballCamera> volumeStyle = vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New();
interactorVolume->SetInteractorStyle(volumeStyle);
pRenderWindow->Render();
pRenderWindowInteractor->Initialize();
pRenderWindowInteractor->Start();
return 0;
}
运行效果:
待后期进行优化和完善。
总结
本文详细介绍了如何使用 VTK 9.3.0 在 Visual Studio 2017 中进行体绘制的实现过程。通过定义交互回调类、初始化图像 Actor、添加交互回调,并在主函数中设置多视口显示和体绘制,你可以实现一个功能完备的医学图像体绘制 Demo。如果在实现过程中遇到问题,可以参考本文提供的代码和步骤,希望对你有所帮助。