5.4 vtkImageData基本操做
圖像處理離不開一些基本的圖像數據操做,例如獲取和修改圖像的基本信息,訪問和修改圖像像素值,圖像顯示,圖像類型轉換等等。熟練掌握這些基本操做有助於使用VTK進行圖像處理的快速開發。數組
vtkImageData中提供了多個函數用於訪問或者獲取圖像的基本信息,這些函數一般使用Set或者Get加上相應的信息名的形式,例如獲取圖像維數的方法定義爲GetDimensions()。固然這裏主要從類的層次上進行VTK的學習,這裏再也不具體贅述每一個函數的基本名稱和使用,用戶能夠查閱相應的類文檔。下面經過一個例子來講明怎樣訪問圖像的基本信息。程序運行如圖5.8所示。ide
1: vtkSmartPointer<vtkBMPReader>reader =函數
2: vtkSmartPointer<vtkBMPReader>::New();學習
3: reader->SetFileName ( "..\\lena.bmp");spa
4: reader->Update();.net
5: 指針
6: int dims[3];orm
7: reader->GetOutput()->GetDimensions(dims);blog
8: std::cout<<"圖像維數:"<<dims[0]<<" "<<dims[1]<<""<<dims[2]<<std::endl;教程
9:
10: double origin[3];
11: reader->GetOutput()->GetOrigin(origin);
12: std::cout<<"圖像原點:"<<origin[0]<<" "<<origin[1]<<""<<origin[2]<<std::endl;
13:
14: double spaceing[3];
15: reader->GetOutput()->GetSpacing(spaceing);
16: std::cout<<"像素間隔:"<<spaceing[0]<<" "<<spaceing[1]<<""<<spaceing[2]<<std::endl;
17:
18:
19: vtkSmartPointer<vtkImageViewer2>p_w_picpathViewer =
20: vtkSmartPointer<vtkImageViewer2>::New();
21: p_w_picpathViewer->SetInputConnection(reader->GetOutputPort());
22: vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor=
23: vtkSmartPointer<vtkRenderWindowInteractor>::New();
24: p_w_picpathViewer->SetupInteractor(renderWindowInteractor);
25: p_w_picpathViewer->Render();
26: p_w_picpathViewer->GetRenderer()->ResetCamera();
27: p_w_picpathViewer->Render();
28:
29: renderWindowInteractor->Start();
圖5.8 VTK圖像基本信息獲取
上例中主要獲取了圖像的三個信息,圖像維數,圖像原點和像素間隔。VTK中二維和三維圖像都用vtkImageData表示,所以第六行中定義圖像維數爲dims[3],而後利用GetDimensions()函數獲取圖像的維數;圖像的原點和像素間隔都是物理空間數值,所以都是定義double類型。本例讀入了二維lena圖像,上圖中顯示了獲取的圖像信息。其中,圖像維數爲512*512*1,經過維數能夠當作z方向的維數爲1,說明該圖像爲二維圖像;而圖像的原點爲(0,0,0)點,而像素間隔爲(1,1,1)。
vtkChangeImageInformation
vtkImageData中提供了多個Set函數用於設置圖像的基本信息。當對一個管線的輸出修改圖像信息後,若是管線從新Update,那麼這些修改都會恢復回原來的值。而vtkChangeImageInformation能夠做爲管線中的一個filter來修改圖像信息。利用這個filter能夠修改圖像的原點,像素間隔,以及範圍起點(extent),另外還能夠對圖像平移縮放等操做。下面代碼說明了怎樣修改圖像的原點,像素間隔。
1: vtkSmartPointer<vtkBMPReader> reader=
2: vtkSmartPointer<vtkBMPReader>::New();
3: reader->SetFileName ( "..\\lena.bmp" );
4: reader->Update();
5:
6: int dims[3];
7: double origin[3];
8: double spaceing[3];
9:
10: reader->GetOutput()->GetDimensions(dims);
11: std::cout<<"原圖像維數:"<<dims[0]<<" "<<dims[1]<<""<<dims[2]<<std::endl;
12: reader->GetOutput()->GetOrigin(origin);
13: std::cout<<"原圖像原點:"<<origin[0]<<" "<<origin[1]<<""<<origin[2]<<std::endl;
14: reader->GetOutput()->GetSpacing(spaceing);
15: std::cout<<"原像素間隔:"<<spaceing[0]<<" "<<spaceing[1]<<""<<spaceing[2]<<std::endl<<std::endl;
16:
17: vtkSmartPointer<vtkImageChangeInformation> changer =
18: vtkSmartPointer<vtkImageChangeInformation>::New();
19: changer->SetInput(reader->GetOutput());
20: changer->SetOutputOrigin(100, 100, 0);
21: changer->SetOutputSpacing(5,5,1);
22: changer->SetCenterImage(1);
23: changer->Update();
24:
25:
26: changer->GetOutput()->GetDimensions(dims);
27: std::cout<<"修改後圖像維數:"<<dims[0]<<" "<<dims[1]<<""<<dims[2]<<std::endl;
28: changer->GetOutput()->GetOrigin(origin);
29: std::cout<<"修改圖像原點:"<<origin[0]<<" "<<origin[1]<<""<<origin[2]<<std::endl;
30: changer->GetOutput()->GetSpacing(spaceing);
31: std::cout<<"修改後像素間隔:"<<spaceing[0]<<" "<<spaceing[1]<<""<<spaceing[2]<<std::endl;
32:
33:
34: vtkSmartPointer<vtkImageViewer2> p_w_picpathViewer =
35: vtkSmartPointer<vtkImageViewer2>::New();
36: p_w_picpathViewer->SetInputConnection(changer->GetOutputPort());
37: vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor=
38: vtkSmartPointer<vtkRenderWindowInteractor>::New();
39: p_w_picpathViewer->SetupInteractor(renderWindowInteractor);
40: p_w_picpathViewer->Render();
41: p_w_picpathViewer->GetRenderer()->ResetCamera();
42: p_w_picpathViewer->Render();
43:
44: renderWindowInteractor->Start();
上例中首先讀入圖像,由vtkImageData提供函數接口獲取圖像的維數,原點和像素間隔。而後定義vtkImageChangeInformation指針,並設置輸出圖像原點爲(100, 100, 0),輸出圖像像素間隔爲(5, 5, 1),而後調用CenterImage()函數將圖像的原點置於圖像的中心。顯示結果如圖5.9:
圖5.9 vtkImageChangeInformation修改圖像信息
從上面結果中能夠看出,操做後的結果使得圖像的原點位於(-1277.5, -1275.5, 0),SetOutputOrigin(100, 100, 0)並無起做用。緣由在哪裏呢?若是看下CenterImage()函數的註釋,能夠發現該函數的做用是將(0, 0, 0)點置於圖像的中心。當CenterImage該函數執行時會重寫SetOutputOrigin(),因此SetOutputOrigin函數不會產生任何做用。那(-1277.5, -1275.5, 0)又是如何計算出來的呢?如圖5.10,根據圖像的維數和像素間隔計算獲得新的圖像的寬度和高度爲(512-1)*5 ,初始圖像的原點位於(0, 0,0),如今將圖像的中心平移至原點,平移量爲(-(512-1)*5/2,(512-1)*5/2, 0) = (-1277.5, -1275.5,0)。
圖5.10 CenterImage函數示意圖
圖像像素值的訪問與修改是最經常使用的一個操做。VTK中提供了兩種訪問圖像像素值的方法。第一種是直接訪問vtkImageData的數據數組。這種方式最直接。在第一節上新建圖像賦值也是採用的這種方法。vtkImageData中提供了GetScalarPointer()函數獲取數據數組指針,該函數有三種形式:
virtualvoid *GetScalarPointer(int coordinates[3]);
virtualvoid *GetScalarPointer(int x, int y, int z);
virtualvoid *GetScalarPointer();
前兩種形式根據給定的像素索引獲得指定的像素值,注意返回的是第(x,y,z)個像素值的地址。而第三種方式是返回圖像數據數組的頭指針,而後根據頭指針能夠依次訪問索引像素。在第一節中採用的就是這樣方式。下面看一個遍歷圖像像素的例子,結果如圖5.12。
1: vtkSmartPointer<vtkBMPReader> reader =
2: vtkSmartPointer<vtkBMPReader>::New();
3: reader->SetFileName ( "…\\lena.bmp" );
4: reader->Update();
5:
6: int dims[3];
7: reader->GetOutput()->GetDimensions(dims);
8:
9: for(int k=0; k<dims[2]; k++)
10: {
11: for(int j=0; j<dims[1]; j++)
12: {
13: for(int i=0; i<dims[0]; i++)
14: {
15: if(i<100 && j<100)
16: {
17: unsigned char * pixel =
18: (unsigned char *) (reader->GetOutput()->GetScalarPointer(i, j, k) );
19: *pixel = 0;
20: *(pixel+1) = 0;
21: *(pixel+2) = 0;
22: }
23: }
24: }
25: }
26:
27: vtkSmartPointer<vtkImageViewer2> p_w_picpathViewer =
28: vtkSmartPointer<vtkImageViewer2>::New();
29: p_w_picpathViewer->SetInputConnection(reader->GetOutputPort());
30: vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor=
31: vtkSmartPointer<vtkRenderWindowInteractor>::New();
32: p_w_picpathViewer->SetupInteractor(renderWindowInteractor);
33: p_w_picpathViewer->Render();
34: p_w_picpathViewer->GetRenderer()->ResetCamera();
35: p_w_picpathViewer->Render();
36:
37: renderWindowInteractor->Start();
上面代碼實現了將圖像的100*100大小的區域設置爲黑色。首先定義一個reader讀取一副bmp圖像,經過vtkImageData函數GetDimensions()獲取圖像的大小。創建三次循環,經過GetScalarPointer(i, j,k)函數獲取訪問圖像像素值。須要注意的是,GetScalarPointer()函數返回的是void*類型,所以須要根據圖像的實際類型進行強制轉換。如上面代碼中將像素值數組的頭指針類型轉換爲unsigned char *。若是對於數據類型不肯定的話,還能夠先經過vtkImageCast將圖像數據類型強制轉換爲特定的數據類型,再進行遍歷。
另外還有一個須要注意地方,彩色以及向量圖像採用的是相似圖5.11這種像素存儲格式。
圖5.11 VTK彩色以及向量圖像像素存儲格式
所以在修改RGB圖像以及向量圖像像素時,須要根據像素的元組的組分數目來訪問。上例中,須要修改每一個像素的RGB值時,首先得到第(i, j, k)個像素的地址也就是R值的地址,而後將地址加1來訪問後續G值以及B值。若是對於像素的元組組分不肯定時,能夠經過函數GetNumberOfScalarComponents()來獲取。以下所示:
int nbOfComp = reader->GetOutput()->GetNumberOfScalarComponents();
圖5.12 修改圖像像素值
另外VTK中提供了vtkImageIterator類來利用迭代器方法訪問圖像像素。該類是一個模板類,使用時,須要提供迭代的圖像像素類型以及迭代的區域大小。下面給出示例代碼。
1: vtkSmartPointer<vtkBMPReader>reader =
2: vtkSmartPointer<vtkBMPReader>::New();
3: reader->SetFileName ( "..\\lena.bmp" );
4: reader->Update();
5:
6: int subRegion[6] = {0,300, 0, 300, 0, 0};
7: vtkImageIterator<unsigned char> it(reader->GetOutput(), subRegion);
8:
9: while(!it.IsAtEnd())
10: {
11: unsigned char *inSI = it.BeginSpan();
12: unsigned char *inSIEnd = it.EndSpan();
13:
14: while(inSI != inSIEnd)
15: {
16: *inSI = 255-*inSI;
17: ++inSI;
18: }
19: it.NextSpan();
20: }
21:
22: vtkSmartPointer<vtkImageViewer2> p_w_picpathViewer =
23: vtkSmartPointer<vtkImageViewer2>::New();
24: p_w_picpathViewer->SetInputConnection(reader->GetOutputPort());
25: vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor=
26: vtkSmartPointer<vtkRenderWindowInteractor>::New();
27: p_w_picpathViewer->SetupInteractor(renderWindowInteractor);
28: p_w_picpathViewer->Render();
29: p_w_picpathViewer->GetRenderer()->ResetCamera();
30: p_w_picpathViewer->Render();
31:
32: renderWindowInteractor->Start();
下面分析一下上面的代碼。若是對於ITK圖像區域迭代器熟悉的話,可能會對上面代碼存在疑問。上面代碼中首先讀取了一副bmp圖像,而後定義了一個子區域。注意在定義子區域的時候,不要超過圖像的大小範圍。subRegion的六個值分別表示區域中x的最小最大值,y的最小最大值,z的最小最大值。因爲處理的圖像爲二維圖像,所以z的取值範圍爲[0,0]。而後根據圖像類型unsigned char定義實例化一個圖像迭代器it,定義it時有兩個參數:一個是要訪問的圖像,另一個是訪問的圖像區域。設置完畢後,迭代器開始工做。注意,上面代碼中有兩個while循環。
首先看第一個while循環,這裏判斷迭代器是否結束。進入循環後,對於每一個迭代器it,又存在第二個循環。這個循環判斷的是當前像素的組分是否迭代完畢。因爲vtk中全部類型的圖像格式都是vtkImageData,所以每一個像素多是標量,也多是向量。所以,每當訪問到一個像素時,須要迭代當前像素的組分。組分迭代時,inSI = it.BeginSpan()獲取第一個組分,inSIEnd = it.EndSpan()表示組分迭代完畢,經過++inSI不斷迭代組分,並對像素的組分值進行處理,當inSI與inSIEnd相等時組分迭代完畢。而後繼續迭代像素it,直至迭代完畢全部像素。上面代碼中將指定區域的像素值取反,獲得如圖5.13所示結果。
圖5.13 利用VTK迭代器修改圖像像素值
==========歡迎轉載,轉載時請保留該聲明信息========== 版權歸@東靈工做室全部,更多信息請訪問東靈工做室
本文來源:http://blog.csdn.net/www_doling_net/article/details/8547317