5.7 区域提取
5.7 區(qū)域提取
5.7.1 提取感興趣區(qū)域
感興趣區(qū)域(Volum of Interest)是指圖像內(nèi)部的一個(gè)子區(qū)域。在VTK中vtkExtractVOI類實(shí)現(xiàn)由用戶指定的區(qū)域范圍提取圖像的子圖像。該Filter的輸入和輸出都是一個(gè)vtkImageData,因此其結(jié)果可以直接作為圖像保存。
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: vtkSmartPointer<vtkExtractVOI> extractVOI = 10: vtkSmartPointer<vtkExtractVOI>::New(); 11: extractVOI->SetInputConnection(reader->GetOutputPort()); 12: extractVOI->SetVOI(dims[0]/4.,3.*dims[0]/4.,dims[1]/4.,3.*dims[1]/4., 0, 0); 13: extractVOI->Update();上例代碼實(shí)現(xiàn)了提取一副圖像的子區(qū)域。首先讀取一個(gè)圖像,并獲取圖像的維數(shù)。然后定義vtkExtractVOI對(duì)象,該對(duì)象接收兩個(gè)輸入一個(gè)是圖像數(shù)據(jù),第二個(gè)是區(qū)域大小。設(shè)置區(qū)域大小的函數(shù)原型:
void SetVOI(int _arg1, int _arg2, int _arg3, int _arg4, int _arg5, int _arg6)void SetVOI(int _arg[])其參數(shù)是提取的區(qū)域各個(gè)方向的大小,共6個(gè)參數(shù),依次表示x方向最小值,x方向最大值,y方向最小值,y方向最大值,z方向最小值和z方向最大值。上例中由于讀取的是二維圖像,因此z方向的區(qū)域?yàn)閇0,0],而在x方向范圍為[ dims[0]/4 , 3*dims[0]/4 ],y方向范圍為[ dims[1]/4 , 3*dims[1]/4 ],即提取圖像原圖中間1/4圖像。執(zhí)行結(jié)果如下:
圖5.18 提取感興趣區(qū)域
5.7.2 三維圖像切片提取
切片是指三維圖像中的一個(gè)切面對(duì)應(yīng)的圖像。切面可以是過圖像內(nèi)部一點(diǎn)且平行于XY、YZ、XZ平面的平面,也可以是任意的過三維圖像內(nèi)部一點(diǎn)任意方向的平面。通過提取切片可以方便的瀏覽和分析圖像內(nèi)部組織結(jié)構(gòu),是醫(yī)學(xué)圖像瀏覽軟件中的一個(gè)重要的功能。在VTK中vtkImageReslice類實(shí)現(xiàn)圖像切片提取功能。下面首先看一段切片提取的代碼。
1: vtkSmartPointer<vtkMetaImageReader> reader = 2: vtkSmartPointer<vtkMetaImageReader>::New(); 3: reader->SetFileName ( " brain.mhd" ); 4: reader->Update(); 5: 6: int extent[6]; 7: double spacing[3]; 8: double origin[3]; 9: 10: reader->GetOutput()->GetExtent(extent); 11: reader->GetOutput()->GetSpacing(spacing); 12: reader->GetOutput()->GetOrigin(origin); 13: 14: double center[3]; 15: center[0] = origin[0] + spacing[0] * 0.5 * (extent[0] + extent[1]); 16: center[1] = origin[1] + spacing[1] * 0.5 * (extent[2] + extent[3]); 17: center[2] = origin[2] + spacing[2] * 0.5 * (extent[4] + extent[5]); 18: 19: static double axialElements[16] = { 20: 1, 0, 0, 0, 21: 0, 1, 0, 0, 22: 0, 0, 1, 0, 23: 0, 0, 0, 1 }; 24: 25: vtkSmartPointer<vtkMatrix4x4> resliceAxes = 26: vtkSmartPointer<vtkMatrix4x4>::New(); 27: resliceAxes->DeepCopy(axialElements); 28: 29: resliceAxes->SetElement(0, 3, center[0]); 30: resliceAxes->SetElement(1, 3, center[1]); 31: resliceAxes->SetElement(2, 3, center[2]); 32: 33: 34: vtkSmartPointer<vtkImageReslice> reslice = 35: vtkSmartPointer<vtkImageReslice>::New(); 36: reslice->SetInputConnection(reader->GetOutputPort()); 37: reslice->SetOutputDimensionality(2); 38: reslice->SetResliceAxes(resliceAxes); 39: reslice->SetInterpolationModeToLinear(); 40: 41: vtkSmartPointer<vtkLookupTable> colorTable = 42: vtkSmartPointer<vtkLookupTable>::New(); 43: colorTable->SetRange(0, 1000); 44: colorTable->SetValueRange(0.0, 1.0); 45: colorTable->SetSaturationRange(0.0, 0.0); 46: colorTable->SetRampToLinear(); 47: colorTable->Build(); 48: 49: vtkSmartPointer<vtkImageMapToColors> colorMap = 50: vtkSmartPointer<vtkImageMapToColors>::New(); 51: colorMap->SetLookupTable(colorTable); 52: colorMap->SetInputConnection(reslice->GetOutputPort()); 53: 54: vtkSmartPointer<vtkImageActor> imgActor = 55: vtkSmartPointer<vtkImageActor>::New(); 56: imgActor->SetInput(colorMap->GetOutput()); 57: 58: vtkSmartPointer<vtkRenderer> renderer = 59: vtkSmartPointer<vtkRenderer>::New(); 60: renderer->AddActor(imgActor); 61: renderer->SetBackground(.4, .5, .6); 62: 63: vtkSmartPointer<vtkRenderWindow> renderWindow = 64: vtkSmartPointer<vtkRenderWindow>::New(); 65: renderWindow->SetSize(500, 500); 66: renderWindow->AddRenderer(renderer); 67: 68: vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = 69: vtkSmartPointer<vtkRenderWindowInteractor>::New(); 70: vtkSmartPointer<vtkInteractorStyleImage> imagestyle = 71: vtkSmartPointer<vtkInteractorStyleImage>::New(); 72: 73: renderWindowInteractor->SetInteractorStyle(imagestyle); 74: renderWindowInteractor->SetRenderWindow(renderWindow); 75: renderWindowInteractor->Initialize(); 76: 77: renderWindowInteractor->Start();首先通過vtkMetaImageReader讀取一副醫(yī)學(xué)三維圖像,并獲取得到圖像范圍(extent),原點(diǎn)和像素間隔;由這三個(gè)參數(shù)可以計(jì)算圖像的中心位置center;接下來(lái)定義了切面的變換矩陣axialElements,該矩陣的前三列分別表示x、y和z方向向量,第四列為中心點(diǎn)坐標(biāo);代碼中的axialElements表示切面變換矩陣與當(dāng)前坐標(biāo)系一致,且切面為過中心點(diǎn)center,并平行于XY平面的平面。當(dāng)前,定義該切面時(shí),也可以是其他平面,甚至是任意平面,但是必須要過圖像內(nèi)部點(diǎn)。下面給出了一個(gè)常用的變換矩陣:
static double coronalElements[16] = {1, 0, 0, 0,0, 0, 1, 0,0,-1, 0, 0,0, 0, 0, 1 }; 提取平行于XZ平面的切片static double sagittalElements[16] = {0, 0,-1, 0,1, 0, 0, 0,0,-1, 0, 0,0, 0, 0, 1 }; 提取平行于YZ平面的切片static double obliqueElements[16] = {1, 0, 0, 0,0, 0.866025, -0.5, 0,0, 0.5, 0.866025, 0,0, 0, 0, 1 }; 提取斜切切片注意使用這些變換矩陣的時(shí)候,需要將第四列替換為切片經(jīng)過圖像的一個(gè)點(diǎn)坐標(biāo),上例中將圖像的中心添加到axialElements矩陣,并通過函數(shù)SetResliceAxes設(shè)置變換矩陣,SetOutputDimensionality(2)指定輸出的圖像為一個(gè)二維圖像;而函數(shù)SetInterpolationModeToLinear()則指定了切面提取中的差值方式為線性差值,另外該類中還提供了其他的差值方式:
SetInterpolationModeToNearestNeighbor():最近鄰方式
SetInterpolationModeToCubic():三次線性差值
設(shè)置完畢后,執(zhí)行Update()即可完成切面計(jì)算。運(yùn)行結(jié)果如下圖:
圖5.19 切片提取
5.7.3 擴(kuò)展
學(xué)習(xí)三維圖像切面的提取后,我們?cè)谏瞎?jié)的程序上做一個(gè)擴(kuò)展,實(shí)現(xiàn)一個(gè)稍微復(fù)雜的程序——通過滑動(dòng)鼠標(biāo)來(lái)切換三維圖像切片,這也是醫(yī)學(xué)圖像處理軟件中一個(gè)很基本的功能。實(shí)現(xiàn)該功能難點(diǎn)是怎樣在VTK中控制鼠標(biāo)來(lái)實(shí)時(shí)提取圖像切片。在前面的章節(jié)中已經(jīng)介紹觀察者/命令(Observer/Command)模式,我們也采用這種機(jī)制來(lái)實(shí)現(xiàn)。VTK中鼠標(biāo)消息是在交互類型對(duì)象(interactorstyle)中響應(yīng),因此通過為交互類型對(duì)象(interactorstyle)添加觀察者(observer)來(lái)監(jiān)聽相應(yīng)的消息,當(dāng)消息觸發(fā)時(shí),由命令模式執(zhí)行相應(yīng)的回調(diào)函數(shù)。閑話少說(shuō),放代碼。
1: class vtkImageInteractionCallback : public vtkCommand 2: { 3: public: 4: 5: static vtkImageInteractionCallback *New() 6: { 7: return new vtkImageInteractionCallback; 8: } 9: 10: vtkImageInteractionCallback() 11: { 12: this->Slicing = 0; 13: this->ImageReslice = 0; 14: this->Interactor = 0; 15: } 16: 17: void SetImageReslice(vtkImageReslice *reslice) 18: { 19: this->ImageReslice = reslice; 20: } 21: 22: vtkImageReslice *GetImageReslice() 23: { 24: return this->ImageReslice; 25: } 26: 27: void SetInteractor(vtkRenderWindowInteractor *interactor) 28: { 29: this->Interactor = interactor; 30: } 31: 32: vtkRenderWindowInteractor *GetInteractor() 33: { 34: return this->Interactor; 35: } 36: 37: virtual void Execute(vtkObject *, unsigned long event, void *) 38: { 39: vtkRenderWindowInteractor *interactor = this->GetInteractor(); 40: 41: int lastPos[2]; 42: interactor->GetLastEventPosition(lastPos); 43: int currPos[2]; 44: interactor->GetEventPosition(currPos); 45: 46: if (event == vtkCommand::LeftButtonPressEvent) 47: { 48: this->Slicing = 1; 49: } 50: else if (event == vtkCommand::LeftButtonReleaseEvent) 51: { 52: this->Slicing = 0; 53: } 54: else if (event == vtkCommand::MouseMoveEvent) 55: { 56: if (this->Slicing) 57: { 58: vtkImageReslice *reslice = this->ImageReslice; 59: 60: // Increment slice position by deltaY of mouse 61: int deltaY = lastPos[1] - currPos[1]; 62: 63: reslice->Update(); 64: double sliceSpacing = reslice->GetOutput()->GetSpacing()[2]; 65: vtkMatrix4x4 *matrix = reslice->GetResliceAxes(); 66: // move the center point that we are slicing through 67: double point[4]; 68: double center[4]; 69: point[0] = 0.0; 70: point[1] = 0.0; 71: point[2] = sliceSpacing * deltaY; 72: point[3] = 1.0; 73: matrix->MultiplyPoint(point, center); 74: matrix->SetElement(0, 3, center[0]); 75: matrix->SetElement(1, 3, center[1]); 76: matrix->SetElement(2, 3, center[2]); 77: interactor->Render(); 78: } 79: else 80: { 81: vtkInteractorStyle *style = vtkInteractorStyle::SafeDownCast( 82: interactor->GetInteractorStyle()); 83: if (style) 84: { 85: style->OnMouseMove(); 86: } 87: } 88: } 89: } 90: 91: private: 92: int Slicing; 93: vtkImageReslice *ImageReslice; 94: vtkRenderWindowInteractor *Interactor; 95: };vtkImageInteractionCallback繼承自vtkCommand類,并覆蓋父類函數(shù)Execute()。該類提供了兩個(gè)接口:SetImageReslice和SetInteractor。SetImageReslice用以設(shè)置vtkImageSlice對(duì)象,vtkImageSlice根據(jù)設(shè)置的變換矩陣提取三維圖像切片。SetInteractor用以設(shè)置vtkRenderWindowInteractor,vtkRenderWindowInteractor類對(duì)象負(fù)責(zé)每次提取切片后刷新視圖。
下面我們重點(diǎn)來(lái)看一下Execute函數(shù),該函數(shù)提供了具體的切片提取功能。在該函數(shù)里面,主要監(jiān)聽了三個(gè)消息:
vtkCommand::LeftButtonPressEvent,vtkCommand::LeftButtonReleaseEvent,vtkCommand::MouseMoveEvent,前兩個(gè)消息分別是鼠標(biāo)左鍵的按下和彈起消息。當(dāng)鼠標(biāo)左鍵按下時(shí),就設(shè)置切片提取標(biāo)志為1,而當(dāng)彈起時(shí),將標(biāo)志置為0。這樣在鼠標(biāo)移動(dòng)時(shí),只有在確定切片提取標(biāo)志為1時(shí),執(zhí)行切片提取功能。
vtkCommand::MouseMoveEvent即為鼠標(biāo)移動(dòng)消息。當(dāng)檢測(cè)到該消息時(shí),首先檢查切片提取標(biāo)志,當(dāng)為1時(shí)提取切片。提取切片時(shí),需要為vtkImageSlice對(duì)象設(shè)置變換矩陣。這里在函數(shù)開始時(shí),首先獲取了鼠標(biāo)滑動(dòng)的前后兩次點(diǎn)的位置lastPos和currPos。然后根據(jù)兩點(diǎn)的Y坐標(biāo)差deltaY,計(jì)算新的中心點(diǎn)center并變換至vtkImageSlice當(dāng)前變換矩陣中,得到變換中心點(diǎn),將其設(shè)置到原來(lái)的變換矩陣matrix中,并設(shè)置到vtkImageSlice中,最后執(zhí)行interactor->Render()即可不斷的根據(jù)鼠標(biāo)移動(dòng)刷新圖像。
Command對(duì)象定義完畢后,即可為交互對(duì)象InteractorStyle添加觀察者,響應(yīng)鼠標(biāo)消息。這里可以在上節(jié)的程序上進(jìn)行修改,前面代碼一致,只需要在最后添加如下代碼:
1: vtkSmartPointer<vtkImageInteractionCallback> callback = 2: vtkSmartPointer<vtkImageInteractionCallback>::New(); 3: callback->SetImageReslice(reslice); 4: callback->SetInteractor(renderWindowInteractor); 5: 6: imagestyle->AddObserver(vtkCommand::MouseMoveEvent, callback); 7: imagestyle->AddObserver(vtkCommand::LeftButtonPressEvent, callback); 8: imagestyle->AddObserver(vtkCommand::LeftButtonReleaseEvent, callback); 9: 10: renderWindowInteractor->Start();這里主要是定義了vtkImageInteractionCallback對(duì)象,并設(shè)置vtkImageSlice對(duì)象和vtkRenderWindowInteractor對(duì)象。然后為交互對(duì)象vtkInteractorStyle添加觀察者來(lái)監(jiān)控相應(yīng)的消息,這里主要是三個(gè)消息:
vtkCommand::LeftButtonPressEvent,vtkCommand::LeftButtonReleaseEvent,vtkCommand::MouseMoveEvent,當(dāng)響應(yīng)到這三個(gè)消息時(shí),立即執(zhí)行vtkImageInteractionCallback的Execute函數(shù),以便實(shí)現(xiàn)切片的實(shí)時(shí)提取和更新。完成以后,運(yùn)行程序,當(dāng)鼠標(biāo)在圖像上移動(dòng)時(shí),會(huì)發(fā)現(xiàn)圖像會(huì)跟著鼠標(biāo)的移動(dòng)而變化,神奇吧?有興趣的話,還可以實(shí)現(xiàn)YZ平面、XZ平面切片提取,甚至是任意方向的切面提取。
==========歡迎轉(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é)
- 上一篇: 看生物信息学如何聚焦特定表型,探索分子机
- 下一篇: python图像处理大全