序言,VTK介紹:
VTK 全稱爲 The Visualization Toolkit
(可視化工具),是一個開源、跨平臺、自由獲取、支持並行計算的圖形應用函數;擁有3D 渲染的最新工具、提供3D交互模式以及2D繪圖等。
VTK 包含一個C++類庫,目前提供了衆多語言接口,例如 Java、Python、TCL
;在三維函數庫OpenGL
的基礎上採用面向對象設計方法發展起來
圖形學基本概念和數據結構,是VTK的核心,VTK是通過 Pipline
的形式來輸送數據,實現預覽效果。
三維重建
在 VTK 中,提供了兩種重建方式:體繪製和麪繪製 (一般來說用VTK做重建,醫學圖像領域較多,如 Dicom、mha、mhd
;當然 VTK 也實現點雲重建)
面繪製
利用面繪值用到VTK封裝到的 Marching Cube
算法,簡稱 MC
算法,MC
算法的實現主要分爲三部分:
1,確定包含等值面的體元
首先介紹一下 體元
的概念,體元是三維圖像中由相鄰的八個體素點組成的正方體方格,英語也叫 Cube
,體元中角點函數值分爲兩種情況,一種是大於等於給定等值面的值 C0
,則將角點設爲 1
稱該角點在等值面內部,否則設爲0
,在等值面之外,
一般來說,會出現一個角點在內,一個角點在外,則角點之間的連線(也就是體元的邊)必然與等值面相交,根據這個原理就能判斷等值面與哪些體元相交。
體元內每個角點(頂點)有兩種情況:0和1,一共8個角點即分爲256種( ),根據平面對稱性、中心對稱性,256種最終降到15種
2,確定等值面與體元邊界的交點
找到含有等值面的體元之後,接下來就是確定等值面與體元邊界的交點,體元間的數值都是呈線性變化,求交點時一般採用的是線性插值,如 Case0 中等值面的兩個端點 一個在外爲( 標記0
) ,一個在內 ( 標記爲1
) 則交點爲0.5;
3,求等值面的法向量
以上步驟 1,2,3
爲實現 MC
算法步驟流程,但利用 VTK ,不需要這麼繁瑣,主要算法步驟都已經封裝到 vtkMarchingCube
類中,使用 vtkMarchingCube
時,需要設置三個參數:
SetValue(int i,double value)
設置第i 個等值面的值b
,(提醒一下,醫學圖像中的灰度值範圍不是0-256
而是0-65326
,但大部分取值範圍都在0-1000
)。SetNumberofContours(int number)
,設置等值面的個數ComputerNormalsOn()
設置計算等值面的法向量,提高渲染質量;
上面這張圖顯示的就是 vtk 呈像的基本流程,下面是仿照官網寫的用面繪製來對圖像重建的代碼部分:
#include<vtkRenderWindow.h>
#include<vtkRenderWindowInteractor.h>
#include<vtkDICOMImageReader.h>
#include<vtkMarchingCubes.h>
#include<vtkPolyDataMapper.h>
#include<vtkStripper.h>
#include<vtkActor.h>
#include<vtkProperty.h>
#include<vtkCamera.h>
#include<vtkOutlineFilter.h>
#include<vtkOBJExporter.h>
#include<vtkRenderer.h>
#include<vtkMetaImageReader.h>
#include<vtkInteractorStyleTrackballCamera.h>
#include<iostream>
#include<string.h>
//需要進行初始化,否則會報錯
#include <vtkAutoInit.h>
#include<vtkRenderingVolumeOpenGL2ObjectFactory.h>
#include<vtkRenderingOpenGL2ObjectFactory.h>
using namespace std;
int main()
{
///Marching Cube;
vtkObjectFactory::RegisterFactory(vtkRenderingOpenGL2ObjectFactory::New());
vtkObjectFactory::RegisterFactory(vtkRenderingVolumeOpenGL2ObjectFactory::New());
vtkSmartPointer<vtkRenderer> ren = vtkSmartPointer<vtkRenderer>::New();
vtkSmartPointer<vtkRenderWindow> renWin = vtkSmartPointer<vtkRenderWindow>::New();//WINDOW;
renWin->AddRenderer(ren);
vtkSmartPointer<vtkRenderWindowInteractor> iren = vtkSmartPointer<vtkRenderWindowInteractor>::New();//wininteratcor;
iren->SetRenderWindow(renWin);
vtkSmartPointer<vtkDICOMImageReader> reader = vtkSmartPointer<vtkDICOMImageReader>::New();
reader->SetDirectoryName("E:/DIcom_Data/DICOM");
reader->SetDataByteOrderToLittleEndian();
reader->Update();
/*vtkDICOMImageReader *reader = vtkDICOMImageReader::New();
reader->SetDirectoryName("E:/Coding Pra/VTK/VTK_Examples_StandardFormats_Input_DicomTestImages/DICOM");
reader->SetDataByteOrderToLittleEndian();
reader->Update();*/
cout << "讀取數據完畢" << endl;
cout << "The width is" << reader->GetWidth() << endl;
cout << "The height is" << reader->GetHeight() << endl;
cout << "The depth is" << reader->GetPixelSpacing() << endl;
cout << "The Output port is" << reader->GetOutputPort() << endl;
////////
vtkSmartPointer<vtkMarchingCubes> marchingcube = vtkSmartPointer<vtkMarchingCubes>::New();
marchingcube->SetInputConnection(reader->GetOutputPort());//獲得讀取的數據的點集;
marchingcube->SetValue(0, 200);//Setting the threshold;
marchingcube->ComputeNormalsOn();//計算表面法向量;
vtkSmartPointer<vtkStripper> Stripper = vtkSmartPointer<vtkStripper>::New();
Stripper->SetInputConnection(marchingcube->GetOutputPort());//獲取三角片
vtkSmartPointer<vtkPolyDataMapper> Mapper = vtkSmartPointer<vtkPolyDataMapper>::New();//將三角片映射爲幾何數據;
Mapper->SetInputConnection(Stripper->GetOutputPort());
Mapper->ScalarVisibilityOff();//
vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();//Created a actor;
actor->SetMapper(Mapper);//獲得皮膚幾何數據
actor->GetProperty()->SetDiffuseColor(1, .49, .25);//設置皮膚顏色;
actor->GetProperty()->SetSpecular(0.3);//反射率;
actor->GetProperty()->SetOpacity(1.0);//透明度;
actor->GetProperty()->SetSpecularPower(20);//反射光強度;
actor->GetProperty()->SetColor(1, 0, 0);//設置角的顏色;
actor->GetProperty()->SetRepresentationToWireframe();//線框;
//vtkSmartPointer<vtkCamera> camera = vtkSmartPointer<vtkCamera>::New();//Setting the Camera;
//camera->SetViewUp(0, 0, -1);//設置相機向上方向;
//camera->SetPosition(0, 1, 0);//位置:世界座標系,相機位置;
//camera->SetFocalPoint(0, 0, 0);//焦點,世界座標系,控制相機方向;
//camera->ComputeViewPlaneNormal();//重置視平面方向,基於當前的位置和焦點;
vtkSmartPointer<vtkOutlineFilter> outfilterline = vtkSmartPointer<vtkOutlineFilter>::New();
outfilterline->SetInputConnection(reader->GetOutputPort());
vtkSmartPointer<vtkPolyDataMapper> outmapper = vtkSmartPointer<vtkPolyDataMapper>::New();
outmapper->SetInputConnection(outfilterline->GetOutputPort());
vtkSmartPointer<vtkActor> OutlineActor = vtkSmartPointer<vtkActor>::New();
OutlineActor->SetMapper(outmapper);
OutlineActor->GetProperty()->SetColor(0, 0, 0);//線框顏色
ren->AddActor(actor);
ren->AddActor(OutlineActor);
//ren->SetActiveCamera(camera);//設置渲染器的相機;
ren->ResetCamera();
ren->ResetCameraClippingRange();
//camera->Dolly(1.5);//使用Dolly()方法延伸着視平面法向移動相機;
ren->SetBackground(1, 1, 1);//設置背景顏色;
renWin->SetSize(1000, 600);
vtkInteractorStyleTrackballCamera *style = vtkInteractorStyleTrackballCamera::New();
iren->SetInteractorStyle(style);
renWin->Render();
iren->Initialize();
iren->Start();
vtkSmartPointer<vtkOBJExporter> porter = vtkSmartPointer<vtkOBJExporter>::New();
porter->SetFilePrefix("E:/ceshi/aaa/regist_after/polywrite.obj");//重建圖像輸出
porter->SetInput(renWin);
porter->Write();
return EXIT_SUCCESS;
}
上面就是 VTK 基於 Marching Cube算法
實現的重建效果:
體繪製重建
體繪製時分爲兩部分:
1,定義 vtkVoluemRayCastMapper 對象
體繪製中最常用的方法 ;vtkVolumeRayCastMapper()
光線投影,體繪製時,首先定義一個Mapper
然後接受兩個輸入:
SetInput(vtkImageDate *)
用於設置輸入圖像數據;SetVolumeRayCastFunction(vtkVolumeRayCastFunction *)
用於設置光線投影函數類型;
2,利用 vtkVolumeProperty 定義體繪製屬性;
SetScalarOpacity()
設置灰度不透明函數;SetColor()
顏色傳輸函數;
3, 定義 vtkVolume 對象接收 Mapper對象和 Property 對象
SetMapper()
接受 Mapper 對象;SetProperty()
接受 Property 對象;
vtk 中體繪製 核心就是改變 Mapper
和 vtkVolumeRayCastFunction()
,上面中vtkColumeRayCastMapper
只是 VolumeMapper
其中的一種,且投影函數類 vtkVolumeRayCastFunction
一共有三個子類:
vtkVolumeRayCastCompositeFunction
vtkVolumeRayCasMIPFunction、
vtkVolumeRayCastIsosurfaceFunction
,- 因此,其細分的話vtk中的體繪製也不止一種
而下面這個是最常用到的(``vtkVolumeRayCastMapper+
vtkVolumeRayCastCompositeFunction`)
//體繪製
#include<vtkRenderWindowInteractor.h>
#include<vtkDICOMImageReader.h>
#include<vtkCamera.h>
#include<vtkActor.h>
#include<vtkRenderer.h>
#include<vtkVolumeProperty.h>
#include<vtkProperty.h>
#include<vtkPolyDataNormals.h>
#include<vtkImageShiftScale.h>
#include "vtkVolumeRayCastMapper.h"
#include<vtkPiecewiseFunction.h>
#include<vtkColorTransferFunction.h>
#include<vtkVolumeRayCastCompositeFunction.h>
#include<vtkRenderWindow.h>
#include<vtkImageCast.h>
#include<vtkVolumeRayCastCompositeFunction.h>
#include<vtkOBJExporter.h>
#include<vtkOutlineFilter.h>
#include<vtkPolyDataMapper.h>
#include<vtkInteractorStyleTrackballCamera.h>
#include<vtkRenderingVolumeOpenGL2ObjectFactory.h>
#include<vtkRenderingOpenGL2ObjectFactory.h>
#include<vtkMetaImageReader.h>
#include<vtkLODProp3D.h>
//體繪製加速
//Gpu光照映射
#include<vtkGPUVolumeRayCastMapper.h>
#include<iostream>
int main()
{
vtkObjectFactory::RegisterFactory(vtkRenderingOpenGL2ObjectFactory::New());
vtkObjectFactory::RegisterFactory(vtkRenderingVolumeOpenGL2ObjectFactory::New());
//定義繪製器;
vtkRenderer *aRenderer = vtkRenderer::New();//指向指針;
vtkSmartPointer<vtkRenderWindow> renWin = vtkSmartPointer<vtkRenderWindow>::New();
renWin->AddRenderer(aRenderer);
vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::New();
iren->SetRenderWindow(renWin);
//讀取數據;
/*vtkDICOMImageReader *reader = vtkDICOMImageReader::New();
reader->SetDirectoryName("E:/Coding Pra/VTK/VTK_Examples_StandardFormats_Input_DicomTestImages/DICOM");
reader->SetDataByteOrderToLittleEndian();*/
vtkSmartPointer<vtkDICOMImageReader> reader = vtkSmartPointer<vtkDICOMImageReader>::New();
reader->SetDirectoryName("E:/DIcom_Data/DICOM");
reader->SetDataByteOrderToLittleEndian();
//圖像數據預處理,類型轉換:通過 vtkimageCast 將不同類型數據集轉化爲 vtk 可以處理的數據集;
vtkImageCast *cast_file = vtkImageCast::New();
cast_file->SetInputConnection(reader->GetOutputPort());
cast_file->SetOutputScalarTypeToUnsignedShort();
cast_file->Update();
//透明度映射函數定義;
vtkPiecewiseFunction *opacityTransform = vtkPiecewiseFunction::New();
opacityTransform->AddPoint(0, 0.0);
opacityTransform->AddPoint(20, 0.0);
opacityTransform->AddPoint(200, 1.0);
opacityTransform->AddPoint(300, 1.0);
//顏色映射函數定義,梯度上升的
vtkColorTransferFunction *colorTransformFunction = vtkColorTransferFunction::New();
colorTransformFunction->AddRGBPoint(0.0, 0.0, 0.0, 0.0);
colorTransformFunction->AddRGBPoint(64.0, 0.0, 0.0, 0.0);
colorTransformFunction->AddRGBPoint(128.0, 1.0, 0.0, 0.0);
colorTransformFunction->AddRGBPoint(192.0, 1.0, 0.0, 0.0);
colorTransformFunction->AddRGBPoint(255.0, 1.0, 0.0, 0.0);
vtkPiecewiseFunction *gradientTransform = vtkPiecewiseFunction::New();
gradientTransform->AddPoint(0, 0.0);
gradientTransform->AddPoint(20, 2.0);
gradientTransform->AddPoint(200, 0.1);
gradientTransform->AddPoint(300, 0.1);
//體數據屬性;
vtkVolumeProperty *volumeProperty = vtkVolumeProperty::New();
volumeProperty->SetColor(colorTransformFunction);
volumeProperty->SetScalarOpacity(opacityTransform);
volumeProperty->SetGradientOpacity(gradientTransform);
volumeProperty->ShadeOn();//應用
volumeProperty->SetInterpolationTypeToLinear();//直線間樣條插值;
volumeProperty->SetAmbient(0.4);//環境光係數;
volumeProperty->SetDiffuse(0.6);//漫反射;
volumeProperty->SetSpecular(0.2);
volumeProperty->SetSpecularPower(10);//高光強度;
////計算光照效應;利用 vtkBolumeRayCaseMapper進行計算;
//vtkVolumeRayCastMapper *volunemapper = vtkVolumeRayCastMapper::New();
//vtkVolumeRayCastCompositeFunction *compositeFunction = vtkVolumeRayCastCompositeFunction::New();
//光纖映射類型定義:
vtkSmartPointer<vtkVolumeRayCastCompositeFunction> compositecast =
vtkSmartPointer<vtkVolumeRayCastCompositeFunction>::New();
//Mapper定義,
vtkSmartPointer<vtkVolumeRayCastMapper> hiresMapper =
vtkSmartPointer<vtkVolumeRayCastMapper>::New();
hiresMapper->SetInputData(cast_file->GetOutput());
hiresMapper->SetVolumeRayCastFunction(compositecast);
vtkSmartPointer<vtkLODProp3D> prop = vtkSmartPointer<vtkLODProp3D>::New();
prop->AddLOD(hiresMapper,volumeProperty,0.0);
//
//volunemapper->SetVolumeRayCastFunction(compositeFunction);//載入體繪製方法;
//volunemapper->SetInputConnection(cast_file->GetOutputPort());
//vtkFixedPointVolumeRayCastMapper *fixedPointVolumeMapper = vtkFixedPointVolumeRayCastMapper::New()
//fixedPointVolumeMapper->SetInput()
vtkVolume *volume = vtkVolume::New();
volume->SetMapper(hiresMapper);
volume->SetProperty(volumeProperty);//設置體屬性;
double volumeView[4] = { 0,0,0.5,1 };
vtkOutlineFilter *outlineData = vtkOutlineFilter::New();//線框;
outlineData->SetInputConnection(reader->GetOutputPort());
vtkPolyDataMapper *mapOutline = vtkPolyDataMapper::New();
mapOutline->SetInputConnection(outlineData->GetOutputPort());
vtkActor *outline = vtkActor::New();
outline->SetMapper(mapOutline);
outline->GetProperty()->SetColor(0, 0, 0);//背景純黑色;
aRenderer->AddVolume(volume);
aRenderer->AddActor(outline);
aRenderer->SetBackground(1, 1, 1);
aRenderer->ResetCamera();
//重設相機的剪切範圍;
aRenderer->ResetCameraClippingRange();
renWin->SetSize(800, 800);
renWin->SetWindowName("測試");
vtkRenderWindowInteractor *iren2 = vtkRenderWindowInteractor::New();
iren2->SetRenderWindow(renWin);
//設置相機跟蹤模式
vtkInteractorStyleTrackballCamera *style = vtkInteractorStyleTrackballCamera::New();
iren2->SetInteractorStyle(style);
renWin->Render();
iren2->Initialize();
iren2->Start();
vtkOBJExporter *porter = vtkOBJExporter::New();
porter->SetFilePrefix("E:/ceshi/aaa/regist_after/esho.obj");
porter->SetInput(renWin);
porter->Write();
porter->Update();
return EXIT_SUCCESS;
}
上面是體繪製的結果,相對來說體繪製需要計算資源更大些, vtk 在這方面有所考慮,提供了vtKGPUVolumeRayCastMapper
GUP 加速的光線投射算法。
以上就是本篇文章的全部內容,最後感謝閱讀!
歡迎交流與聯繫,文章首發於公衆號 (Z先生點記)
Reference:
https://blog.csdn.net/wp_veil/article/details/7047537;
https://blog.csdn.net/www_doling_net/article/details/44960713
`