区域提取
5.7 區(qū)域提取
5.7.1 提取感興趣區(qū)域?
感興趣區(qū)域(Volum of Interest)是指圖像內部的一個子區(qū)域。在VTK中vtkExtractVOI類實現(xiàn)由用戶指定的區(qū)域范圍提取圖像的子圖像。該Filter的輸入和輸出都是一個vtkImageData,因此其結果可以直接作為圖像保存。
?
?? 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();
?
上例代碼實現(xiàn)了提取一副圖像的子區(qū)域。首先讀取一個圖像,并獲取圖像的維數(shù)。然后定義vtkExtractVOI對象,該對象接收兩個輸入一個是圖像數(shù)據(jù),第二個是區(qū)域大小。設置區(qū)域大小的函數(shù)原型:
void SetVOI(int _arg1, int _arg2, int _arg3, int _arg4, int _arg5, int _arg6)
void SetVOI(int _arg[])
其參數(shù)是提取的區(qū)域各個方向的大小,共6個參數(shù),依次表示x方向最小值,x方向最大值,y方向最小值,y方向最大值,z方向最小值和z方向最大值。上例中由于讀取的是二維圖像,因此z方向的區(qū)域為[0,0],而在x方向范圍為[ dims[0]/4 , 3*dims[0]/4 ],y方向范圍為[ dims[1]/4 , 3*dims[1]/4 ],即提取圖像原圖中間1/4圖像。執(zhí)行結果如下:
?
圖5.18 提取感興趣區(qū)域
5.7.2 三維圖像切片提取
切片是指三維圖像中的一個切面對應的圖像。切面可以是過圖像內部一點且平行于XY、YZ、XZ平面的平面,也可以是任意的過三維圖像內部一點任意方向的平面。通過提取切片可以方便的瀏覽和分析圖像內部組織結構,是醫(yī)學圖像瀏覽軟件中的一個重要的功能。在VTK中vtkImageReslice類實現(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ī)學三維圖像,并獲取得到圖像范圍(extent),原點和像素間隔;由這三個參數(shù)可以計算圖像的中心位置center;接下來定義了切面的變換矩陣axialElements,該矩陣的前三列分別表示x、y和z方向向量,第四列為中心點坐標;代碼中的axialElements表示切面變換矩陣與當前坐標系一致,且切面為過中心點center,并平行于XY平面的平面。當前,定義該切面時,也可以是其他平面,甚至是任意平面,但是必須要過圖像內部點。下面給出了一個常用的變換矩陣:
?
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 }; 提取斜切切片
?
注意使用這些變換矩陣的時候,需要將第四列替換為切片經(jīng)過圖像的一個點坐標,上例中將圖像的中心添加到axialElements矩陣,并通過函數(shù)SetResliceAxes設置變換矩陣,SetOutputDimensionality(2)指定輸出的圖像為一個二維圖像;而函數(shù)SetInterpolationModeToLinear()則指定了切面提取中的差值方式為線性差值,另外該類中還提供了其他的差值方式:
SetInterpolationModeToNearestNeighbor():最近鄰方式
SetInterpolationModeToCubic():三次線性差值
設置完畢后,執(zhí)行Update()即可完成切面計算。運行結果如下圖:
?
圖5.19 切片提取
5.7.3 擴展
學習三維圖像切面的提取后,我們在上節(jié)的程序上做一個擴展,實現(xiàn)一個稍微復雜的程序——通過滑動鼠標來切換三維圖像切片,這也是醫(yī)學圖像處理軟件中一個很基本的功能。實現(xiàn)該功能難點是怎樣在VTK中控制鼠標來實時提取圖像切片。在前面的章節(jié)中已經(jīng)介紹觀察者/命令(Observer/Command)模式,我們也采用這種機制來實現(xiàn)。VTK中鼠標消息是在交互類型對象(interactorstyle)中響應,因此通過為交互類型對象(interactorstyle)添加觀察者(observer)來監(jiān)聽相應的消息,當消息觸發(fā)時,由命令模式執(zhí)行相應的回調函數(shù)。閑話少說,放代碼。
? 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()。該類提供了兩個接口:SetImageReslice和SetInteractor。SetImageReslice用以設置vtkImageSlice對象,vtkImageSlice根據(jù)設置的變換矩陣提取三維圖像切片。SetInteractor用以設置vtkRenderWindowInteractor,vtkRenderWindowInteractor類對象負責每次提取切片后刷新視圖。
下面我們重點來看一下Execute函數(shù),該函數(shù)提供了具體的切片提取功能。在該函數(shù)里面,主要監(jiān)聽了三個消息:
vtkCommand::LeftButtonPressEvent,
vtkCommand::LeftButtonReleaseEvent,
vtkCommand::MouseMoveEvent,
前兩個消息分別是鼠標左鍵的按下和彈起消息。當鼠標左鍵按下時,就設置切片提取標志為1,而當彈起時,將標志置為0。這樣在鼠標移動時,只有在確定切片提取標志為1時,執(zhí)行切片提取功能。
vtkCommand::MouseMoveEvent即為鼠標移動消息。當檢測到該消息時,首先檢查切片提取標志,當為1時提取切片。提取切片時,需要為vtkImageSlice對象設置變換矩陣。這里在函數(shù)開始時,首先獲取了鼠標滑動的前后兩次點的位置lastPos和currPos。然后根據(jù)兩點的Y坐標差deltaY,計算新的中心點center并變換至vtkImageSlice當前變換矩陣中,得到變換中心點,將其設置到原來的變換矩陣matrix中,并設置到vtkImageSlice中,最后執(zhí)行interactor->Render()即可不斷的根據(jù)鼠標移動刷新圖像。
Command對象定義完畢后,即可為交互對象InteractorStyle添加觀察者,響應鼠標消息。這里可以在上節(jié)的程序上進行修改,前面代碼一致,只需要在最后添加如下代碼:
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對象,并設置vtkImageSlice對象和vtkRenderWindowInteractor對象。然后為交互對象vtkInteractorStyle添加觀察者來監(jiān)控相應的消息,這里主要是三個消息:
vtkCommand::LeftButtonPressEvent,
vtkCommand::LeftButtonReleaseEvent,
vtkCommand::MouseMoveEvent,
當響應到這三個消息時,立即執(zhí)行vtkImageInteractionCallback的Execute函數(shù),以便實現(xiàn)切片的實時提取和更新。完成以后,運行程序,當鼠標在圖像上移動時,會發(fā)現(xiàn)圖像會跟著鼠標的移動而變化,神奇吧?有興趣的話,還可以實現(xiàn)YZ平面、XZ平面切片提取,甚至是任意方向的切面提取。
?
==========歡迎轉載,轉載時請保留該聲明信息==========
版權歸@東靈工作室所有,更多信息請訪問東靈工作室
教程系列導航:http://blog.csdn.net/www_doling_net/article/details/8763686
================================================
總結
- 上一篇: 机器学习也可以在线做!
- 下一篇: 监督学习 | 集成学习 之Bagging