7、灰色图转彩色图像(查表映射):
灰色图转化成彩色图,实际上是将灰色图的不同黑白程度对应到不同的其他颜色,是一种颜色一一对应的方法,在实际中,有的图片保存也有这种方式,里面保存了一张表,像素点保存的是索引值。
#include <vtkSmartPointer.h>
#include <vtkJPEGReader.h>
#include <vtkImageLuminance.h>
#include <vtkLookupTable.h>
#include <vtkImageMapToColors.h>
#include <vtkImageActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkInteractorStyleImage.h>
int main()
{
vtkSmartPointer<vtkJPEGReader> reader = vtkSmartPointer<vtkJPEGReader>::New();
reader->SetFileName("flower.jpg");
reader->Update();
// 灰度图
vtkSmartPointer<vtkImageLuminance> grayImg = vtkSmartPointer<vtkImageLuminance>::New();
grayImg->SetInputConnection(reader->GetOutputPort());
grayImg->Update();
// 颜色表
vtkSmartPointer<vtkLookupTable> colorTable = vtkSmartPointer<vtkLookupTable>::New();
colorTable->SetRange(0.0, 255.0); // 对应灰色图的范围,标量范围(0~255)
// 实际上,下面三行将0~255对应到了整个HSI色域空间
colorTable->SetHueRange(0, 1); // 对应色相,色调范围
colorTable->SetSaturationRange(0, 1); // 饱和度范围
colorTable->SetValueRange(0, 1); // 对应的值范围(如果都是0,则全部映射为黑色)
colorTable->Build();
// 创建映射
vtkSmartPointer<vtkImageMapToColors> colorMap = vtkSmartPointer<vtkImageMapToColors>::New();
colorMap->SetInputConnection(reader->GetOutputPort());
colorMap->SetLookupTable(colorTable);
colorMap->Update();
//
vtkSmartPointer<vtkImageActor> origActor = vtkSmartPointer<vtkImageActor>::New();
origActor->SetInputData(reader->GetOutput());
vtkSmartPointer<vtkImageActor> colorActor = vtkSmartPointer<vtkImageActor>::New();
colorActor->SetInputData(colorMap->GetOutput());
//
double origView[4] = { 0.0, 0.0, 0.5, 1.0 };
double colorView[4] = { 0.5, 0.0, 1.0, 1.0 };
vtkSmartPointer<vtkRenderer> origRender = vtkSmartPointer<vtkRenderer>::New();
origRender->SetViewport(origView);
origRender->AddActor(origActor);
origRender->ResetCamera();
origRender->SetBackground(1.0, 0.0, 0.0);
vtkSmartPointer<vtkRenderer> colorRender = vtkSmartPointer<vtkRenderer>::New();
colorRender->SetViewport(colorView);
colorRender->AddActor(colorActor);
colorRender->ResetCamera();
colorRender->SetBackground(0.0, 0.0, 0.0);
//
vtkSmartPointer<vtkRenderWindow> renderwindow = vtkSmartPointer<vtkRenderWindow>::New();
renderwindow->AddRenderer(origRender);
renderwindow->AddRenderer(colorRender);
renderwindow->SetSize(640, 320);
renderwindow->SetWindowName("GrayToColor");
//
vtkSmartPointer<vtkRenderWindowInteractor> rwi =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
vtkSmartPointer<vtkInteractorStyleImage> style =
vtkSmartPointer<vtkInteractorStyleImage>::New();
rwi->SetInteractorStyle(style);
rwi->SetRenderWindow(renderwindow);
rwi->Initialize();
rwi->Start();
return 0;
}
vtkLookupTable并不是专门将灰度图映射到彩色,实际上,他的作用是将一定的标量范围映射到HSI色与空间的某个范围,如果输入的为彩色图像,实际上也是可以映射的,效果如下,和上面差别并不大。
vtkLookupTable是映射器对象用于将标量值映射到RGBA(红 - 绿 - 蓝 - 阿尔法)颜色规范或RGBA到标量值的对象。可以通过直接插入颜色值,或通过指定色调,饱和度,值和alpha范围以及生成表来创建颜色表。
在医学中,就可以使用这个将CT扫描的或则MRI扫描的人体图像,将骨骼,血管,神经,肌肉等不同组织映射成不同的颜色,提供给医生分析,甚至可以通过vtk建立三维模型,不同组织使用不同颜色,非常方便,是一项很实用的技术。
8、通过设置每个通道合成图像:
实际上,使用vtkImageAppendComponents可以合成图像,里面可以对一个通道添加多个输入,然后按照默认的方式,对不同输入做处理,例如,当总共有两个输入的时候,第一个输入就代表显示的图像,第二个代表现实的范围,有三个输入的时候,三个输入就分别代表红绿蓝三同时到,实际上还可以有四个输入,此时前三个输入代表红绿蓝,第四个就代表显示范围。例子如下:
#include <vtkSmartPointer.h>
#include <vtkImageCanvasSource2D.h>
#include <vtkImageAppendComponents.h>
#include <vtkImageActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkInteractorStyleImage.h>
int main()
{
// 红色分量的位图
vtkSmartPointer<vtkImageCanvasSource2D> red = vtkSmartPointer<vtkImageCanvasSource2D>::New();
red->SetScalarTypeToUnsignedChar();
red->SetNumberOfScalarComponents(1);
red->SetExtent(0, 1000, 0, 1000, 0, 0);
red->SetDrawColor(0, 0, 0, 0);
red->FillBox(0, 1000, 0, 1000);
red->SetDrawColor(255, 0, 0, 0);
red->FillBox(250, 550, 250, 550);
red->Update();
// 绿色
vtkSmartPointer<vtkImageCanvasSource2D> green = vtkSmartPointer<vtkImageCanvasSource2D>::New();
green->SetScalarTypeToUnsignedChar();
green->SetNumberOfScalarComponents(1);
green->SetExtent(0, 1000, 0, 1000, 0, 0);
green->SetDrawColor(0, 0, 0, 0);
green->FillBox(0, 1000, 0, 1000);
green->SetDrawColor(255, 0, 0, 0);
green->FillBox(350, 650, 350, 650);
green->Update();
// 蓝色
vtkSmartPointer<vtkImageCanvasSource2D> blue =
vtkSmartPointer<vtkImageCanvasSource2D>::New();
blue->SetScalarTypeToUnsignedChar();
blue->SetNumberOfScalarComponents(1);
blue->SetExtent(0, 1000, 0, 1000, 0, 0);
blue->SetDrawColor(0, 0, 0, 0);
blue->FillBox(0, 1000, 0, 1000);
blue->SetDrawColor(255, 0, 0, 0);
blue->FillBox(450, 750, 450, 750);
blue->Update();
// other
vtkSmartPointer<vtkImageCanvasSource2D> ShowRect =
vtkSmartPointer<vtkImageCanvasSource2D>::New();
ShowRect->SetScalarTypeToUnsignedChar();
ShowRect->SetNumberOfScalarComponents(1);
ShowRect->SetExtent(0, 1000, 0, 1000, 0, 0);
ShowRect->SetDrawColor(0, 0, 0, 0);
ShowRect->FillBox(0, 1000, 0, 1000);
ShowRect->SetDrawColor(255, 0, 0, 0);
ShowRect->FillBox(200, 800, 200, 800);
ShowRect->Update();
// other
vtkSmartPointer<vtkImageCanvasSource2D> other =
vtkSmartPointer<vtkImageCanvasSource2D>::New();
other->SetScalarTypeToUnsignedChar();
other->SetNumberOfScalarComponents(1);
other->SetExtent(0, 1000, 0, 1000, 0, 0);
other->SetDrawColor(0, 0, 0, 0);
other->FillBox(0, 1000, 0, 1000);
other->SetDrawColor(255, 0, 0, 0);
other->FillBox(499, 499, 501, 501);
other->Update();
// 从两个输入中获取组件并将它们合并为一个输出。如果Input1具有M个组件,并且Input2具有N个组件,则输出将具有M + N个组件,其中input1组件首先出现
vtkSmartPointer<vtkImageAppendComponents> appendFilter = vtkSmartPointer<vtkImageAppendComponents>::New();
// 端口0添加一个输入,调用此函数会清除端口0原有所有的输入
appendFilter->SetInputConnection(0, red->GetOutputPort()); // 如果没有这些Add输入,则默认白色,第一个Set输入的算是范围
appendFilter->AddInputConnection(0, green->GetOutputPort()); // 如果只有两个元素,则第一个为显示,第二个为范围
appendFilter->AddInputConnection(0, blue->GetOutputPort()); // 如果有三个输入,则代表红绿蓝输入
appendFilter->AddInputConnection(0, ShowRect->GetOutputPort()); // 如果有四个输入,则前三个代表红绿蓝,第四个代表显示的范围
// appendFilter->AddInputConnection(0, other->GetOutputPort()); // 输入五个显示比较乱,看不出效果
appendFilter->Update();
vtkSmartPointer<vtkImageActor> redActor = vtkSmartPointer<vtkImageActor>::New();
redActor->SetInputData(red->GetOutput());
vtkSmartPointer<vtkImageActor> greenActor = vtkSmartPointer<vtkImageActor>::New();
greenActor->SetInputData(green->GetOutput());
vtkSmartPointer<vtkImageActor> blueActor = vtkSmartPointer<vtkImageActor>::New();
blueActor->SetInputData(blue->GetOutput());
vtkSmartPointer<vtkImageActor> combinedActor = vtkSmartPointer<vtkImageActor>::New();
combinedActor->SetInputData(appendFilter->GetOutput());
double redViewport[4] = { 0.0, 0.0, 0.25, 1.0 };
double greenViewport[4] = { 0.25, 0.0, 0.5, 1.0 };
double blueViewport[4] = { 0.5, 0.0, 0.75, 1.0 };
double combinedViewport[4] = { 0.75, 0.0, 1.0, 1.0 };
vtkSmartPointer<vtkRenderer> redRenderer = vtkSmartPointer<vtkRenderer>::New();
redRenderer->SetViewport(redViewport);
redRenderer->AddActor(redActor);
redRenderer->ResetCamera();
redRenderer->SetBackground(1.0, 1.0, 1.0);
vtkSmartPointer<vtkRenderer> greenRenderer = vtkSmartPointer<vtkRenderer>::New();
greenRenderer->SetViewport(greenViewport);
greenRenderer->AddActor(greenActor);
greenRenderer->ResetCamera();
greenRenderer->SetBackground(1.0, 1.0, 1.0);
vtkSmartPointer<vtkRenderer> blueRenderer = vtkSmartPointer<vtkRenderer>::New();
blueRenderer->SetViewport(blueViewport);
blueRenderer->AddActor(blueActor);
blueRenderer->ResetCamera();
blueRenderer->SetBackground(1.0, 1.0, 1.0);
vtkSmartPointer<vtkRenderer> combinedRenderer = vtkSmartPointer<vtkRenderer>::New();
combinedRenderer->SetViewport(combinedViewport);
combinedRenderer->AddActor(combinedActor);
combinedRenderer->ResetCamera();
combinedRenderer->SetBackground(1.0, 1.0, 1.0);
vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->AddRenderer(redRenderer);
renderWindow->AddRenderer(greenRenderer);
renderWindow->AddRenderer(blueRenderer);
renderWindow->AddRenderer(combinedRenderer);
renderWindow->SetSize(1200, 300);
renderWindow->Render();
renderWindow->SetWindowName("Image");
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
vtkSmartPointer<vtkInteractorStyleImage> style =
vtkSmartPointer<vtkInteractorStyleImage>::New();
renderWindowInteractor->SetInteractorStyle(style);
renderWindowInteractor->SetRenderWindow(renderWindow);
renderWindowInteractor->Initialize();
renderWindowInteractor->Start();
return 0;
}
还可以尝试不同的输入带来的不同效果。
9、提取图像感兴趣区域:
vtkExtractVOI可以对图像的某一部分区域做提取:
#include <vtkSmartPointer.h>
#include <vtkJPEGReader.h>
#include <vtkImageData.h>
#include <vtkExtractVOI.h>
#include <vtkImageActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkInteractorStyleImage.h>
int main()
{
vtkSmartPointer<vtkJPEGReader> reader = vtkSmartPointer<vtkJPEGReader>::New();
reader->SetFileName("flower.jpg");
reader->Update();
int dim[3];
reader->GetOutput()->GetDimensions(dim); // 像素信息,尺寸
// 感兴趣区域提取(vtkExtractVOI只可以用于提取图像数据)
vtkSmartPointer<vtkExtractVOI> extractVOI = vtkSmartPointer<vtkExtractVOI>::New();
extractVOI->SetInputConnection(reader->GetOutputPort());
extractVOI->SetVOI(dim[0] / 4.0, 3.0*dim[0] / 4.0, dim[1] / 4.0, 3.0*dim[1] / 4.0, 0, 0);
extractVOI->Update();
//
vtkSmartPointer<vtkImageActor> origActor = vtkSmartPointer<vtkImageActor>::New();
origActor->SetInputData(reader->GetOutput()); // GetOutput是vtkImageData
vtkSmartPointer<vtkImageActor> voiActor = vtkSmartPointer<vtkImageActor>::New();
voiActor->SetInputData(extractVOI->GetOutput());
//
double origView[4] = { 0, 0, 0.5, 1.0 };
double voiView[4] = { 0.5, 0, 1.0, 1.0 };
vtkSmartPointer<vtkRenderer> origRender = vtkSmartPointer<vtkRenderer>::New();
origRender->SetViewport(origView);
origRender->AddActor(origActor);
origRender->ResetCamera();
origRender->SetBackground(1.0, 0.0, 1.0);
vtkSmartPointer<vtkRenderer> voiRender = vtkSmartPointer<vtkRenderer>::New();
voiRender->SetViewport(voiView);
voiRender->AddActor(voiActor);
voiRender->ResetCamera();
voiRender->SetBackground(0.0, 1.0, 1.0);
//
vtkSmartPointer<vtkRenderWindow> renderwindow = vtkSmartPointer<vtkRenderWindow>::New();
renderwindow->AddRenderer(origRender);
renderwindow->AddRenderer(voiRender);
renderwindow->SetSize(640, 320);
renderwindow->SetWindowName("ExtractVolumeOfInterestFromImage");
//设置交互
vtkSmartPointer<vtkRenderWindowInteractor> rwi =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
vtkSmartPointer<vtkInteractorStyleImage> style =
vtkSmartPointer<vtkInteractorStyleImage>::New();
rwi->SetInteractorStyle(style);
rwi->SetRenderWindow(renderwindow);
rwi->Initialize();
rwi->Start();
return 0;
}
如上图,将JPG图像的中间部分提取出来了。
10、vtk三维图像切片机:
三维切片机可以切割三维模型,切出来三维模型的某一个平面,切出来的这个平面实际上就是一副图像:
#include <vtkSmartPointer.h>
#include <vtkImageData.h>
#include <vtkDICOMImageReader.h>
#include <vtkMatrix4x4.h>
#include <vtkImageReslice.h>
#include <vtkLookupTable.h>
#include <vtkImageMapper3D.h>
#include <vtkImageMapToColors.h>
#include <vtkImageActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkInteractorStyleImage.h>
int main(int argc, char* argv[])
{
vtkSmartPointer<vtkDICOMImageReader> reader = vtkSmartPointer<vtkDICOMImageReader>::New();
reader->SetDirectoryName("F:\\Data\\36040001\\"); // 本身就是float的数据
reader->SetDataByteOrderToLittleEndian();
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]);
static double axialElements[16] = {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
};
vtkSmartPointer<vtkMatrix4x4> resliceAxes =
vtkSmartPointer<vtkMatrix4x4>::New();
resliceAxes->DeepCopy(axialElements);
resliceAxes->SetElement(0, 3, center[0]);
resliceAxes->SetElement(1, 3, center[1]);
resliceAxes->SetElement(2, 3, center[2]);
vtkSmartPointer<vtkImageReslice> reslice =
vtkSmartPointer<vtkImageReslice>::New();
reslice->SetInputConnection(reader->GetOutputPort());
reslice->SetOutputDimensionality(2);
reslice->SetResliceAxes(resliceAxes);
reslice->SetInterpolationModeToLinear();
vtkSmartPointer<vtkLookupTable> colorTable =
vtkSmartPointer<vtkLookupTable>::New();
colorTable->SetRange(0, 1000);
colorTable->SetValueRange(0.0, 1.0);
colorTable->SetSaturationRange(0.0, 0.0);
colorTable->SetRampToLinear();
colorTable->Build();
vtkSmartPointer<vtkImageMapToColors> colorMap =
vtkSmartPointer<vtkImageMapToColors>::New();
colorMap->SetLookupTable(colorTable);
colorMap->SetInputConnection(reslice->GetOutputPort());
vtkSmartPointer<vtkImageActor> imgActor =
vtkSmartPointer<vtkImageActor>::New();
imgActor->GetMapper()->SetInputConnection(reslice->GetOutputPort());
vtkSmartPointer<vtkRenderer> renderer =
vtkSmartPointer<vtkRenderer>::New();
renderer->AddActor(imgActor);
renderer->SetBackground(1.0, 1.0, 1.0);
vtkSmartPointer<vtkRenderWindow> renderWindow =
vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->AddRenderer(renderer);
renderWindow->Render();
renderWindow->SetSize(640, 480);
renderWindow->SetWindowName("Extract3Dslice");
vtkSmartPointer<vtkRenderWindowInteractor> rwi =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
vtkSmartPointer<vtkInteractorStyleImage> imagestyle =
vtkSmartPointer<vtkInteractorStyleImage>::New();
rwi->SetInteractorStyle(imagestyle);
rwi->SetRenderWindow(renderWindow);
rwi->Initialize();
rwi->Start();
return 0;
}
切出来的平面图如上,实际上原始数据是一幅三维图像,下面一节(11节)会看到。
11、vtk三维图像等值面合成图像及三维图像切片机:
切片机可以切割三维模型,切出来三维模型的某一个平面,切出来的这个平面实际上就是一副图像,下面代码只有少部分演示了切片机的使用,现实的切片实际上并非使用的切片机,而是显示的三维照片的某个部分而已。
#include <vtkSmartPointer.h>
#include <vtkImageData.h>
#include <vtkDICOMImageReader.h>
#include <vtkMatrix4x4.h> //
#include <vtkImageReslice.h>
#include <vtkLookupTable.h>
#include <vtkImageMapToColors.h>
#include <vtkImageActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkInteractorStyleImage.h>
#include <vtkImageMapper3D.h>
#include <vtkMarchingCubes.h>
#include <vtkGeometryFilter.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
int main(int argc, char* argv[])
{
vtkSmartPointer<vtkDICOMImageReader> pSrcImage = vtkSmartPointer<vtkDICOMImageReader>::New();
pSrcImage->SetDirectoryName("F:\\Data\\36040001\\"); // 本身就是float的数据
// pSrcImage->SetDataScalarTypeToUnsignedChar();
pSrcImage->SetDataByteOrderToLittleEndian();
pSrcImage->Update();
int extent[6]; // 像素范围
double spacing[3]; // 间距
double origin[3]; // 起始点
pSrcImage->GetOutput()->GetExtent(extent);
pSrcImage->GetOutput()->GetSpacing(spacing);
pSrcImage->GetOutput()->GetOrigin(origin);
double center[3]; // 中心点 = 每个方向的起点 + 间距 * 层数 * 0.5
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]);
static double sagittalElements[16] = { // 矢状面,移动矩阵位置全是0,默认不移动,只有旋转矩阵
0, 0, -1, 0,
1, 0, 0, 0,
0, -1, 0, 0,
0, 0, 0, 1 };
static double axialElements[16] = { // 轴向面,旋转矩阵(没有移动部分)
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
};
static double coronalElements[16] = { // 冠状面
1, 0, 0, 0,
0, 0, 1, 0,
0, -1, 0, 0,
0, 0, 0, 1 };
// 轴向面
vtkSmartPointer<vtkMatrix4x4> resliceAxesSagittal = vtkSmartPointer<vtkMatrix4x4>::New();
resliceAxesSagittal->DeepCopy(sagittalElements);
resliceAxesSagittal->SetElement(0, 3, center[0]); // 设置矩阵移动到3D图像中心
resliceAxesSagittal->SetElement(1, 3, center[1]);
resliceAxesSagittal->SetElement(2, 3, center[2]);
vtkSmartPointer<vtkImageReslice> resliceSagittal = vtkSmartPointer<vtkImageReslice>::New();
resliceSagittal->SetInputConnection(pSrcImage->GetOutputPort());
resliceSagittal->SetOutputDimensionality(2); // 2维切片机
resliceSagittal->SetResliceAxes(resliceAxesSagittal); // 切片矩阵
resliceSagittal->SetInterpolationModeToLinear(); // 线性插值模式
vtkSmartPointer<vtkMatrix4x4> resliceAxesAxial = vtkSmartPointer<vtkMatrix4x4>::New();
resliceAxesAxial->DeepCopy(axialElements);
resliceAxesAxial->SetElement(0, 3, center[0]); // 设置矩阵移动到3D图像中心
resliceAxesAxial->SetElement(1, 3, center[1]);
resliceAxesAxial->SetElement(2, 3, center[2]);
vtkSmartPointer<vtkImageReslice> resliceAxial = vtkSmartPointer<vtkImageReslice>::New();
resliceAxial->SetInputConnection(pSrcImage->GetOutputPort());
resliceAxial->SetOutputDimensionality(2); // 2维切片机
resliceAxial->SetResliceAxes(resliceAxesAxial); // 切片矩阵
resliceAxial->SetInterpolationModeToLinear(); // 线性插值模式
vtkSmartPointer<vtkMatrix4x4> resliceAxesCoronal = vtkSmartPointer<vtkMatrix4x4>::New();
resliceAxesCoronal->DeepCopy(coronalElements);
resliceAxesCoronal->SetElement(0, 3, center[0]); // 设置矩阵移动到3D图像中心
resliceAxesCoronal->SetElement(1, 3, center[1]);
resliceAxesCoronal->SetElement(2, 3, center[2]);
vtkSmartPointer<vtkImageReslice> resliceCoronal = vtkSmartPointer<vtkImageReslice>::New();
resliceCoronal->SetInputConnection(pSrcImage->GetOutputPort());
resliceCoronal->SetOutputDimensionality(2); // 2维切片机
resliceCoronal->SetResliceAxes(resliceAxesCoronal); // 切片矩阵
resliceCoronal->SetInterpolationModeToLinear(); // 线性插值模式
// 以上三个切片机切出来的,可以直接当Image使用,设置到vtkImageActor即可显示
vtkSmartPointer<vtkLookupTable> colorTable = vtkSmartPointer<vtkLookupTable>::New();
colorTable->SetTableRange(0, 1000);
colorTable->SetValueRange(0.1, 1.0);
colorTable->SetSaturationRange(0.0, 1.0);
colorTable->SetHueRange(0.0, 1.0);
colorTable->SetRampToLinear();
colorTable->Build();
vtkSmartPointer<vtkImageMapToColors> colorMapSagittal = vtkSmartPointer<vtkImageMapToColors>::New();
colorMapSagittal->SetLookupTable(colorTable);
colorMapSagittal->SetInputConnection(pSrcImage->GetOutputPort());
vtkSmartPointer<vtkImageActor> imgActorSagittal = vtkSmartPointer<vtkImageActor>::New();
imgActorSagittal->GetMapper()->SetInputConnection(colorMapSagittal->GetOutputPort());
imgActorSagittal->SetDisplayExtent((extent[0] + extent[1]) / 2, (extent[0] + extent[1]) / 2, extent[2], extent[3], extent[4], extent[5]);
imgActorSagittal->ForceOpaqueOn();
vtkSmartPointer<vtkImageMapToColors> colorMapAxial = vtkSmartPointer<vtkImageMapToColors>::New();
colorMapAxial->SetLookupTable(colorTable);
colorMapAxial->SetInputConnection(pSrcImage->GetOutputPort());
vtkSmartPointer<vtkImageActor> imgActorAxial = vtkSmartPointer<vtkImageActor>::New();
imgActorAxial->GetMapper()->SetInputConnection(colorMapAxial->GetOutputPort());
imgActorAxial->SetDisplayExtent(extent[0], extent[1], extent[2], extent[3], (extent[4] + extent[5]) / 2, (extent[4] + extent[5]) / 2);
imgActorAxial->ForceOpaqueOn();
vtkSmartPointer<vtkImageMapToColors> colorMapCoronal = vtkSmartPointer<vtkImageMapToColors>::New();
colorMapCoronal->SetLookupTable(colorTable);
colorMapCoronal->SetInputConnection(pSrcImage->GetOutputPort());
vtkSmartPointer<vtkImageActor> imgActorCoronal = vtkSmartPointer<vtkImageActor>::New();
imgActorCoronal->GetMapper()->SetInputConnection(colorMapCoronal->GetOutputPort());
imgActorCoronal->SetDisplayExtent(extent[0], extent[1], (extent[2] + extent[3]) / 2, (extent[2] + extent[3]) / 2, extent[4], extent[5]);
imgActorCoronal->ForceOpaqueOn();
// 3D部分
vtkSmartPointer<vtkActor> m_3DActorBone = vtkActor::New();
vtkSmartPointer<vtkMarchingCubes> m_3DImageData = vtkMarchingCubes::New();
m_3DImageData->SetInputConnection(pSrcImage->GetOutputPort());
m_3DImageData->ComputeNormalsOn();
m_3DImageData->SetValue(0, 275); // CT数据下,骨骼对应的值大约275
vtkSmartPointer<vtkGeometryFilter>geoVolumeBone = vtkGeometryFilter::New();
geoVolumeBone->SetInputConnection(m_3DImageData->GetOutputPort());
vtkSmartPointer<vtkPolyDataMapper> m_3DMapper = vtkPolyDataMapper::New();
m_3DMapper->SetInputConnection(geoVolumeBone->GetOutputPort());
m_3DMapper->ScalarVisibilityOff();
m_3DMapper->Update();
m_3DActorBone->SetMapper(m_3DMapper);
m_3DActorBone->GetProperty()->SetColor(0.725, 0.478, 0.341);
vtkSmartPointer<vtkRenderer> renderer =
vtkSmartPointer<vtkRenderer>::New();
renderer->AddActor(imgActorSagittal);
renderer->AddActor(imgActorAxial);
renderer->AddActor(imgActorCoronal);
// renderer->AddActor(m_3DActorBone);
renderer->SetBackground(0.8, 0.8, 0.8);
vtkSmartPointer<vtkRenderWindow> renderWindow =
vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->AddRenderer(renderer);
renderWindow->Render();
renderWindow->SetSize(640, 480);
renderWindow->SetWindowName("Extract3DTest");
vtkSmartPointer<vtkRenderWindowInteractor> rwi =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
vtkSmartPointer<vtkInteractorStyleTrackballCamera> imagestyle =
vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New();
rwi->SetInteractorStyle(imagestyle);
rwi->SetRenderWindow(renderWindow);
renderWindow->Render();
rwi->Initialize();
rwi->Start();
return 0;
}
如上图,红色部分代表估值紧密部分,蓝色,绿色部分骨质比较疏松,其他灰色等,代表其他组织,由于CT扫描只能很好的分辨骨骼等高密度的物质,其他组织无法很好的表现,如果要看其他组织,可以使用MRI扫描的DICOM标准的数据。
如上图,同时还可以建立三维模型,背后比较平的部分是床板(人体睡在床上扫描的CT),将代码略作改变,可以得到如下效果:
12、三维图像相关的交互:
加上三维现实的交互,就可以自由切换切片机切得位置了,可以动态调整:
#include <vtkSmartPointer.h>
#include <vtkImageData.h>
#include <vtkDICOMImageReader.h>
#include <vtkMatrix4x4.h>
#include <vtkLookupTable.h>
#include <vtkImageMapToColors.h>
#include <vtkImageActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkInteractorStyleImage.h>
#include <vtkImageMapper3D.h>
#include <vtkAxesActor.h>
class vtkVolumeImageInteractionCallback : public vtkCommand
{
public:
static vtkVolumeImageInteractionCallback *New() //回调函数初始化函数
{
return new vtkVolumeImageInteractionCallback;
}
vtkVolumeImageInteractionCallback()
{
this->m_bSlicing = false;
this->m_imgActorSagittal = nullptr;
this->m_imgActorCoronal = nullptr;
this->m_imgActorAxial = nullptr;
this->Interactor = nullptr;
}
void SetImageActor(vtkImageActor* s, vtkImageActor* c, vtkImageActor*a)
{
this->m_imgActorSagittal = s;
this->m_imgActorCoronal = c;
this->m_imgActorAxial = a;
}
void SetInteractor(vtkRenderWindowInteractor *interactor)
{
this->Interactor = interactor;
}
vtkRenderWindowInteractor *GetInteractor()
{
return this->Interactor;
}
virtual void Execute(vtkObject *, unsigned long event, void *)
{
vtkRenderWindowInteractor *interactor = GetInteractor();
int lastPos[2];
interactor->GetLastEventPosition(lastPos);
int currPos[2];
interactor->GetEventPosition(currPos);
if (event == vtkCommand::RightButtonPressEvent)
{
this->m_bSlicing = 1; // 开始设置切片位置
}
else if (event == vtkCommand::RightButtonReleaseEvent)
{
this->m_bSlicing = 0; // 停止设置切片位置
}
else if (event == vtkCommand::MouseMoveEvent)
{
if (this->m_bSlicing)//检验鼠标左键已经按下 正在执行操作
{
//记下鼠标Y向变化的幅值大小
int deltaX = lastPos[0] - currPos[0];
int deltaY = lastPos[1] - currPos[1];
int Pos[6] = { 0 };
// 矢状面
m_imgActorSagittal->GetDisplayExtent(Pos);
Pos[0] -= deltaX; Pos[1] = Pos[0];
m_imgActorSagittal->SetDisplayExtent(Pos);
// 冠状面
m_imgActorCoronal->GetDisplayExtent(Pos);
Pos[2] -= deltaY; Pos[3] = Pos[2];
m_imgActorCoronal->SetDisplayExtent(Pos);
// 上面部分最好加上范围判断,超出范围显示不出的,vtk会报错
interactor->Render();
}
else
{
vtkInteractorStyle *style = vtkInteractorStyle::SafeDownCast(
interactor->GetInteractorStyle());
if (style)
{
style->OnMouseMove();
}
}
}
}
private:
bool m_bSlicing;
vtkImageActor* m_imgActorSagittal;
vtkImageActor* m_imgActorCoronal;
vtkImageActor* m_imgActorAxial;
vtkRenderWindowInteractor *Interactor;
};
int main(int argc, char* argv[])
{
vtkSmartPointer<vtkDICOMImageReader> pSrcImage = vtkSmartPointer<vtkDICOMImageReader>::New();
pSrcImage->SetDirectoryName("F:\\Data\\36040001\\"); // 本身就是float的数据
pSrcImage->SetDataByteOrderToLittleEndian();
pSrcImage->Update();
int extent[6]; // 像素范围
double spacing[3]; // 间距
double origin[3]; // 起始点
pSrcImage->GetOutput()->GetExtent(extent);
pSrcImage->GetOutput()->GetSpacing(spacing);
pSrcImage->GetOutput()->GetOrigin(origin);
double center[3]; // 中心点 = 每个方向的起点 + 间距 * 层数 * 0.5
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]);
vtkSmartPointer<vtkLookupTable> colorTable = vtkSmartPointer<vtkLookupTable>::New();
colorTable->SetTableRange(0, 1000);
colorTable->SetValueRange(0.1, 1.0);
colorTable->SetSaturationRange(0.0, 1.0);
colorTable->SetHueRange(0.0, 1.0);
colorTable->SetRampToLinear();
colorTable->Build();
vtkSmartPointer<vtkImageMapToColors> colorMapSagittal = vtkSmartPointer<vtkImageMapToColors>::New();
colorMapSagittal->SetLookupTable(colorTable);
colorMapSagittal->SetInputConnection(pSrcImage->GetOutputPort());
vtkSmartPointer<vtkImageActor> imgActorSagittal = vtkSmartPointer<vtkImageActor>::New();
imgActorSagittal->GetMapper()->SetInputConnection(colorMapSagittal->GetOutputPort());
imgActorSagittal->SetDisplayExtent((extent[0] + extent[1]) / 2, (extent[0] + extent[1]) / 2, extent[2], extent[3], extent[4], extent[5]);
imgActorSagittal->ForceOpaqueOn();
vtkSmartPointer<vtkImageMapToColors> colorMapCoronal = vtkSmartPointer<vtkImageMapToColors>::New();
colorMapCoronal->SetLookupTable(colorTable);
colorMapCoronal->SetInputConnection(pSrcImage->GetOutputPort());
vtkSmartPointer<vtkImageActor> imgActorCoronal = vtkSmartPointer<vtkImageActor>::New();
imgActorCoronal->GetMapper()->SetInputConnection(colorMapCoronal->GetOutputPort());
imgActorCoronal->SetDisplayExtent(extent[0], extent[1], (extent[2] + extent[3]) / 2, (extent[2] + extent[3]) / 2, extent[4], extent[5]);
imgActorCoronal->ForceOpaqueOn();
vtkSmartPointer<vtkImageMapToColors> colorMapAxial = vtkSmartPointer<vtkImageMapToColors>::New();
colorMapAxial->SetLookupTable(colorTable);
colorMapAxial->SetInputConnection(pSrcImage->GetOutputPort());
vtkSmartPointer<vtkImageActor> imgActorAxial = vtkSmartPointer<vtkImageActor>::New();
imgActorAxial->GetMapper()->SetInputConnection(colorMapAxial->GetOutputPort());
imgActorAxial->SetDisplayExtent(extent[0], extent[1], extent[2], extent[3], (extent[4] + extent[5]) / 2, (extent[4] + extent[5]) / 2);
imgActorAxial->ForceOpaqueOn();
vtkSmartPointer<vtkAxesActor> axes = vtkSmartPointer<vtkAxesActor>::New();
axes->SetTotalLength(100, 100, 100);
axes->SetCylinderResolution(10);
vtkSmartPointer<vtkRenderer> renderer =
vtkSmartPointer<vtkRenderer>::New();
renderer->AddActor(imgActorSagittal);
renderer->AddActor(imgActorAxial);
renderer->AddActor(imgActorCoronal);
renderer->AddActor(axes);
renderer->SetBackground(0.8, 0.8, 0.8);
vtkSmartPointer<vtkRenderWindow> renderWindow =
vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->AddRenderer(renderer);
renderWindow->Render();
renderWindow->SetSize(640, 480);
renderWindow->SetWindowName("Extract3Dslice");
vtkSmartPointer<vtkRenderWindowInteractor> rwi =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
vtkSmartPointer<vtkInteractorStyleTrackballCamera> imagestyle =
vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New();
vtkSmartPointer<vtkVolumeImageInteractionCallback> cb =
vtkSmartPointer<vtkVolumeImageInteractionCallback>::New();
cb->SetImageActor(imgActorSagittal, imgActorCoronal, imgActorAxial);
cb->SetInteractor(rwi);
imagestyle->AddObserver(vtkCommand::MouseMoveEvent, cb);
imagestyle->AddObserver(vtkCommand::RightButtonPressEvent, cb);
imagestyle->AddObserver(vtkCommand::RightButtonReleaseEvent, cb);
rwi->SetInteractorStyle(imagestyle);
rwi->SetRenderWindow(renderWindow);
renderWindow->Render();
rwi->Initialize();
rwi->Start();
return 0;
}
如上图,两幅图是通过鼠标右键拖动达到的效果,鼠标拖动的X变化决定矢状面的移动,Y的变化决定冠状面的变化,轴向面一直保持不变。
如果要直接使用普通jpg图片,也可以尝试,通过鼠标拖动改变一幅jpg图片的位置,达到类似的效果,三实际上,上面演示的,位置和图像都在改变。