VTK笔记-切面重建-使用交互器更新断层图的奇异现象的问题排查

问题

  VTK图像处理交流群(962611958)里有朋友提了一个问题:使用交互器操作轴面图像的中心点时,出现图像随着鼠标移动;大家都认为平移时出现图像的平移是不可能的,因为在使用vtkImageReslice取图时,如果不指定OutputOrigin坐标位置,是默认以体数据的中心点在切面上的投影为中心点的图像,在同一个断层上移动,中心点是不会发生变化的,图像也就是同一张图。这个问题很有意思;
  我这段时间也在做和切面重建相关的内容,跟着群友的代码来查这个问题;
  参考群友的代码写的复现代码,每次平移只考虑了鼠标的XY的改变量,没有结合XY方向的spacing的值,这个地方暂时忽略,主要关注在鼠标移动事件MouseMoveEvent,代码如下:

#pragma once

#include "vtk_include.h"
#include <vtkMetaImageReader.h>
#include <vtkImageActor.h>

class vtkImageInteractionCallback : public vtkCommand {
public:
	static vtkImageInteractionCallback* New() {
		return new vtkImageInteractionCallback;
	}
	vtkImageInteractionCallback() {
		this->Slicing = 0;
		this->ImageReslice = 0;
		this->Interactor = 0;
	}
	void SetImageReslice(vtkImageReslice* reslice) {
		this->ImageReslice = reslice;
	}
	void SetImageMapToColors(vtkImageMapToColors* mapToColors) {
		this->mapToColors = mapToColors;
	}
	void SetImageActor(vtkImageActor* actor) {
		myActor = actor;
	}

	void SetInteractor(vtkRenderWindowInteractor* interactor) {
		this->Interactor = interactor;
	}
	vtkRenderWindowInteractor* GetInteractor() {
		return this->Interactor;
	}
	virtual void Execute(vtkObject*, unsigned long event, void*) {
		vtkRenderWindowInteractor* interactor = this->GetInteractor();
		int lastPos[2];
		interactor->GetLastEventPosition(lastPos);
		int currPos[2];
		interactor->GetEventPosition(currPos);

		if (event == vtkCommand::LeftButtonPressEvent) {
			this->Slicing = 1;
		}
		else if (event == vtkCommand::LeftButtonReleaseEvent) {
			this->Slicing = 0;
		}
		else if (event == vtkCommand::MouseMoveEvent) {
			if (this->Slicing)
			{
				int deltaX = lastPos[0] - currPos[0];
				int deltaY = lastPos[1] - currPos[1];
				double translateC[16] = {
					1,0,0,deltaX,
					0,1,0,deltaY,
					0,0,1,0,
					0,0,0,1 };
				vtkImageReslice* reslice = this->ImageReslice;
				reslice->Update();
				vtkMatrix4x4* matrix = reslice->GetResliceAxes();
				vtkMatrix4x4* translateM = vtkMatrix4x4::New();
				vtkMatrix4x4* resultM = vtkMatrix4x4::New();
				translateM->DeepCopy(translateC);
				vtkMatrix4x4::Multiply4x4(matrix, translateM, resultM);
				matrix->DeepCopy(resultM);
				reslice->SetResliceAxes(matrix);
				reslice->Update();
				translateM->Delete();
				resultM->Delete();
				mapToColors->Update();
				interactor->Render();
			}
			else {
				vtkInteractorStyle* style = vtkInteractorStyle::SafeDownCast(interactor->GetInteractorStyle());
				if (style) {
					style->OnMouseMove();
				}
			}
		}
	}
private:
	int Slicing;
	vtkImageReslice* ImageReslice;
	vtkRenderWindowInteractor* Interactor;
	vtkImageMapToColors* mapToColors;
	vtkImageActor* myActor;
};

