1. 程式人生 > >05-VTK在影象處理中的應用(2)

05-VTK在影象處理中的應用(2)

5.4 vtkImageData基本操作

影象處理離不開一些基本的影象資料操作,例如獲取和修改影象的基本資訊,訪問和修改影象畫素值,影象顯示,影象型別轉換等等。熟練掌握這些基本操作有助於使用VTK進行影象處理的快速開發。

5.4.1影象資訊訪問和修改

vtkImageData中提供了多個函式用於訪問或者獲取影象的基本資訊,這些函式通常使用Set或者Get加上相應的資訊名的形式,例如獲取影象維數的方法定義為GetDimensions()。當然這裡主要從類的層次上進行VTK的學習,這裡不再具體贅述每個函式的基本名稱和使用,使用者可以查閱相應的類文件。下面通過一個例子來說明怎樣訪問影象的基本資訊。程式執行如圖5.8所示。

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:   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>imageViewer =

20:   vtkSmartPointer<vtkImageViewer2>::New();

21: imageViewer->SetInputConnection(reader->GetOutputPort());

22: vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor=

23:   vtkSmartPointer<vtkRenderWindowInteractor>::New();

24: imageViewer->SetupInteractor(renderWindowInteractor);

25:  imageViewer->Render();

26: imageViewer->GetRenderer()->ResetCamera();

27:  imageViewer->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> imageViewer =

  35:        vtkSmartPointer<vtkImageViewer2>::New();

  36:    imageViewer->SetInputConnection(changer->GetOutputPort());

  37:    vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor=

  38:        vtkSmartPointer<vtkRenderWindowInteractor>::New();

  39:    imageViewer->SetupInteractor(renderWindowInteractor);

  40:    imageViewer->Render();

  41:    imageViewer->GetRenderer()->ResetCamera();

  42:    imageViewer->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函式示意圖

5.4.2影象畫素值訪問和修改

影象畫素值的訪問與修改是最常用的一個操作。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> imageViewer =

  28:    vtkSmartPointer<vtkImageViewer2>::New();

  29: imageViewer->SetInputConnection(reader->GetOutputPort());

  30: vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor=

  31:    vtkSmartPointer<vtkRenderWindowInteractor>::New();

  32: imageViewer->SetupInteractor(renderWindowInteractor);

  33: imageViewer->Render();

  34: imageViewer->GetRenderer()->ResetCamera();

  35: imageViewer->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> imageViewer =

  23:         vtkSmartPointer<vtkImageViewer2>::New();

  24:     imageViewer->SetInputConnection(reader->GetOutputPort());

  25:     vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor=

  26:         vtkSmartPointer<vtkRenderWindowInteractor>::New();

  27:     imageViewer->SetupInteractor(renderWindowInteractor);

  28:     imageViewer->Render();

  29:     imageViewer->GetRenderer()->ResetCamera();

  30:     imageViewer->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迭代器修改影象畫素值

==========歡迎轉載,轉載時請保留該宣告資訊==========

版權歸所有,更多資訊請訪問東靈工作室

================================================