5、VTK在图像处理中的应用
5、VTK在圖像處理中的應(yīng)用
圖像是VTK中一個(gè)非常重要的數(shù)據(jù)。數(shù)字圖像廣泛應(yīng)用于工業(yè)生產(chǎn)、生物醫(yī)學(xué)、媒體娛樂、地質(zhì)、氣象等重要領(lǐng)域,數(shù)字圖像處理具有重要的應(yīng)用價(jià)值。我們?cè)谡莆樟薞TK的基本知識(shí)后,這一章著重講解數(shù)字圖像處理相關(guān)技術(shù),學(xué)完本章你會(huì)覺得原來圖像處理是如此簡(jiǎn)單!
5.1 VTK圖像數(shù)據(jù)結(jié)構(gòu)
數(shù)字圖像文件內(nèi)容由兩個(gè)部分組成:圖像頭信息和數(shù)據(jù)。圖像頭信息定義了圖像的基本信息,主要包括起點(diǎn)位置(Origin),像素間隔(space)和維數(shù)(dimension)。通過這三個(gè)參數(shù)即可以決定圖像空間位置和規(guī)模。圖像可以看做是空間中的一個(gè)規(guī)則網(wǎng)格,網(wǎng)格中的每個(gè)最小單元稱之為一個(gè)像素(二維)或者體素(三維),這樣網(wǎng)格在每個(gè)方向上的像素或者體素個(gè)數(shù)即為圖像在該方向的維數(shù)。像素索引表示每個(gè)像素在圖像網(wǎng)格中的位置,是圖像內(nèi)部的網(wǎng)格坐標(biāo)。而在醫(yī)學(xué)圖像中,每個(gè)圖像除了內(nèi)部坐標(biāo)外,還存在一個(gè)世界坐標(biāo)。這個(gè)世界坐標(biāo)依賴于成像設(shè)備。在醫(yī)學(xué)圖像中起點(diǎn)位置(Origin),像素間隔(space)和圖像維數(shù)決定了世界坐標(biāo)系。這樣通過起點(diǎn)位置,像素間隔和像素索引即可計(jì)算每個(gè)像素的世界坐標(biāo)位置。
圖5.1圖像結(jié)構(gòu)和空間表示
如圖5.1所示,該圖表示一個(gè)4x2x3的圖像,即圖像的維數(shù),每一個(gè)小球表示一個(gè)像素;而圖像的原點(diǎn)為(5.1,10.0,6.5),兩兩像素之間的間隔表示像素間隔,每個(gè)方向的像素間隔為1.5,1.5,和1.8。
圖像數(shù)據(jù)即為圖像像素的像素值,一般采用一維數(shù)組來表示和存儲(chǔ)。已知像素索引和圖像維數(shù)下,即可計(jì)算每個(gè)像素對(duì)應(yīng)的像素值。通常圖像的像素值為一個(gè)標(biāo)量,例如一般灰度圖像;圖像像素值也可以是一個(gè)向量,例如彩色圖像;另外圖像像素值還可以是張量,如梯度場(chǎng)圖像。醫(yī)學(xué)圖像處理中大部分的圖像都是灰度圖像。
這里需要注意灰度圖像的灰度值的數(shù)據(jù)類型,在一般的灰度圖像處理中不需要考慮,因?yàn)槠浞秶J(rèn)為0-255,可以采用一個(gè)unsigned char類型類表示。但是在醫(yī)學(xué)圖像處理中,256灰度級(jí)遠(yuǎn)遠(yuǎn)不能滿足要求,因此灰度范圍往往大于256級(jí)。常見的醫(yī)學(xué)圖像的像素?cái)?shù)據(jù)類型為unsigned short,灰度范圍為0-65536。另外,有時(shí)為了精度的考慮,也會(huì)使用int、float甚至double類型,因此需要格外注意。
通過前面的章節(jié)我們已經(jīng)知道,在VTK中圖像數(shù)據(jù)結(jié)構(gòu)由vtkImageData類表示。利用vtkImageData,我們可以方便的創(chuàng)建、讀寫、和訪問圖像數(shù)據(jù)。下面我們以VTK圖像創(chuàng)建為起點(diǎn),一步步走進(jìn)VTK圖像處理的世界中來。
5.2 VTK圖像創(chuàng)建
5.2.1圖像源(Source)
VTK中內(nèi)置了多個(gè)創(chuàng)建圖像的Source,利用這些Source可以快速的創(chuàng)建圖像,其中以vtkImageCanvasSource2D為代表。該Source功能是創(chuàng)建一個(gè)畫布(空白圖像),并提供了多種幾何圖形(點(diǎn)、線段、圓、矩形以及圖像等)的繪制填充功能。下列代碼顯示了該source的使用方法。
1: vtkSmartPointer canvas =
2: vtkSmartPointer::New();
3: canvas->SetScalarTypeToUnsignedChar();
4: canvas->SetNumberOfScalarComponents(1);
5: canvas->SetExtent(0, 100,0, 100, 0, 0);
6:
7: canvas->SetDrawColor(0, 0,0, 0);
8: canvas->FillBox(0,100,0,100);
9:
10: canvas->SetDrawColor(255,0, 0, 0);
11: canvas->FillBox(20,40,20,40);
12: canvas->Update();
在上面代碼片段中,首先定義了一個(gè)vtkImageCanvasSource2D的指針,然后設(shè)置畫布的像素?cái)?shù)據(jù)類型,像素成分?jǐn)?shù)目和畫布的大小。然后,在該畫布中,利用FillBox繪制兩個(gè)填充矩形,并通過SetDrawColor()來設(shè)置兩個(gè)矩形的顏色。
圖5.2 vtkImageCanvasSource2D繪圖
除了vtkImageCanvasSource2D外,VTK中還提供了其他類似的source類來快速生成特定的圖像,例如vtkImageEllipsoidSource,該類根據(jù)指定的中心,各個(gè)軸的半徑來生成一個(gè)前景為橢圓(球)的二值圖像;vtkImageGaussianSource類生成一副像素值服從高斯分布的圖像;vtkImageGridSource用于生成網(wǎng)格線圖像;vtkImageNoiseSource生成一個(gè)像素值為隨機(jī)數(shù)的噪聲圖像;vtkImageSinusoidSource生成的圖像像素值由正弦函數(shù)決定。
5.2.2直接創(chuàng)建圖像
利用上述圖像Source可以快速的生成特定的圖像,不過相對(duì)來說,在實(shí)際應(yīng)用中較少用到他們。VTK中可以手動(dòng)生成圖像,然后根據(jù)需要進(jìn)行像素賦值。下面代碼演示了怎樣手動(dòng)創(chuàng)建圖像。
1: vtkSmartPointer img= vtkSmartPointer::New();
2: img->SetDimensions(10,10,10);
3: img->SetScalarTypeToUnsignedChar();
4: img->SetNumberOfScalarComponents(1);
5: img->AllocateScalars();
6:
7: unsigned char ptr = (unsignedchar)img->GetScalarPointer();
8: for(int i=0; i<10*10*10; i++)
9: {
10: *ptr ++ =i%256;
11: }
首先定義vtkImageData指針,然后指定圖像的維數(shù),而圖像的原點(diǎn)和像素間隔則都是采用默認(rèn)值,因此不需要設(shè)置。SetScalarTypeToUnsignedChar指定圖像的每個(gè)像素值的數(shù)據(jù)類型為unsigned char,SetNumberOfScalarComponents則指定了每個(gè)像素值的數(shù)據(jù)成分為1,每個(gè)像素值為1個(gè)標(biāo)量值,參數(shù)設(shè)置完畢后,調(diào)用AllocateScalars()分配內(nèi)存,生成圖像數(shù)據(jù)。圖像生成后,默認(rèn)所有像素值為0??梢酝ㄟ^訪問圖像數(shù)據(jù)數(shù)組來設(shè)置每個(gè)像素值,GetScalarPointer()即返回圖像的數(shù)據(jù)數(shù)組(圖像數(shù)據(jù)數(shù)組都采用一維數(shù)組),然后根據(jù)圖像的大小,訪問每個(gè)像素并為其賦值。生成的圖像結(jié)果如圖5.3所示。
圖5.3 用VTK的類直接創(chuàng)建圖像數(shù)據(jù)
5.3 圖像顯示
5.3.1vtkImageViewer2
在VTK早期版本中,提供了vtkImageViewer類來顯示圖像。隨著版本的發(fā)展,目前使用vtkImageViewer2來代替vtkImageViewer實(shí)現(xiàn)圖像的顯示。vtkImageViewer2中封裝了VTK圖像顯示的管線,包括vtkActor,vtkRender,vtkRenderWindow,vtkInteractorStypeImage等對(duì)象,可以方便的完成圖像顯示和交互。該類提供的主要交互操作有:圖像放縮,窗寬窗位調(diào)節(jié),并提供切片選擇,切片方向設(shè)置接口,尤其適合三維圖像的顯示。下面代碼說明了怎樣使用vtkImageViewer2顯示圖像。
1: vtkSmartPointerreader =
2: vtkSmartPointer::New();
3: reader->SetFileName (“…\brain.mhd” );
4: reader->Update();
5:
6: vtkSmartPointer imageViewer =
7: vtkSmartPointer::New();
8: imageViewer->SetInputConnection(reader->GetOutputPort());
9: vtkSmartPointerrenderWindowInteractor =
10: vtkSmartPointer::New();
11: imageViewer->SetupInteractor(renderWindowInteractor);
12:
13: imageViewer->SetColorLevel(500);
14: imageViewer->SetColorWindow(2000);
15: imageViewer->SetSlice(40);
16: imageViewer->SetSliceOrientationToXY();
17: imageViewer->Render();
18:
19: renderWindowInteractor->Start();
這里為了更好的說明vtkImageViewer2功能,使用一副三維醫(yī)學(xué)圖像為例進(jìn)行說明。首先使用vtkMetaImageReader讀入一個(gè)mhd圖像,然后定義一個(gè)vtkImageViewer2對(duì)象顯示圖像。第9和10行定義了一個(gè)vtkRenderWindowInteractor對(duì)象,并傳遞給vtkImageViewer2對(duì)象,用于完成鼠標(biāo)、鍵盤等消息響應(yīng),便于進(jìn)行圖像的交互操作。接下來分別設(shè)置了四個(gè)參數(shù),窗位(ColorLevel)、窗寬(ColorWindow)、切片(Slice)和切片方向(Orientation)。設(shè)置完畢后,運(yùn)行程序,圖像顯示如下。另外,按下鼠標(biāo)左鍵拖動(dòng)鼠標(biāo),可以調(diào)節(jié)圖像的窗寬窗位,從而顯示不同灰度范圍內(nèi)容;按下鼠標(biāo)右鍵拖動(dòng)鼠標(biāo)可以放縮圖像。當(dāng)然這些交互操作可以由用戶根據(jù)需要自己定義vtkInteractorStyle子類,并響應(yīng)相應(yīng)的操作。
窗寬是CT圖像上顯示的CT值范圍。一般顯示器的灰度范圍為256級(jí),而X光圖像的灰度范圍則遠(yuǎn)遠(yuǎn)大于該范圍,因此通過顯示器顯示時(shí)不能顯示所有灰度級(jí)。因此需要窗寬來定義需要顯示的灰度范圍。當(dāng)灰度值高于該范圍的最大值時(shí),均以白影顯示;而低于該范圍時(shí)均顯示為黑色。增大窗寬,顯示具有不同灰度值的組織結(jié)構(gòu)增多,但是會(huì)降低組織之間的對(duì)比度。減小窗寬,則可視的不同灰度組織結(jié)構(gòu)會(huì)減少,同時(shí)增大組織結(jié)構(gòu)的對(duì)比度。
窗位是窗的中心位置。窗寬只是確定了CT圖像灰度范圍上的可視部分范圍,還需要窗位來確定可視灰度范圍的具體位置。同樣的窗寬,會(huì)根據(jù)窗位的位置變化來顯示不同的組織結(jié)構(gòu)。比如,窗寬為200時(shí),當(dāng)窗位為100時(shí),可視灰度范圍為0至200;當(dāng)窗位為500時(shí),則可視灰度范圍為400至600。當(dāng)窗寬窗位確定以后,顯示時(shí)底層會(huì)將可視灰度范圍轉(zhuǎn)換到256灰度級(jí)進(jìn)行顯示(參考圖5.4)。
圖5.4醫(yī)學(xué)圖像窗寬窗位示意圖
而顯示三維圖像時(shí),需要確定當(dāng)前顯示切片和方向。vtkImageViewer2提供了SetSlice()函數(shù)設(shè)置切片號(hào),SetSliceOrientationToXY()則將切片的方向設(shè)置為垂直XY平面方向。此外還可以設(shè)置為垂直YZ或者XZ平面方向,其對(duì)應(yīng)函數(shù)分別為SetSliceOrientationToYZ()和SetSliceOrientationToXZ()。默認(rèn)情況下切片方向?yàn)榇怪庇赬Y平面即沿著Z軸方向,根據(jù)設(shè)置的切片號(hào)獲取Z軸方向的具體切片進(jìn)行顯示。
切片(slice)與切面是三維圖像比較常用的概念。尤其是在醫(yī)學(xué)圖像中,不同方向的切面都有特定的名字。矢狀面(sagital)是沿著身體前后徑所做的與地面垂直的切面;冠狀面(coronal)是沿著身體左右徑所做的與地面垂直的切面;而橫斷面(transverse/axial)是指橫斷身體與地面平行的切面。(圖5.5所示)
圖5.5 矢狀面、冠狀面和橫斷面示意圖
設(shè)置切片的方向即是通過不同的方向來觀察人體內(nèi)部組織結(jié)構(gòu)。因此,常見的醫(yī)學(xué)圖像可視化軟件常常提供四個(gè)視圖用來顯示圖像:橫斷面視圖,矢狀面視圖,冠狀面視圖和三維視圖。
5.3.2vtkImageActor
vtkImageActor是一個(gè)三維圖像渲染Actor,通過紋理映射將圖像映射到一個(gè)多邊形上進(jìn)行顯示。使用vtkImageActor較vtkImageViewer2要復(fù)雜一些,需要建立完整的渲染管線:包括vtkImageActor,vtkRender,vtkRenderWindow,vtkRenderWindowInteractor管線。另外,作為圖像二維瀏覽器,不需要在三維空間中進(jìn)行旋轉(zhuǎn)操作,因此還需要為vtkRenderWindow定義一個(gè)vtkInteractorStyleImage對(duì)象。下面給出vtkImageActor顯示圖像的示例(結(jié)果如圖5.6所示)。
1: vtkSmartPointer reader=
2: vtkSmartPointer::New();
3: reader->SetFileName (“…\lena.bmp” );
4: reader->Update();
5:
6: vtkSmartPointerimgActor =
7: vtkSmartPointer::New();
8: imgActor->SetInput(reader->GetOutput());
9:
10: vtkSmartPointer renderer =
11: vtkSmartPointer::New();
12: renderer->AddActor(imgActor);
13: renderer->SetBackground(.4, .5, .6);
14:
15: vtkSmartPointer renderWindow =
16: vtkSmartPointer::New();
17: renderWindow->SetSize(500, 500);
18: renderWindow->AddRenderer(renderer);
19:
20: vtkSmartPointer renderWindowInteractor=
21: vtkSmartPointer::New();
22: vtkSmartPointer style =
23: vtkSmartPointer::New();
24:
25: renderWindowInteractor->SetInteractorStyle(style);
26: renderWindowInteractor->SetRenderWindow(renderWindow);
27: renderWindowInteractor->Initialize();
28:
29: renderWindowInteractor->Start();
上面代碼中在讀入圖像后,依次建立vtkImageActor,vtkRender,vtkRenderWindow,vtkRenderWindowInteractor,并組裝為管線。為了屏蔽旋轉(zhuǎn)操作,建立vtkInteractorStyleImage對(duì)象,并通過renderWindowInteractor->SetInteractorStyle(style)設(shè)置交互對(duì)象。需要注意的是,vtkImageActor接收的圖像vtkImageData數(shù)據(jù)類型必須為unsigned char類型,因此在顯示之前,需要利用vtkImageCast將圖像數(shù)據(jù)類型轉(zhuǎn)換為unsigned char。
圖5.6 使用vtkImageActor顯示圖像數(shù)據(jù)
5.3.3圖像融合
上面兩種方法都是在一個(gè)窗口中顯示一個(gè)圖像。但是在常見的醫(yī)學(xué)處理軟件中,經(jīng)常會(huì)遇到在一個(gè)窗口中顯示多個(gè)圖像,這就會(huì)用到圖像融合技術(shù)。圖像融合是利用圖像的alpha通道和不透明度來實(shí)現(xiàn)。VTK中vtkImageBlend實(shí)現(xiàn)圖像的融合。
vtkImageBlend可以接收多個(gè)圖像輸入,輸出為融合圖像。輸出圖像的像素間隔、原點(diǎn)、范圍(extent)以及像素組分個(gè)數(shù)與第一個(gè)圖像一致。該類提供了兩種融合模式,第一種是標(biāo)準(zhǔn)模式,也是默認(rèn)的融合方式。其計(jì)算公式如下:
1:output<- input[0]
2:foreachinput i {
3:foreachpixel px {
4: r <- inputi(alpha)* opacity[i]
5: f <- (255 - r)
6: output(px) <- output(px) * f + input(px) *r
7:}
8:}
第二種是混合模式(Compound)。該模式下輸出結(jié)果經(jīng)過alpha/opacity不透明度的和做過歸一化。另外還可以設(shè)置一個(gè)閾值,當(dāng)alpha*opacity小于等于該閾值時(shí)會(huì)忽略該像素。其計(jì)算公式如下:
1:output<- 0
2:foreachpixel px {
3: sum <- 0
4: foreach input i {
5: r <- inputi(alpha)* opacity(i)
6: sum <- sum + r
7: if r > threshold {
8: output(px) <- output(px) + input(px) *r
9: }
10:}
11:output(px) <- output(px) / sum
12:}
下面代碼說明了怎么融合圖像并顯示。代碼中讀入了一副灰度圖像,并生成了一個(gè)二值圖像;然后定義了vtkImageBlend對(duì)象,函數(shù)SetInput()設(shè)置兩個(gè)圖像作為輸入。這里設(shè)置輸入圖像時(shí),由于可以輸入多個(gè)圖像,因此需要給定圖像的id號(hào)來設(shè)置輸入。SetOpacity()用于設(shè)置對(duì)應(yīng)id號(hào)的圖像不透明度的大小,當(dāng)不透明度為1.0時(shí),為完全不透明。程序的執(zhí)行結(jié)果如圖5.7所示。
1: vtkSmartPointerreader =
2: vtkSmartPointer::New();
3: reader->SetFileName (“..\lena2.jpg” );
4: reader->Update();
5:
6: vtkSmartPointer imageSource =
7: vtkSmartPointer::New();
8: imageSource->SetNumberOfScalarComponents(1);
9: imageSource->SetScalarTypeToUnsignedChar();
10: imageSource->SetExtent(0, 512, 0, 512, 0, 0);
11: imageSource->SetDrawColor(0.0);
12: imageSource->FillBox(0, 512, 0, 512);
13: imageSource->SetDrawColor(255.0);
14: imageSource->FillBox(100,400,100,400);
15: imageSource->Update();
16:
17: vtkSmartPointer imageBlend =
18: vtkSmartPointer::New();
19: imageBlend->SetInput(0, reader->GetOutput());
20: imageBlend->SetInput(1, imageSource->GetOutput());
21: imageBlend->SetOpacity(0, 0.4);
22: imageBlend->SetOpacity(1, 0.6);
23: imageBlend->Update();
圖5.7 VTK圖像整合效果
==========歡迎轉(zhuǎn)載,轉(zhuǎn)載時(shí)請(qǐng)保留該聲明信息==========
版權(quán)歸@東靈工作室所有,更多信息請(qǐng)?jiān)L問東靈工作室
教程系列導(dǎo)航:http://blog.csdn.net/www_doling_net/article/details/8763686
總結(jié)
以上是生活随笔為你收集整理的5、VTK在图像处理中的应用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C 中的static关键字
- 下一篇: vtkImageData基本操作