设计:数字化建模实验。
时间及地点:实验于2012年7月至2013年5月在中南大学湘雅三医院骨科完成。
材料:个人电脑(Core i5-3210,2.7 GHz CPU,4 Gb内存,500 G硬盘,Nvidia GT640图形显示卡);Visual C++ 6.0(Microsoft Corporation,美国);VTK(Kitware Inc.,美国);CMake(Kitware Inc.,美国);CT DICOM数据由Philips公司Mx8000 IDT 16 CT机采集。
实验方法:在Windows环境下安装VC++6.0及VTK,使用VC++6.0及VTK构建系统平台;下载并进行vtk、Cmake 2.8、VC++6.0的安装,并在VC++6.0中进行工程设置,完成整个编程环境的安装及设置。
采用MarchingCubes算法,实现DICOM数据到三维模型建立。三维面绘制及三维体绘制是医学数据三维重建的两种主要方法。其中三维面绘制过程中的重要步骤是将数量较多的二维图像序列在进行较高水平的分割处理后,再通过一定的技术手段对其进行轮廓处理识别以及成像提取等,经过相应算法的处理对抽取的等值面进行赋值,最终还原出被检测物体的三维模型,并将结果以三维模型的形式显示出来。
针对于医学应用三维面绘制方法主要用于重建对比清晰、表面特征显著及光泽度高的组织或器官,如皮肤、骨骼等,对形态不明显且亮度有光泽度变化大的血管、细支气管等精细组织和器官则不适用。三维面绘制算法可以根据其不同的处理方式主要被分为两种类型:体素级和轮廓级的构建。其中体素级的重新构建主要是基于按照实际需求进行确定采样点值后,截取物体表面在每一个体素中呈现出的小面片,然后运用相应的图形处理技术手段将这些小面片拼接在一起从而构建出一个完整的表面[17-18]。采用这种方式进行演算的算法主要有Marching Cubes算法和Dividing Cubes算法等;基于轮廓的重建则是通过一组平面轮廓来重建物体的表面,轮廓拼接法为该类型算法的典型代表,但基于轮廓的重建在实际应用中因为重建的物体表面常有残缺,现已基本不再采用。
Marching Cubes算法是面显示算法中的经典算法,它也被称为“等值面提取”(Isosurface Extraction)。等值面是将某个三维空间中由编程者人为规定的一个设定值,将与规定值相同的所有点进行统计,最后形成相同点的集合[19]。采用移动立方体算法可以方便的重建出人体外部轮廓及组织、器官等的三维模型,通过观察这些三维模型能让医务工作者直观观察病变部位的形态、大小及其与周围组织的毗邻关系。
Marching Cubes算法的具体流程可以简单描述如下:将CT或MRI检查的DICOM数据分层读入内存分别扫描上下两层数据,两张切片上下相对应的4个点构成一个立方体(Cube),也被称为体素,其中相邻两层中的8个点形成了单个体素中顶点,将人为指定的等值面设定值与体素中每个顶点的密度或灰度进行对比,再通过对比的结果形成单个体素的索引值,使用索引值计算出该体素与设置好的等值面之间是否连接以及其连接的方式,并进行线性插值运算得到三角片顶点的坐标。
以1例骨盆骨折患者的CT数据进行三维重建,对软件效果进行验证,具体步骤如下:启动三维重建软件,通过文件菜单下“打开DICOM序列”命令读取采集到的骨盆骨折CT数据:DICOM数据导入后使用三维重建菜单下“MarchingCubes”命令进行三维建模,抽取等值面参数设置为400,对CT数据进行移动立方体计算。
VTK中实现CT DICOM数据三维重建的具体步骤:
VTK下MC算法程序实例:
reader=vtkDICOMImageReader::New();
reader->SetDataByteOrderToLittleEndian();
shrink=vtkImageShrink3D::New();
shrink->SetShrinkFactors(0,0,0);
shrink->AveragingOn();
skinExtractor=vtkContourFilter::New();
skinExtractor->SetlnputConnection(shrink->GetOutputPort());
skinExtractor->SetValue(0,400);
sphere=vtkSphereSource::New();
sphere->SetThetaResolution(100);
sphere->SetPhiResolution(100);
sphere->SetRadius(2);
deci=vtkDecimatePro::New();
deci->SetlnputConnection(sphere->GetOutputPort());
deci->SetTargetReduction(0.3);
smooth—vtkSmoothPolyDataFilter::New();
smooth->SetlnputConnection(deci->GetOutputPort());
smooth->SetNumberOflterations(200);
skinNormals=vtkPolyDataNormals::New0;
skinNormals->SetlnputConnection(smooth->GetOutputPort());
skinNormals->SetFeatureAngle(60.0);
stripper=vtkStripper::New();
stripper->Setlnput(skinNormals->GetOutput());
skinMapper =vtkPolyDataMappe::New();
skinMapper->Setlnput(stripper->GetOutput());
skinMapper->ScalarVisibilityOff();
coneActor=vtkActor::New();
coneActor->SetMapper(skinMapper);
coneActor->GetProperty()->SetAmbient(0.5);
coneActor->GetPropertY()->SetAmbientColor(0.1,0.2,0.6);
coneActor->GetProperty()->SetDiffuse(1);
coneActor->GetProperty()->SetDiffuseColor(0.3,0.6,0.2);
coneActor->GetProperty()->SetSpecular(0.6);
coneActor->GetProperty()->SetSpecularColor(0.8,0.3,0.5);
coneActor->GetProperty()->SetSpecularPower(1);
aCamera=vtkCamera::New();
aCamera->SetViewUp(0,0,0);
aCamera->SetPosition(1,1,1);
aCamera->SetFocalPoim(99,99,40);
renderer=vtkRenderer::New();
renderer->AddActor(coneActor);