基于VTK9.3.0+Visual Studio2017 c++实现DICOM影像MPR多平面重建+V R体绘制4个视图展示功能的实现

开源库:VTK9.3.0

开发工具:Visual Studio2017

开发语言:C++

实现过程:

cpp 复制代码
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;
}

运行效果:

待后期进行优化和完善。

相关推荐
CE贝多芬12 分钟前
用C/C++写一个客户端和服务器之间进行TCP通信的DEMO
开发语言·c++·tcp/ip
零号全栈寒江独钓1 小时前
QT基本对话框(基本对话框、工具盒类、进度条、调色板与电子钟、可扩展对话框、程序启动画面)
c++·qt·visual studio
风景依旧在1 小时前
C++ tcp中的可变长度结构体的序列化和反序列化
c++·windows·tcp/ip
每天努力进步!1 小时前
LeetCode热题100刷题1:1.两数之和、49. 字母异位词分组、128. 最长连续序列
c++·算法·leetcode·哈希表
时光32 小时前
函数参数里的&怎么判断是引用还是取地址的研究
c++
爱C++的小笨2 小时前
C++ STL: std::vector与std::array的深入对比
c++
人间花木2 小时前
快速排序(C/C++实现)—— 简单易懂系列
c语言·c++·算法·快速排序·入门教学
零二年的冬2 小时前
常见的行为型设计模式
开发语言·c++·设计模式
qq_455760852 小时前
visual studio 2022配置和使用jsoncpp
ide·visual studio
俺不是文盲2 小时前
C++笔记:实现一个字符串类(构造函数、拷贝构造函数、拷贝赋值函数)
开发语言·c++·笔记