写在前面
目前,根据重建所采取的原理和方式不同,分为面绘制和体绘制两种方式。面绘制速度快,交互性好,具有实时性,Lorensen等人 [1] 提出的移动立方体算法是面绘制的经典算法,该算法实现简单,并且三维重建效果较好。体绘制直接从三维数据场产生屏幕上的二维图像,成像真实,能够观察物体内部结构,缺点数据存储量打,计算时间长。
原理
常用的算法有:
- 光线投射算法(RayCasting):
1)特点:基于射线扫描过程;绘制效果较好;可以较轻松一直到GPU上实现,达到实时绘制要求。
2)原理:光线投射方法是基于图像序列的直接体绘制算法。从投影图像平面的每一个像素,沿固定方向(通常是视线方向)从视点发射一条射线,光线穿越整个图像序列,并在这个过程中,在射线上对图像序列按照一定的步长进行等距离采样,对每个采样点采用插值技术来计算其体素值,根据颜色传输函数和不透明度传输函数来获取相应的颜色值和不透明度,依据光线吸收模型将颜色值进行累加直至光线穿过体数据,最后得到当前平面像素的渲染颜色, 生成最终的显示图像。[参考链接里写得超级详细也很深入!!!] - 抛雪球算法(Splatting)
- 错切变形算法(Shear Warp)
- 三维纹理贴图法:使用一系列平面来构造对象。数据集投射到平面成为纹理。最后的图形是由混合平面上α粒子组成。
过程(VTK)
体绘制形成的图像一般是半透明的图像,颜色一般是人工指定的伪彩色。体绘制首先需要对数据进行分类处理,不同类别赋予不同的颜色和透明度值,然后根据空间中视点和体数据的相对位置确定最终成像效果。首先根据下面管线确定总体流程。
-
vtk体绘制的渲染管线(右):
-
vtkVolumeMapper
vtkVolumeMapper是所有体绘制Mapper的虚基类,提供接口函数,并由其子类实现具体功能。下面是它的几个子类。
vtkGPUVolumeRayCastMapper类实现了基于GPU加速的光线投影体绘制算法,生成渲染图元数据传递给vtkVolume对象进行渲染。内部有两个重要函数?vtkGPUVolumeRayCastMapper::SetInput(vtkImageData *) //用于设置输入图像数据 vtkGPUVolumeRayCastMapper::SetBlendMode() //设置混合模式。默认模式是“复合”,标量值是通过体积采样的,并通过alpha混合以从前到后的方式进行复合。使用颜色和不透明度传递函数确定最终的颜色和不透明度。 //BlendMode https://vtk.org/doc/nightly/html/classvtkVolumeMapper.html#ac17e07d033289f756622d1e7b2a6a0ea SetBlendModeToComposite()//COMPOSITE_BLEND SetBlendModeToMaximumIntensity()//MAXIMUM_INTENSITY_BLEND SetBlendModeToMinimumIntensity()//MINIMUM_INTENSITY_BLEND SetBlendModeToAverageIntensity()//AVERAGE_INTENSITY_BLEND SetBlendModeToAdditive()//ADDITIVE_BLEND SetBlendModeToIsoSurface()//ISOSURFACE_BLEND SetBlendModeToSlice()//SLICE_BLEND
-
vtkVolume Property
该对象用于设置体绘制的颜色和不透明度函数以及阴影等信息。在体绘制中,颜色以及不透明度的设置至关重要,决定了最终的显示效果。
1)不透明度传输函数是一个分段线性标量映射函数,利用该函数可将光线投射过程中的采样点灰度值映射为不同的不透明度值,以决定最终的颜色值。 vtkPiecewiseFunction类实现不透明度传输函数的设置。该类定义标量线性分段函数,其支持两种设置方式,第一种是直接添加断点,第二种直接添加一条线段。vtkPiecewiseFunction 中有个Clamping 标志,当Clamping 标志为真时(默认) ,对于小于所有断点最小灰度值的灰度值,其映射值为最小灰度值断点对应的映射值;对于大于所有断点最大灰度值的灰度值, 其映射值为最大灰度值断点对应的映射值,而当Clamping 标志为假时,所有位于断点灰度范围之外的灰度对应的映射值均为0 。利用不透明度设置函数,可以有选择地对图像中的对象进行显示。对于不显示的对象,只需将其对应的灰度范围的不透明度映射为0 即可。内部用到的函数?//添加断点 vtkPiecewiseFunction::AddPoint(double x, double y) // x-自变量,这里指灰度值;y-映射值,这里指不透明度。执行成功后,返回当前添加的断点的index索引值(以0开始),否则返回-1. //添加一条直线 vtkPiecewiseFunction::AddSegment(double x1, double y1, double x2, double y2) //添加两个断点(xl ,yl)和(泣,y2),组成一条线段。当添加一条线段时,如果该线段内已经存在断点,则该断点会被清除。 vtkPiecewiseFunction::RemovePoint(double x) //将自变量值为x 的断点删除。 vtkPiecewiseFunction::RemoveAllPoints() //删除所有断点。 vtkPiecewiseFunction::ClampingOff()
2)颜色传输函数,将一个标量值映射为一个颜色值。这个颜色值既可以是RGB 值,也可以是HSV 值。 vtkColorTransferFunction类来实现,内部用到的函数?vtkColorTransferFunction::AddRGBPoint(double x, double r, double g, double b); vtkColorTransferFunction:AddRGBSegment(double x1, double r, double g, double b, double x2, double r, double g, double b);
最终vtkVolumeProperty的设置用到的一些函数?
vtkVolumeProperty::SetScalarOpacity(vtkPiecewiseFunction *function); vtkVolumeProperty::SetGradientOpacity(vtkPiecewiseFunction *Gradientfunction);//梯度不透明度函数 vtkVolumeProperty::SetColor(vtkColorTransferFunction *function); //下面是设置阴影效果(Shading)。阴影效果主要受环境光系数、散射光系数、反射光系数和高光强度四个参数印象。 vtkVolumeProperty::ShadeOn(); //默认是关闭阴影效果的,关闭时,等同于环境光系数为1.0 ,其他两个系数为0。 vtkVolumeProperty::SetAmbient(0.4);//设置环境光系数 vtkVolumeProperty::SetDiffuse(0.6);//设置散射光系数 vtkVolumeProperty::SetSpecular(0.2);//设置反射光系数 以上三个系数一般合为1,有时提高亮度,合大于1 vtkVolumeProperty:::SetSpecularPower();//高光强度,控制外观平滑程度
-
vtkVolume
vtkVolume 类似于几何渲染中的vtkActor,用于表示渲染场景中的对象。除了存储基本的变换信息(平移、旋转、放缩等),其内部还存储了两个重要对象:vtkAbstractVolumeMapper对象和vtkVolumeProperty对象。内部用到的函数?vtkVolume::SetMapper(vtkAbstractVolumeMapper *mapper) //连接vtkAbstractVolumeMapper对象,不同体绘制算法,有不同的Mapper类。 vtkVolume::SetProperty(vtkVolumeProperty *property) //用于设置vtkVolumeProperty对象,
实现
#include "stdafx.h"
#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL)
VTK_MODULE_INIT(vtkInteractionStyle);
VTK_MODULE_INIT(vtkRenderingFreeType);
#define vtkRenderingCore_AUTOINIT 4(vtkInteractionStyle,vtkRenderingFreeType,vtkRenderingFreeTypeOpenGL,vtkRenderingOpenGL)
#define vtkRenderingVolume_AUTOINIT 1(vtkRenderingVolumeOpenGL)
#include <vtkSmartPointer.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkInteractorStyleTrackballCamera.h>
#include <vtkDICOMImageReader.h>
#include <vtkImageShiftScale.h>
#include <vtkGPUVolumeRayCastMapper.h>
#include <vtkVolumeProperty.h>
#include <vtkPiecewiseFunction.h>
#include <vtkColorTransferFunction.h>
#include <vtkVolume.h>
int _tmain(int argc, _TCHAR* argv[])
{
vtkSmartPointer<vtkDICOMImageReader> reader = vtkSmartPointer<vtkDICOMImageReader>::New();
reader->SetDirectoryName("ST9");
reader->Update();
vtkSmartPointer<vtkGPUVolumeRayCastMapper> volumeMapper = vtkSmartPointer<vtkGPUVolumeRayCastMapper>::New();
volumeMapper->SetInputData(reader->GetOutput());
volumeMapper->SetBlendModeToComposite();
vtkSmartPointer<vtkVolumeProperty> volumeProperty = vtkSmartPointer<vtkVolumeProperty>::New();
volumeProperty->SetInterpolationTypeToLinear();
volumeProperty->ShadeOn();//阴影开关,打开下面的设置才有效
volumeProperty->SetAmbient(0.4);
volumeProperty->SetDiffuse(0.6); //漫反射
volumeProperty->SetSpecular(0.2); //镜面反射
//设置不透明度
vtkSmartPointer<vtkPiecewiseFunction> compositeOpacity = vtkSmartPointer<vtkPiecewiseFunction>::New();
compositeOpacity->AddPoint(-200, 0.00);
compositeOpacity->AddPoint(-50, 0.10);
compositeOpacity->AddPoint(90, 0.40);
compositeOpacity->AddPoint(500, 0.60);
volumeProperty->SetScalarOpacity(compositeOpacity); //设置不透明度传输函数
//设置梯度不透明属性
//vtkSmartPointer<vtkPiecewiseFunction> volumeGradientOpacity = vtkSmartPointer<vtkPiecewiseFunction>::New();
//volumeGradientOpacity->AddPoint(10, 0.0);
//volumeGradientOpacity->AddPoint(70, 0.3);
//volumeGradientOpacity->AddPoint(90, 0.5);
//volumeGradientOpacity->AddPoint(500, 1.0);
//volumeProperty->SetGradientOpacity(volumeGradientOpacity);//设置梯度不透明度效果对比
//设置颜色属性
vtkSmartPointer<vtkColorTransferFunction> color = vtkSmartPointer<vtkColorTransferFunction>::New();
color->AddRGBPoint(-400.000, 0.9, 0.8, 0.50);
color->AddRGBPoint(-90.00, 0.9, 0.8, 0.50);
color->AddRGBPoint(0.00, 8.00, 0.00, 0.00);
color->AddRGBPoint(190.0, 1.00, 1.00, 1.00);
color->AddRGBPoint(500.0, 1.00, 1.00, 1.00);
volumeProperty->SetColor(color);
//类似Actor
vtkSmartPointer<vtkVolume> volume = vtkSmartPointer<vtkVolume>::New();
volume->SetMapper(volumeMapper);
volume->SetProperty(volumeProperty);
vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
renderer->SetBackground(0, 0, 0.25);
renderer->AddVolume(volume);
vtkSmartPointer<vtkRenderWindow> rw = vtkSmartPointer<vtkRenderWindow>::New();
rw->AddRenderer(renderer);
rw->Render();
vtkSmartPointer<vtkRenderWindowInteractor> rwi = vtkSmartPointer<vtkRenderWindowInteractor>::New();
rwi->SetRenderWindow(rw);
vtkSmartPointer<vtkInteractorStyleTrackballCamera> style = vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New();
rwi->SetInteractorStyle(style);
rwi->Initialize();
rwi->Start();
return 0;
}
透明度和颜色的参数得好好调一下才能显示好的效果,这个有点烂?
一些概念补充
- 数字图像
描述数据元素的颜色和光强的二维阵列,这些元素称作像素;描述数据元素的颜色和光强的三维阵列,每个值称为体素。 - 体数据
体数据由体素组成。 体素是基本体积元素,也可以理解为三维空间内的具有排列和颜色的点或一小块。通常体素属于固定网格,因此体数据可以作为表格储存。在这种情况下,运行可以被认为是多维数组,体数据可以被当作为本地储存的*.csv文件。然而,更常见的是,数据集被分成若干片,并且每个片被存储为位图图像。由于可以应用于图像的复杂压缩算法,明显减小模型尺寸。体数据信息可被用于癌症检测,动脉瘤可视化和治疗计划。 - 医学图像三维数据场
由CT(计算机断层成像)或MRI(核磁共振)扫描获得一系列的医学图像切片数据,把这些切片数据按照位置和角度信息进行规则化处理,然后就形成一个三维空间中由均匀网格组成的规则的数据场,网格上的每个节点描述了对象的密度等属性信息,相邻层之间的对应的八个节点包围的小立方体称为体素。体绘制以这种体素为基本操作单位,计算出每个体素对显示图像的影响。 - CT图像
CT值的单位是Hounsfied(Hu),范围是 [-1024~3071],用于衡量人体组织对X射线的吸收率,设定睡得吸收率为0Hu。 - CT值及CT常用窗宽、窗位
参考
- 张晓东, 罗火灵. VTK图形图像开发进阶[M]. 机械工业出版社, 2015.
- Lorensen W E, Cline H E. Marching cubes: a high resolution 3D surface construction algorithm [M]. Seminal graphics. ACM, 1998: 163-169.
- 体绘制算法
- 体绘制
- 什么是体数据可视化(Volume data visualization)?及体绘制的各种算法和技术的特点?
- 体绘制(Volume Rendering)概述
- VTK修炼之道62:体绘制_固定点光线投影体绘制与GPU加速光线投影体绘制
- https://blog.csdn.net/YZY_001/article/details/85257905
- no override found for vtkGPUVolumeRaycastMapper
- RGB颜色参考
- 临床CT值