標籤:vtk中文 vtk users guide vtk使用者指南 影像處理 可視化
【譯者:這個系列教程是以Kitware公司出版的《VTK User’s Guide -11th edition》一書作的中文翻譯(出版時間2010年,ISBN: 978-1-930934-23-8),由於時間關係,我們不能保證每周都能更新本書內容,但盡量做到一周更新一篇到兩篇內容。敬請期待^_^。歡迎轉載,另請轉載時註明本文出處,謝謝合作!同時,由於譯者水平有限,出錯之處在所難免,歡迎指出訂正!】
【本節對應原書中的第119頁至第125頁】
第6章影像處理及可視化
映像資料,6-1所示,在拓撲上和幾何上都是規則的,VTK中用vtkImageData表示。規則資料意味著可以用較少的參數如原點,間距和維數就可以隱式定義每個資料點的空間位置。醫學及科學掃描裝置如CT、MRI、超聲儀和共聚焦顯微鏡等都是產生這種類型的資料。概念上講,vtkImageData是由體素(vtkVoxel)或者像素(vtkPixel)單元組成。然而,這種資料的規則特性使得可以通過一個簡單的數組來儲存資料,而不是顯示的定義vtkVoxel或者vtkPixel單元。
圖6-1 vtkImageData結構根據映像的維數、像素間距和原點來定義。維數是每個軸上體素或者像素的個數。原點是第一頁映像左下角點的全局座標。像素間隔則是每個軸上相鄰像素的距離。
在VTK中映像資料是一類比較特殊的資料類型,它可以由多種方式進行處理和渲染。雖然沒有嚴格的定義,VTK中處理映像資料的大部分運算元可以分為三類:影像處理,幾何提取或者是渲染。影像處理Filter資料比較多,他們接收vtkImageData輸入,輸出的結果也是vtkImageData類型。幾何提取Filter是將vtkImageData轉換為vtkPolyData類型。例如,vtkContourFilter從映像資料中提取由三角面片組成的等值面。最後還有許多不同的Mapper和專門的Actor來渲染vtkImageData,從簡單的二維映像渲染到體繪製。
本章中我們主要學習一些重要的影像處理技術。我們將討論基本的映像顯示、處理和高程圖幾何提取技術。其他的幾何提取技術如等值面已在第5章中介紹。vtkImageData和vtkUnstructuredGrid資料的體繪製技術將在第7章中講解。
6.1 手動建立vtkImageData
手動建立映像資料非常簡單,只需要定義映像的維數,原點和間距。原點是映像左下角點的全局座標系位置。維數是沿著三個主軸方向上體素或者像素的個數。間距則是體素的長、寬和高,或者是每個方向上相鄰像素相鄰像素的距離,這將區別與你將映像像素看做是同類的盒子或者是連續函數的採樣點。
在第一個例子中我們假設一個包含size[0]*size[1]*size[2]個元素的數組“data”。該資料在VTK外部產生,現在我們需要將其讀入到vtkImageData資料中以便用VTK Filter進行處理或者渲染操作。由於我們只是將資料記憶體指標傳到VTK中,因此需要我們自己控制資料記憶體的釋放。
vtkUnsignedCharArray*array = vtkUnsignedCharArray::New();
array->SetArray(data,size[0]*size[1]*size[2], 1);
接下來建立映像。我們必須保證各個數值的匹配-資料類型必須是標量類型,而且資料長度必須與映像的維數一致。
imageData= vtkImageData::New();
imageData->GetPointData()->SetScalars(array);
imageData->SetDimensions(size);
imageData->SetScalarType(VTK_UNSIGNED_CHAR);
imageData->SetSpacing(1.0,1.0, 1.0);
imageData->Set Origin(0.0, 0.0, 0.0);
由於映像維數、原點和間距隱式的定義映像資料的幾何和拓撲結構,因此表示結構的資料存放區需求就非常小。另外,由於資料的規則分布也使得該結構上的計算比較快速。真正需要記憶體的則是資料集上的屬性資料。
下面例子中,我們將用C++語言來建立映像。這次不是直接建立資料數組並將其指定到一個映像中,而是用vtkImageData對象自動建立標量資料。這也將排除掉資料數組的大小與映像維數不一致的問題。
//Createthe image data
vtkImageData*id = vtkImageData::New();
id->SetDimensions(10,25, 100);
id->SetScalarTypeToUnsignedShort();
id->SetNumberOfScalarComponents(1);
id->AllocateScalars();
//Fillin scalar values
Unsignedshort *ptr = (unsigned shor*)id->GetScalarPointer();
for(int i=0; i<10*25*100; i++)
{
*ptr ++= i;
}
在這個例子中AllocateScalars()方法用來為映像分配記憶體。需要注意的是,這個方法調用之前,必須先設定標量類型(scalar type)和標量成分的個數(最多4個標量成分)。然後GetScalarPointer()方法,並將其返回的void*結果強制轉換成unsigned short類型。我們只能在已知資料類型為unsigned short時才能這樣做。RequestData()函數查詢純量資料型別,然後然後根據類型選擇模板函數來實現。VTK在設計時就盡量避免在公有介面中暴露標量類型為模板參數。這樣就為那些缺少模板的語言如Tcl,Java和Python等提供了一個簡單的介面函數。
6.2映像降採樣
如103頁“提取單元子集”,提取部分資料是常見的需求。vtkExtractVOI可以提取輸入映像的部分資料。這個Filter也可以對映像降採樣,雖然vtkImageReslice(後面會介紹)能夠更方便的對資料重採樣。其輸出類型是vtkImageData。
VTK中有兩個類似的Filter來執行裁剪功能:vtkExtractVOI和vtkImageClip。之所以分為兩個Filter,是由於曆史原因——映像管線與圖形管線分開,在映像管線中vtkImageClip只處理vtkImageData資料,而在圖形管線中vtkExtractVOI只處理vtkStructuredPoints資料。現在這些區別已經沒有了,但是這兩個Filter之間還有一些不同。vtkExtractVOI提取一個體資料的子領域,將其輸出為一個vtkImageData。另外,vtkExtractVOI也可以在感興趣地區VOI中進行重採樣。另一方面,vtkImageClip預設情況下會保持輸出映像資料和輸入一致,除了映像範圍資訊。可以通過設定該filter的一個標誌來強制產生精確地資料量,這種情況下對應地區也會直接拷貝到輸出vktImageData中。vtkImageClip不能重採樣映像。
下面Tcl執行個體(取自VTK/Examples/ImageProcessing/Tcl/Contours2D.tcl)說明了怎麼樣使用vtkExtractVOI。它提取輸入映像的局部地區資料,然後對其重採樣。輸出資料再傳遞給vtkContourFilter。(你也許會像去掉vtkExtractVOI再比較結果。)
vtkQuadric quadric
quadricSetCoefficients .5 1 .2 0 .1 0 0 .2 0 0
vtkSampleFunction sample
sampleSetSampleDimensions 30 30 30
sampleSetImplicitFunction quadric
sampleComputeNormalsOff
vtkExtractVOI extract
extractSetInputConnection [sample GetOutputPort]
extractSetVOI 0 29 0 29 15 15
extractSetSampleRate 1 2 3
vtkContourFilter contours
contoursSetInputConnection [extract GetOutputPort]
contoursGenerateValues 13 0.0 1.2
vtkPolyDataMapper contMapper
contMapperSetInputConnection [contours GetOutputPort]
contMapperSetScalarRange 0.0 1.2
vtkActor contActor
contActorSetMapper contMapper
注意上面代碼中通過指定一個感興趣地區(0,29,0,29,15,15)來提取未經處理資料中的一個平面,而沿三個軸方向的採樣率也是設定為不同的值。通過指定VOI大小,可以從資料中提取一個地區,甚至一條線或者一個點(VOI設定採用0位移數值)。
6.3
基於標量值的變形
映像資料的一個常見應用是儲存高程值。這種映像常為稱為範圍圖或者高程圖。映像中每個像素的標量值表示一個高程值或者範圍值。而可視化中一個常見的目的就是將這種映像顯示成為一個精確地三維高程表示。圖6-2中顯示了一個根據高程值顯示的映像。左邊映像為原始映像,右邊映像則是由原始映像產生的三維曲面。
圖6-2 Image warped by scalar values
高程圖可視化管線比較簡單,但是有一個重要的概念需要理解。原始映像中隱藏了幾何和拓撲結構。而映像經Warpping操作後會產生一個三維幾何曲面。為了支援該操作,我們首先利用vtkImageDataGeometryFilter將映像轉換成vtkPolyData類型,然後執行Warp操作並串連到mapper上。下面指令碼中你會注意到我們用到了vtkWindowLevelLookupTable來提供一個灰階顏色尋找表,取代預設的紅到藍顏色尋找表。
vtkImageReader reader
readerSetDataByteOrderToLittleEndian
readerSetDataExtent 0 63 0 63 40 40
readerSetFilePrefix “$VTK_DATA_ROOT/Data/headsq/quarter”
readerSetDataMask 0x7fff
vtkImageDataGeometryFilter geometry
geometrySetInputConnection [reader GetOutputPort]
vtkWarpScalar warp
warpSetInputConnection [geometry GetOutputPort]
warpSetScalarFactor 0.005
vtkWindowLevelLookupTable wl
vtkPolyDataMapper mapper
mapperSetInputConnection [warp GetOutputPort]
mapperSetScalarRange 0 2000
mapperImmediateModeRenderingOff
mapperSetLookupTable wl
vtkActor actor
actorSetMapper mapper
這個例子經常會結合其他技術使用。如果你想使用映像的標量值進行Warp,然後使用其他的標量場對其進行著色。另一個常用的操作是對產生的曲面消減多邊形個數。因為由映像產生的結果往往含有大量的多邊形面片。可以使用vtkDecimatePro來消減面片數量。另外還可以考慮使用vtkTriangleFilter和vtkStripper來將多邊形網格轉換為三角形網格,這樣可以提高渲染速度,減少記憶體佔用。
6.4映像顯示
VTK中顯示映像有多種方式,本節中將介紹兩種常用的方式。體繪製技術用來直接繪製三維映像,其細節將在第7章中介紹。
映像瀏覽器
vtkImageViewer2類取代了早期版本的vtkImageViewer,可以方便的顯示映像。vtkImageViewer2類內部包含了vtkRenderWindow,vtkRenderer, vtkImageActor和vtkImageMapToWindowLevelColors對象,可以方便的在使用者應用程式中調用。這個類也根據映像來執行個體化一個互動器類型(vtkInteractorStypeImage),提供了映像放縮、展開和互動窗寬/窗位調節(參考43頁“互動器類型”和283頁“vtkRenderWindow互動器類型”)。vtkImageViewer2(與vtkImageViewer不同)採用的是3D渲染和紋理映射技術將映像繪製到平面上,從而方便了快速渲染,放縮和展開。根據特定的映像切片所在的深度座標,映像顯示在三維空間中。每次調用SetSlice()函數都會更改顯示的映像切片和對應的三維空間深度。該功能通過InteractorStyle中的AutoAdjustCameraClippingRange選項來控制。你還可以設定方向來顯示XY,YZ或者XZ方向切片。
利用映像瀏覽器來顯示三維映像切片的例子可以參考Widgets/Testing/CXX/TestingImageActorContourWidget.cxx。下面代碼示範了該類的一個典型使用方法。
vtkImageViewer2 *ImageViewer =vtkImageViewer2::New();
ImageViewer->SetInput(shifter->GetOutput());
ImageViewer->SetColorLevel(127);
ImageViewer->SetColorWindow(255);
ImageViewer->SetupInteractor(iren);
ImageViewer->SetSlice(40);
ImageViewer->SetOrientationToXY();
ImageViewer->Render();
該類也支援映像和幾何圖形的混合顯示,例如:
viewer->SetInput( myImage );
viewer->GetRenderer()->AddActor( myActor );
這樣可以用一些邊來注釋映像或者高亮顯示部分映像或者同時顯示映像的一個切片和等值面等等。所有在當前顯示的映像切片前面的幾何圖形都是可見的,而後面的部分則被遮擋。
Window-level傳輸函數定義6-3。Level值是位於window中央的值。窗寬(window)則是用來映射顯示的資料範圍。傳輸函數的斜率決定了最終映像的對比。所有位於window以外的資料都會截取到windows邊界值。
圖6-3 窗寬窗位傳輸函數
映像Actor
當你想在一個視窗中顯示映像以及一些簡單的2D注釋時,可以使用vtkImageViewer類。而當你想在3D渲染視窗中顯示映像時,需要使用的是vtkImageActor。建立一個多邊形來表示映像邊界,然後通過紋理映射將映像複製到多邊形上來顯示顯示映像。在大部分的平台下,在即時進行旋轉、展開和放縮映像時需要對映像進行雙線性插值。如果將其互動器替換為一個vtkInteractorStyleImage類型時,旋轉操作就可以禁用,這樣三維渲染視窗操作就如同二維映像瀏覽器。
vtkImageActor是一個包含Actor和Mapper對象的合成類。它的使用非常的簡單,如下例所示。
vtkBMPReader bmpReader
bmpReader SetFileName “$VTK_DATA_ROOT/Data/masonry.bmp”
vtkImageActor imageActor
imageActor SetInput [ bmpReader GetOutput ]
映像Actor可以通過AddProp()函數添加至renderer中。vtkImageActor類期望輸入的映像在一個方向上長度為1,而在其他兩個方向上擴充。這樣如果裁剪操作是沿著X或者Y軸時,就不需要重新組織資料,從而使vtkImageActor通過一個裁剪Filter與一個Volume串連。
vtkImagePlaneWidget
Widget會在255頁“Interaction,widgets and Selections”中講述。這個widget可以互動地放置到映像中,並通過該平面來顯示重建的映像切片。產生切片資料時採用的插值選項包括最近鄰、線性和立方插值。平面的位置和方向可以進行互動控制。當然也可以互動地控制新切片的窗寬窗位,並且可以選擇性的顯示窗寬窗位和位置注釋。
vtkImagePlaneWidget* planeWidgetX =vtkImagePlaneWidget::New();
planeWidgetX->SetInteractor(iren);
planeWidgetX->RestrictPlaneToVolumeOn();
planeWidgetX->SetResliceInterpolateToNearestNeighbour();
planeWidgetX->SetInput(v16->GetOutput());
planeWidgetX->SetPlaneOrientationToXAxes();
planeWidgetX->SetSliceIndex(32);
planeWidgetX->DisplayTextOn();
planeWidgetX->On();