该项目是自己在学习VTK时尝试显示dicom,自己对vtk接口理解不是很透彻,目前只能做到显示图像和鼠标滚轮切换层,后期如果有项目锻炼,加深理解后再修改,本文使用的是QVTKOpenGLNativeWidget控件进行显示图像,只列举主要的代码,其他的部分需要自己根据自己的工程情况进行添加界面
#pragma once
#include <QtWidgets/QWidget>
#include
#include “QVTKOpenGLNativeWidget.h”
#include “CommonData.h”
#include “ui_VtkOpenGLWidget.h”
#include “ImageStyleInteractor.h”
class VtkOpenGLWidget : public QVTKOpenGLNativeWidget
{
Q_OBJECT
public:
VtkOpenGLWidget(QWidget* parent = nullptr);
~VtkOpenGLWidget();
void setImageData(vtkSmartPointer<vtkImageData> pImageData);
void setCurrentViewType(View_Type type);
void setName(const char* name);
void setUID(const char* uid);
void setStudyID(const char* studyID);
private:
void show3DImage(/vtkSmartPointer pImageData/);
void initView();
void showMPRImage();
void setWindowLevel(vtkSmartPointer<vtkImageMapToWindowLevelColors>& colors, int min, int max);
void showImageInfo();
void showSlice();
private:
Ui::VtkOpenGLWidgetClass ui;
View_Type m_trViewType;
vtkSmartPointer<vtkOpenGLRenderer> m_pRenderer;
vtkSmartPointer<vtkGenericOpenGLRenderWindow> m_pRenderWindow;
vtkSmartPointer<vtkImageReslice> m_pImageReslice;
vtkSmartPointer<vtkImageData> m_pImageData;
vtkSmartPointer<AxialImageStyle> m_pImageInteractor;
vtkSmartPointer<vtkActor2D> m_pSliceActor;
double m_dAxial[16] = { 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1 };
double m_dCornal[16] = { 1, 0, 0, 0,
0, 0, -1, 0,
0, 1, 0, 0,
0, 0, 0, 1 };
double m_dSagittal[16] = { 0, 0, 1,0,
1, 0, 0,0,
0, 1, 0,0,
0, 0, 0,1 };
vtkSmartPointer<vtkMatrix4x4> m_matAxial;
vtkSmartPointer<vtkMatrix4x4> m_matCornal;
vtkSmartPointer<vtkMatrix4x4> m_matSigittal;
char* m_pName;
char* m_pUID;
char* m_pStudyID;
std::function<void()> m_pCallBack;
};
#include “VtkOpenGLWidget.h”
#include “ImageStyleInteractor.h”
#include
#include
-
VtkOpenGLWidget::VtkOpenGLWidget( QWidget *parent)
-
QVTKOpenGLNativeWidget(parent)
, m_pImageData(nullptr)
, m_pRenderer(nullptr)
, m_pSliceActor(nullptr)
{
ui.setupUi(this);
this->initView();
}
VtkOpenGLWidget::~VtkOpenGLWidget()
{
}
void VtkOpenGLWidget::setCurrentViewType(View_Type type)
{
m_trViewType = type;
}
void VtkOpenGLWidget::initView()
{
/vtkOpenGLRenderWindow::SetGlobalMaximumNumberOfMultiSamples(8);
QSurfaceFormat::setDefaultFormat(QVTKOpenGLWidget::defaultFormat());/
m_pRenderWindow = vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New();
this->SetRenderWindow(m_pRenderWindow);
if (m_pRenderer == nullptr)
m_pRenderer = vtkSmartPointer<vtkOpenGLRenderer>::New();
if (m_pSliceActor == nullptr)
m_pSliceActor = vtkSmartPointer<vtkActor2D>::New();
m_pCallBack = std::bind(&VtkOpenGLWidget::showSlice, this);
}
void VtkOpenGLWidget::setImageData(vtkSmartPointer pImageData)
{
if (pImageData == nullptr) return;
if(m_pRenderer)
m_pRenderer->RemoveAllViewProps();
m_pImageData = vtkSmartPointer::New();
m_pImageData->DeepCopy(pImageData);
// m_pImageData = pImageData;
int dims[3];
m_pImageData->GetDimensions(dims);
if (m_trViewType == Image3D)
this->show3DImage();
else
this->showMPRImage();
}
void VtkOpenGLWidget::show3DImage(/vtkSmartPointer pImageData/)
{
vtkSmartPointer pInteractor = vtkSmartPointer::New();
pInteractor->SetRenderWindow(m_pRenderWindow);
vtkSmartPointer pImageInteractor = vtkSmartPointer::New();
pInteractor->SetInteractorStyle(pImageInteractor);
pInteractor->Initialize();
vtkSmartPointer<vtkOpenGLGPUVolumeRayCastMapper> pVolumeMapper = vtkSmartPointer < vtkOpenGLGPUVolumeRayCastMapper>::New();
pVolumeMapper->SetInputData(m_pImageData);
vtkSmartPointer<vtkVolumeProperty> pVolumeProperty = vtkSmartPointer<vtkVolumeProperty>::New();
pVolumeProperty->SetInterpolationTypeToLinear();
pVolumeProperty->ShadeOn();
pVolumeProperty->SetAmbient(0.2);//环境温度系数
pVolumeProperty->SetDiffuse(0.7);//漫反射系数
pVolumeProperty->SetSpecular(0.5);//镜面反射系数
vtkSmartPointer<vtkPiecewiseFunction> pFunction = vtkSmartPointer<vtkPiecewiseFunction>::New();
pFunction->AddPoint(-3024, 0, 0.5, 0.0);
pFunction->AddPoint(-16.0,0,0.49, 0.61);
pFunction->AddPoint(641,0.72,0.5, 0.0);
pFunction->AddPoint(3071,0.71,0.5, 0.0);
pVolumeProperty->SetScalarOpacity(pFunction);
vtkSmartPointer<vtkPiecewiseFunction> pGridFunction = vtkSmartPointer<vtkPiecewiseFunction>::New();
pGridFunction->AddPoint(10, 0.0);
pGridFunction->AddPoint(90, 0.5);
pGridFunction->AddPoint(100, 1.0);
pVolumeProperty->SetGradientOpacity(pGridFunction);
vtkSmartPointer<vtkColorTransferFunction> pColorTransferFunction = vtkSmartPointer<vtkColorTransferFunction>::New();
pColorTransferFunction->AddRGBPoint(-3024, 0, 0, 0, 0.5, 0.0);
pColorTransferFunction->AddRGBPoint(-16, 0.73, 0.25, 0.30, 0.49, .61);
pColorTransferFunction->AddRGBPoint(641, .90, .82, .56, .5, 0.0);
pColorTransferFunction->AddRGBPoint(3071, 1, 1, 1, .5, 0.0);
pVolumeProperty->SetColor(pColorTransferFunction);
vtkSmartPointer<vtkVolume> p3DVolume = vtkSmartPointer<vtkVolume>::New();
p3DVolume->SetMapper(pVolumeMapper);
p3DVolume->SetProperty(pVolumeProperty);
if (m_pRenderer == nullptr)
m_pRenderer = vtkSmartPointer<vtkOpenGLRenderer>::New();
m_pRenderWindow->AddRenderer(m_pRenderer);
m_pRenderer->AddVolume(p3DVolume);
m_pRenderer->GetActiveCamera()->SetPosition(0, 1, 0);
m_pRenderer->GetActiveCamera()->SetFocalPoint(0, 0, 0);
m_pRenderer->GetActiveCamera()->SetViewUp(0, 0, 1);
m_pRenderer->GetActiveCamera()->Zoom(0.8);
m_pRenderer->ResetCamera();
m_pRenderWindow->Render();
}
void VtkOpenGLWidget::showMPRImage()
{
if (m_pImageReslice == nullptr)
m_pImageReslice = vtkSmartPointer::New();
vtkSmartPointer<vtkRenderWindowInteractor> pInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
pInteractor->SetRenderWindow(m_pRenderWindow);
m_pImageInteractor = vtkSmartPointer<AxialImageStyle>::New();
m_pImageInteractor->setViewType(m_trViewType);
m_pImageInteractor->setMouseWheelCallBack(m_pCallBack);
pInteractor->SetInteractorStyle(m_pImageInteractor);
pInteractor->Initialize();
{
double* spacing = m_pImageData->GetSpacing();
int* extent = m_pImageData->GetExtent();
double* origin = m_pImageData->GetOrigin();
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]);
m_pImageReslice->SetInputData(m_pImageData);
if (m_trViewType == ImageAxial)
{
m_matAxial = vtkSmartPointer<vtkMatrix4x4>::New();
m_matAxial->DeepCopy(m_dAxial);
m_matAxial->SetElement(0, 3, center[0]);
m_matAxial->SetElement(1, 3, center[1]);
m_matAxial->SetElement(2, 3, center[2]);
m_pImageReslice->SetResliceAxes(m_matAxial);
m_pImageReslice->SetOutputDimensionality(3);
m_pImageReslice->SetInterpolationModeToLinear();
m_pImageReslice->Update();
}
else if (m_trViewType == ImageCoronal)
{
m_matCornal = vtkSmartPointer<vtkMatrix4x4>::New();
m_matCornal->DeepCopy(m_dCornal);
m_matCornal->SetElement(0, 3, center[0]);
m_matCornal->SetElement(1, 3, center[1]);
m_matCornal->SetElement(2, 3, center[2]);
m_pImageReslice->SetResliceAxes(m_matCornal);
m_pImageReslice->SetOutputDimensionality(3);
m_pImageReslice->SetInterpolationModeToLinear();
m_pImageReslice->Update();
}
else if (m_trViewType == ImageSagittal)
{
m_matSigittal = vtkSmartPointer<vtkMatrix4x4>::New();
m_matSigittal->DeepCopy(m_dSagittal);
m_matSigittal->SetElement(0, 3, center[0]);
m_matSigittal->SetElement(1, 3, center[1]);
m_matSigittal->SetElement(2, 3, center[2]);
m_pImageReslice->SetResliceAxes(m_matSigittal);
}
m_pImageReslice->SetOutputDimensionality(3);
m_pImageReslice->SetInterpolationModeToLinear();
m_pImageReslice->Update();
vtkSmartPointer<vtkLookupTable> myLookupTable = vtkSmartPointer<vtkLookupTable>::New();
myLookupTable->SetRange(0, 1000);
myLookupTable->SetValueRange(0.0, 1.0);
myLookupTable->SetSaturationRange(0.0, 0.0);
myLookupTable->SetRampToLinear();
myLookupTable->Build();
vtkSmartPointer<vtkImageMapToWindowLevelColors> myMapToColors = vtkSmartPointer < vtkImageMapToWindowLevelColors > ::New();
myMapToColors->SetLookupTable(myLookupTable);
myMapToColors->SetInputConnection(m_pImageReslice->GetOutputPort());
m_pImageInteractor->setViewImageMapColor(myMapToColors);
auto al = myMapToColors->GetInputAlgorithm();
al->UpdateInformation();
int* temp = al->GetOutputInformation(0)->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT());
vtkSmartPointer<vtkImageActor> pImageActor = vtkSmartPointer<vtkImageActor>::New();
pImageActor->GetMapper()->SetInputConnection(myMapToColors->GetOutputPort());
//pImageActor->SetInputData(myMapToColors->GetOutput());
m_pImageInteractor->setViewActor(pImageActor);
int currentSlice;
m_pImageInteractor->getCurrentSlice(currentSlice);
if (this->m_trViewType == ImageCoronal)//ImageCoronal
{
pImageActor->SetDisplayExtent(extent[0], extent[1], extent[2], extent[3], currentSlice, currentSlice);
this->setWindowLevel(myMapToColors, extent[2], extent[3]);
}
else if (this->m_trViewType == ImageAxial)//ImageAxial
{
pImageActor->SetDisplayExtent(extent[0], extent[1], currentSlice, currentSlice, extent[4], extent[5]);
this->setWindowLevel(myMapToColors, extent[4], extent[5]);
}
else if (this->m_trViewType == ImageSagittal)
{
pImageActor->SetDisplayExtent(currentSlice, currentSlice, extent[2], extent[3], extent[4], extent[5]);
this->setWindowLevel(myMapToColors, extent[0], extent[1]);
}
pImageActor->ForceOpaqueOn();
m_pRenderer->AddActor(pImageActor);
m_pRenderWindow->AddRenderer(m_pRenderer);
m_pRenderer->GetActiveCamera()->SetFocalPoint(0, 0, 0);
if (m_trViewType == ImageAxial)//xz
{
m_pRenderer->GetActiveCamera()->SetPosition(0, -1, 0);
m_pRenderer->GetActiveCamera()->SetViewUp(0, 0, 1);
}
else if (m_trViewType == ImageCoronal)//xy
{
m_pRenderer->GetActiveCamera()->SetPosition(0, 0, 1);
m_pRenderer->GetActiveCamera()->SetViewUp(0, 1, 0);
}
else if (m_trViewType == ImageSagittal)//yz
{
m_pRenderer->GetActiveCamera()->SetPosition(1, 0, 0);
m_pRenderer->GetActiveCamera()->SetViewUp(0, 0, 1);
m_pRenderer->GetActiveCamera()->SetClippingRange(0.1, 1000);
}
m_pRenderer->GetActiveCamera()->OrthogonalizeViewUp();
m_pRenderer->ResetCamera();
showImageInfo();
//showSlice();
m_pRenderWindow->Render();
m_pImageInteractor->setRender(m_pRenderer);
}
}
void VtkOpenGLWidget::setName(const char* name)
{
if (name == nullptr) return;
m_pName = (char*)malloc(128 * sizeof(char));
memcpy(m_pName, name, 127);
m_pName[128] = ‘\0’;
}
void VtkOpenGLWidget::setUID(const char* uid)
{
if (uid == nullptr)return;
int s = sizeof(char);
m_pUID = (char*)malloc(256 * sizeof(char));
memcpy(m_pUID, uid, 255);
m_pUID[255] = ‘\0’;
}
void VtkOpenGLWidget::setStudyID(const char* studyID)
{
if (studyID == nullptr)return;
m_pStudyID = (char*)malloc(256 * sizeof(char));
memcpy(m_pStudyID, studyID, 255);
m_pStudyID[256] = ‘\0’;
}
void VtkOpenGLWidget::setWindowLevel(vtkSmartPointer& colors, int min, int max)
{
colors->SetWindow(max - min);
colors->SetLevel(0.5 * (min + max));
}
void VtkOpenGLWidget::showImageInfo()
{
std::stringstream stream;
stream << “name:” << m_pName<< “\n” << “uid:” << m_pUID<<“\n” << “studyID:” << m_pStudyID;
/*vtkUnicodeString pUnicodeString = vtkUnicodeString::from_utf8(stream.str().c_str());
auto str = pUnicodeString.utf8_str();*/
vtkSmartPointer<vtkTextProperty> pProperty = vtkSmartPointer<vtkTextProperty>::New();
pProperty->SetFontFamilyToCourier();
pProperty->SetFontSize(12);
pProperty->SetJustificationToLeft();
pProperty->SetVerticalJustificationToBottom();
vtkSmartPointer<vtkTextMapper> pMapper = vtkSmartPointer<vtkTextMapper>::New();
pMapper->SetInput(stream.str().c_str());
pMapper->SetTextProperty(pProperty);
vtkSmartPointer<vtkActor2D> pTextActor = vtkSmartPointer<vtkActor2D>::New();
pTextActor->SetMapper(pMapper);
m_pRenderer->AddActor2D(pTextActor);
}
下面是vtkInteractorStyleImage子类,主要用于鼠标交互
class AxialImageStyle : public vtkInteractorStyleImage
{
public:
static AxialImageStyle* New();
vtkTypeMacro(AxialImageStyle, vtkInteractorStyleImage)
void setImageSlice(vtkSmartPointer pImageSlice);
void setViewType(View_Type type);
void setViewActor(vtkSmartPointer<vtkImageActor> pActor);
void setViewImageMapColor(vtkSmartPointer<vtkImageMapToWindowLevelColors> pWindowColor);
int* getImageRange();
void getCurrentSlice(int& slice);
void setRender(vtkSmartPointer<vtkOpenGLRenderer> pRender);
void setMouseWheelCallBack(std::function<void()> pCallBack);
void getSliceRange(int& min, int& max);
protected:
void OnMouseWheelForward()override;
void OnMouseWheelBackward()override;
private:
explicit AxialImageStyle();
void showSlice();
void updateDisplayExtent();
std::array<int, 2> sliceMax();
private:
vtkSmartPointer m_pCurrSlice;
View_Type m_viewType;
vtkSmartPointer<vtkImageActor> m_pActor;
vtkSmartPointer<vtkImageMapToWindowLevelColors> m_pMapColors;
vtkSmartPointer<vtkOpenGLRenderer> m_pRenderer;
vtkSmartPointer<vtkTextActor> m_pSliceActor;
int m_iSlice;
std::function<void()> m_pCallBack;
};
AxialImageStyle* AxialImageStyle::New()
{
return new AxialImageStyle();
}
-
AxialImageStyle::AxialImageStyle()
-
vtkInteractorStyleImage()
, m_pSliceActor(nullptr)
{
}
void AxialImageStyle::setImageSlice(vtkSmartPointer slice)
{
this->m_pCurrSlice = slice;
}
void AxialImageStyle::setViewType(View_Type type)
{
m_viewType = type;
}
void AxialImageStyle::setViewActor(vtkSmartPointer pActor)
{
m_pActor = pActor;
}
void AxialImageStyle::setViewImageMapColor(vtkSmartPointer colors)
{
m_pMapColors = colors;
}
int* AxialImageStyle::getImageRange()
{
vtkAlgorithm* pAlgorithm = m_pMapColors->GetInputAlgorithm();
pAlgorithm->UpdateInformation();
return pAlgorithm->GetOutputInformation(0)->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT());
}
void AxialImageStyle::getCurrentSlice(int& slice)
{
int* rangeValue = getImageRange();
if (m_viewType == ImageAxial)
{
slice = std::round((rangeValue[5] + rangeValue[4])* 0.5);
m_iSlice = slice;
}
else if (m_viewType == ImageSagittal)
{
slice = std::round((rangeValue[0] + rangeValue[1]) * 0.5);
m_iSlice = slice;
}
else if (m_viewType == ImageCoronal)
{
slice = std::round((rangeValue[2] + rangeValue[3]) * 0.5);
m_iSlice = slice;
}
}
void AxialImageStyle::setRender(vtkSmartPointer pRender)
{
m_pRenderer = pRender;
//showSlice();
}
void AxialImageStyle::setMouseWheelCallBack(std::function<void()> pCallBack)
{
m_pCallBack = pCallBack;
}
void AxialImageStyle::getSliceRange(int& min, int& max)
{
int* rangeValue = getImageRange();
if (m_viewType == ImageAxial)
{
min = rangeValue[4];
max = rangeValue[5];
}
else if (m_viewType == ImageCoronal)
{
min = rangeValue[2];
max = rangeValue[3];
}
else if (m_viewType == ImageSagittal)
{
min = rangeValue[0];
max = rangeValue[1];
}
}
void AxialImageStyle::OnMouseWheelForward()
{
//滚轮放大
/this->FindPokedRenderer(this->Interactor->GetEventPosition()[0], this->Interactor->GetEventPosition()[1]);
if (this->CurrentRenderer == nullptr)return;
this->GrabFocus(this->EventCallbackCommand);
this->StartDolly();
double factor = this->MotionFactor * 0.2 * this->MouseWheelMotionFactor;
this->Dolly(pow(1.1, factor));
this->EndDolly();
this->ReleaseFocus();/
std::array<int, 2> sliceMinMax = this->sliceMax();
if (this->m_iSlice < sliceMinMax[1])
++this->m_iSlice;
this->updateDisplayExtent();
//if (m_pCallBack) m_pCallBack();
}
void AxialImageStyle::OnMouseWheelBackward()
{
//滚轮缩小
/this->FindPokedRenderer(this->Interactor->GetEventPosition()[0], this->Interactor->GetEventPosition()[1]);
if (this->CurrentRenderer == nullptr)return;
this->GrabFocus(this->EventCallbackCommand);
this->StartDolly();
double factor = this->MotionFactor * -0.2 * this->MouseWheelMotionFactor;
this->Dolly(pow(1.1, factor));
this->EndDolly();
this->ReleaseFocus();/
std::array<int, 2> sliceMinMax = this->sliceMax();
if (this->m_iSlice > sliceMinMax[0])
--this->m_iSlice;
this->updateDisplayExtent();
//if (m_pCallBack) m_pCallBack();
}
void AxialImageStyle::updateDisplayExtent()
{
int* range = this->getImageRange();
int min = range[this->m_viewType * 2];
int max = range[this->m_viewType * 2 + 1];
if (this->m_iSlice < min)
this->m_iSlice = min;
else if (this->m_iSlice > max)
this->m_iSlice = max;
switch (this->m_viewType)
{
case View_Type::ImageCoronal:
this->m_pActor->SetDisplayExtent(range[0], range[1], range[2], range[3], this->m_iSlice, this->m_iSlice);
break;
case View_Type::ImageSagittal:
this->m_pActor->SetDisplayExtent(this->m_iSlice, this->m_iSlice, range[2], range[3], range[4], range[5]);
break;
case View_Type::ImageAxial:
this->m_pActor->SetDisplayExtent(range[0], range[1], this->m_iSlice, this->m_iSlice, range[4], range[5]);
break;
default:
break;
}
this->Modified();
this->Interactor->Render();
if (this->m_pRenderer)
{
if (this->GetAutoAdjustCameraClippingRange())
{
this->m_pRenderer->ResetCameraClippingRange();
}
else
{
vtkCamera* cam = this->m_pRenderer->GetActiveCamera();
if (cam)
{
double bounds[6];
this->m_pActor->GetBounds(bounds);
double spos = bounds[this->m_viewType * 2];
double cpos = cam->GetPosition()[this->m_viewType];
double range = fabs(spos - cpos);
double* spacing = m_pMapColors->GetInputAlgorithm()->GetOutputInformation(0)->Get(vtkDataObject::SPACING());
double avg_spacing = (spacing[0] + spacing[1] + spacing[2]) / 3.0;
cam->SetClippingRange(range - avg_spacing * 3.0, range + avg_spacing * 3.0);
}
}
}
}
void AxialImageStyle::showSlice()
{
if (m_pSliceActor == nullptr)
{
m_pSliceActor = vtkSmartPointer::New();
m_pRenderer->AddActor2D(m_pSliceActor);
}
int slice = 0, min = 0, max = 0;
this->getCurrentSlice(slice);
this->getSliceRange(min, max);
std::stringstream stream;
stream << "slice:" << slice << "/" << max;
m_pSliceActor->SetInput(stream.str().c_str());
m_pSliceActor->SetDisplayPosition(50, 50);
m_pSliceActor->GetTextProperty()->SetFontSize(12);
}
std::array<int, 2> AxialImageStyle::sliceMax()
{
vtkAlgorithm* pGroithm = this->m_pMapColors->GetInputAlgorithm();
pGroithm->UpdateInformation();
int* sliceExtent = pGroithm->GetOutputInformation(0)->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT());
std::array<int, 2> range{ sliceExtent[this->m_viewType * 2], sliceExtent[this->m_viewType * 2 + 1] };
return range;
}