前段时间对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;
}