前段时间对VTK9.3.0进行了编译,开发了MPR+VR实现的demo,显示效果不是很理想,正好趁着周末有时间,再度对之前的程序进行优化和完善,先展示下效果:
VTK实现MPR+VR四视图
再次讲解下基于VTK的MPR+VR实现的简单项目创建过程:
1、在vtk官网https://vtk.org/download/下载vtk库,我下载的是9.3.0版本,如下:
2、下载后解压,用Cmake进行编译,具体编译过程我就不详细说明了.
3、我选择的是Visual Studio 2022 64位开发工具,Cmake编译完成后就生成了VTK.sln解决方案:
编译生成即可,编译过程遇到的问题在我其他几篇博客里已经做了记录,需要可以查看。
4、右键VTK中的“INSTALL”生成VTK的库目录和包含目录,如下:
5、右键VTK项目解决方案,选择 添加新项目,再选择 c++控制台项目即可
6、在新添加的MPR demo项目中添加包含目录、库目录和依赖项,如下:
到此我们已经创建了一个基于VTK9.3.0+Visual Studio的C++控制台项目,在生成的cpp源文件中就可以编写具体的DICOM影像MPR多平面重建+V R体绘制的代码了。
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;
}