class Test_Reslice
{
public:
	Test_Reslice() {
		reader = vtkSmartPointer<vtkMetaImageReader>::New();
		reader->SetFileName("G:\\Data\\brain.mhd");
		reader->Update();	

		reader->GetOutput()->GetExtent(extent);
		reader->GetOutput()->GetSpacing(spacing);
		reader->GetOutput()->GetOrigin(origin);
		
		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]);
	}
	
	void Render()
	{
		this->renwin = vtkRenderWindow::New();
		this->iren = vtkRenderWindowInteractor::New();
		int extent[6];
		double spacing[3];
		double origin[3];
		double center[3];
		this->Getimagedata()->GetExtent(extent);
		this->Getimagedata()->GetSpacing(spacing);
		this->Getimagedata()->GetOrigin(origin);
		center[0] = origin[0] + 0.5 * spacing[0] * (extent[0] + extent[1]);
		center[1] = origin[1] + 0.5 * spacing[1] * (extent[2] + extent[3]);
		center[2] = origin[2] + 0.5 * spacing[2] * (extent[4] + extent[5]);

		double Axial[16] = {
			1, 0, 0, 0,
			0, 1, 0, 0,
			0, 0, 1, 0,
			0, 0, 0, 1
		};

		vtkMatrix4x4 *AxialResliceMatrix = vtkMatrix4x4::New();
		AxialResliceMatrix->DeepCopy(Axial);
		AxialResliceMatrix->SetElement(0, 3, center[0]);
		AxialResliceMatrix->SetElement(1, 3, center[1]);
		AxialResliceMatrix->SetElement(2, 3, center[2]);
		vtkImageReslice* myImageReslice = this->ImageReslice = vtkImageReslice::New();
		//设置体数据来源
		myImageReslice->SetInputData(this->Getimagedata());
		myImageReslice->SetResliceAxes(AxialResliceMatrix);
		myImageReslice->SetOutputDimensionality(2);
		myImageReslice->SetInterpolationModeToLinear();
		vtkLookupTable* myLookupTable = vtkLookupTable::New();
		myLookupTable->SetRange(0, 2000);
		myLookupTable->SetValueRange(0.0, 1.0);
		myLookupTable->SetSaturationRange(0.0, 0.0);
		myLookupTable->SetRampToLinear();
		myLookupTable->Build();
		vtkImageMapToColors* myMapToColors = this->ColorMap = vtkImageMapToColors::New();
		myMapToColors->SetLookupTable(myLookupTable);
		myMapToColors->SetInputConnection(myImageReslice->GetOutputPort());
		myMapToColors->Update();
		vtkImageActor* myImageActor = vtkImageActor::New();
		myImageActor->SetInputData(myMapToColors->GetOutput());
		
		this->ren = vtkRenderer::New();
		this->ren->AddActor(myImageActor);
		
		this->renwin->AddRenderer(this->ren);
		vtkSmartPointer<vtkInteractorStyleImage> imagestyle = vtkSmartPointer<vtkInteractorStyleImage>::New();
		this->iren->SetInteractorStyle(imagestyle);
		iren->SetRenderWindow(this->renwin);
		iren->Initialize();
		vtkSmartPointer<vtkImageInteractionCallback> callback = vtkSmartPointer<vtkImageInteractionCallback>::New();
		callback->SetImageReslice(ImageReslice);
		callback->SetInteractor(this->iren);
		callback->SetImageMapToColors(ColorMap);
		callback->SetImageActor(myImageActor);
		imagestyle->AddObserver(vtkCommand::MouseMoveEvent, callback);
		imagestyle->AddObserver(vtkCommand::LeftButtonPressEvent, callback);
		imagestyle->AddObserver(vtkCommand::LeftButtonReleaseEvent, callback);		
		this->iren->Start();
	}
	vtkImageData* Getimagedata() { return reader->GetOutput(); }
	vtkSmartPointer<vtkMetaImageReader> reader;
	int extent[6];
	double spacing[3];
	double origin[3];
	double center[3];
	vtkRenderer* ren;
	vtkRenderWindow* renwin;
	vtkRenderWindowInteractor* iren;
	vtkImageReslice* ImageReslice;
	vtkImageMapToColors*  ColorMap;
};

  复现结果是,左图为原始图像,右图为鼠标移动改变取图中心点后的图像。
  可以发现图像向左下方向移动了,和鼠标的移动方向是一致的;
在这里插入图片描述在这里插入图片描述

问题定位过程

  1.拿到问题代码后,确实可以复现,其实问题百分百是可以解决的,最怕的就是不能复现和偶发的现象;
  2.之前也说过我一直认为图像是不变的,那么要么是我的认知是错误的,要么是图像是对的渲染后在窗口中的展示结果是错误的;
  3.验证从vtkImageReslice中获取的图像是否正确。这里犯了一个错误,我用vtkImageViewer2来展示我从vtkImageReslice中获取的图像,发现图像确实有偏移;难道是我理解的真有问题…… ,记录了当前右图对应的坐标,在代码中我初始时对vtkImageReslice设置这个坐标,发现图像居中,没有偏移,这个和刚才那个vtkImageViewer2显示的结果相悖;这个时候大概感觉到是vtkImageViewer2和vtkImageActor的坑了,因为图像是从vtkImageData放到vtkImageActor中然后在窗口渲染显示;
  4.步骤3验证了图像没有问题,那么只能是渲染的问题,从vtkImageActor提供的接口来看,vtkImageActor内确实有自己的坐标,使用vtkImageActor的GetCenter方法获取坐标位置;

cout << "++++++++++++++++++x:" << myActor->GetCenter()[0] << endl;
cout << "++++++++++++++++++y:" << myActor->GetCenter()[1] << endl;

  随着鼠标的移动,Center坐标确实发生了改变;
在这里插入图片描述

  我使用SetOrigin和SetPosition妄想将vtkImageActor的坐标设置为(0,0),发现没有效果,图像照旧向鼠标移动方向平移;

double zero_center[3] = { 0 };
myActor->SetOrigin(zero_center);
myActor->SetPosition(zero_center);

  从VTK的源码中,可以得知vtkImageActor的Center是由Bounds计算的:

double *vtkProp3D::GetCenter()
{
  this->GetBounds();
  this->Center[0] = (this->Bounds[1] + this->Bounds[0])/2.0;
  this->Center[1] = (this->Bounds[3] + this->Bounds[2])/2.0;
  this->Center[2] = (this->Bounds[5] + this->Bounds[4])/2.0;
  return this->Center;
}

  打印vtkImageActor的Bounds,从上图上看Bounds是在改变的;

double bounds[6] = { 0 };
myActor->GetBounds(bounds);

结论

  可以定位是vtkImageActor和vtkImageViewer2的问题,群友给出的解决办法是使用vtkRenderer的ResetCamera方法,根据图像的坐标,重新计算相机的坐标。不过实现项目中很少会遇到这种问题,因为项目中一般不会使用交互器,也很少使用vtkImageActor的(这里仅限于图像切面重建,不包括体渲染)。
  注:多看VTK的源码,还是极其必要的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黑山老妖的笔记本

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值