基于VTK的MFC应用程序开发(3)
基于VTK的MFC應用程序開發(3)
分類: VTK應用示例 2013-05-17 13:37 3307人閱讀 評論(23) 收藏 舉報目錄(?)[+]
之前介紹了基于VTK的單文檔應用程序開發,并以圖像重采樣為例,實現了一個簡單的圖像重采樣的應用程序。對于多文檔應用程序,與單文檔應用程序基本一致,這里就不再講述。對話框應用程序是MFC應用程序中一個使用非常廣泛的框架,本節就以醫學圖像可視化中常用的四視圖框架程序的實現為例,講述基于VTK的對話框應用程序開發。
1.? 利用VS和CMake建立一個空的MFC對話框程序框架。
利用VS創建一個MFC對話框工程vtkDialog,刪除其中的工程文件,完成CMakeLists.txt文件,并添加相應的代碼文件和鏈接VTK動態庫,利用CMake配置完畢后,打開生成的工程文件vtkDialog.sln,編譯執行,即可得到一個空的對話框程序。其中CvtkDialogDlg為該程序的主對話框類。
2.? 設計用戶界面,添加相應的控件
本程序需要實現的功能有(1)圖像讀取和管理;(2)圖像切分和瀏覽。一個常見的醫學圖像可視化程序,包括四個視圖,橫斷面視圖,矢狀面視圖,冠狀面視圖和三維視圖。因此,基于以上設計,我們添加一個樹控件,MFC中對應的控件類為CTreeCtrl。樹控件是最常用的文件管理控件,能夠方便的對文件進行層次化組織和管理。四視圖的實現則需要四個控件,這里我們選擇CStatic控件,將其添加至對話框窗口中。添加完畢后,為控件生成相應的Control類型的變量。
按照上述設計,需要在CStatic中顯示圖像。這就需要對CStatic類繼續擴展,使其支持VTK可視化管線。一個可行的方法是,設計一個CStatic類的子類,并在該子類中實現VTK可視化管線和處理。
3.? 實現VTK圖像可視化控件
3.1 首先添加一個MFC類CvtkView
其基類選擇為CStatic,并添加至CMakeLists.txt文件中進行管理。
3.2 重載CvtkView類PreSubclassWindow()函數和OnPaint()函數
PreSubclassWindow函數負責創建VTK可視化管線,OnPaint()函數負責客戶區內場景渲染。
3.3 建立VTK可視化管線
VTK可視化管線在第二章中已經介紹過,其中最主要包含 vtkAcor,vtkRenderer,vtkRenderWindow,vtkRenderWindowInteractor四個部分。當然根據需要還可以設置vtkRenderWindowInteractorStyle,以及光照,材質,顏色等。在CvtkView類頭文件中定義相關對象,并在PreSubclassWindow函數中實例化和構建可視化管線,代碼如下。
[cpp] view plaincopy
相信通過前面的學習,這里建立可視化管線的流程已經比較熟悉了。需要注意的是,vtkRenderWindow需要通過函數vtkRenderWindow::SetParentId()來建立與控件本身的關聯,這樣才能將m_RenderWindow中的渲染內容在控件的窗口上進行顯示;而vtkRenderWindow::SetSize()則是設置渲染窗口與當前控件客戶區保持大小一致。
大家可能會有疑問,怎么沒有vtkActor?正常的一個可視化管線中,vtkActor表示需要進行渲染或者繪制的對象。這里需要渲染的對象是圖像,而與以前不同的是,沒有直接去定義一個圖像vtkActor。至于原因暫且不管,隨著該類功能的逐步完善,我們再詳細說明。VTK渲染管線建立完畢后在OnPaint()函數中調用vtkRenderWindow的Render()函數來實現渲染。
到這里一個基本的VTK顯示控件已經實現,在設計界面時,通過MFC自動添加的四個視圖變量類型默認為CStatic。由于CvtkView是繼承自CStatic,因此我們可以直接將主對話框類CvtkDialogDlg頭文件中定義的四個變量類型修改為CvtkView。然后編譯運行程序,是不是已經出現了一個四視圖的原型了(如下圖所示)?由于沒有添加任何的渲染對象,因此四個視圖均為空的黑色窗口。
3.4 交互式圖像切分
該控件需要實現兩個基本功能:一是交互式圖像切分;二是切片圖像提取。第一個功能,采用vtkResliceCursorWidget和vtkResliceCursor類。通常兩個類同時使用,每個vtkResliceCursorWidget對象中需要定義相應的vtkResliceCursor對象。vtkResliceCursorWidget通過定義的“十”字坐標軸,提供用戶方便的切分和交互方式,支持坐標軸的旋轉和平移;當坐標系發生改變時即調用vtkResliceCursor來進行圖像切分并進行更新到vtkRenderer對象中。
第二個功能采用vtkImagePlaneWidget實現。該類內部定義了一個vtkImageReslice對象,利用vtkResliceCursor中定義的切分平面來切分圖像,在其內部通過紋理映射來繪制到一個平面上,并在用戶指定的vtkRenderer進行顯示。
另外前面在定義可視化管線時,我們并沒有定義相關的vtkActor。這主要是因為在視圖顯示圖像時,都是通過相關的widget來實現,我們只需要為widget對象設置相應的vtkRenderer即可,在其內部會自動生成相應的vtkActor對象。根據以上分析在頭文件中定義相關對象并在PreSubclassWindow函數中進行實例化。
[cpp] view plaincopy?在實例化時需要注意,該視圖類在默認情況下渲染的是vtkResliceCursorWidget對象的輸出,因此需要為vtkResliceCursorWidget對象指定相應的vtkRenderer對象,
[cpp] view plaincopy這樣在vtkResliceCursorWidget對象內部會將切片圖像轉化為一個vtkActor對象并添加至指定的vtkRenderer對象中進行顯示渲染;而vtkImagePlaneWidget中也有一個重要的函數vtkImagePlaneWidget::SetDefaultRenderer(vtkRenderer*) 用于設置相應的vtkRenderer來顯示切片。根據本程序的設計,vtkImagePlaneWidget產生的切片需要在三維場景中顯示,因此這里并沒有調用。也就是說,在默認情況下,本控件類只顯示二維切片圖像。
3.5 添加圖像設置函數并初始化圖像切分對象
該控件類需要從外部設置相應的處理圖像,因此提供一個接口函數來供外部調用。在3.4中僅僅定義和創建了交互式圖像切分對象,并沒有設置相應的輸入數據,因此每次有新的數據傳入時,需要為其進行初始化。
[cpp] view plaincopySetupReslice()函數中首先對vtkImagePlaneWidget對象進行初始化,
vtkImagePlaneWidget::SetInput() 設置輸入圖像
vtkImagePlaneWidget::SetPlaneOrientation()設置切片的方向
vtkImagePlaneWidget::SetSliceIndex() 設置當前方向上默認的層號
注意一定要在設置完輸入圖像和相應的交互對象后,再開啟vtkImagePlaneWidget,相應的函數為:
vtkImagePlaneWidget::On();
vtkImagePlaneWidget::InteractionOn();
?
然后設置vtkResliceCursor對象,
vtkResliceCursor::SetInput() 設置輸入圖像
vtkResliceCursor::SetCenter() 設置默認的切分中心點
vtkResliceCursor::SetThickMode()設置切分模式
SetThickMode(int)函數當參數為0時每次切分得到一個單層的圖像切片;而當參數為1時開啟厚度模式,可以通過SetThickness()來設置切片厚度,即得到的是一個多層的厚度圖像。這里我們關閉厚度模式。
m_ResliceCursorRep 是一個vtkResliceCursorThickLineRepresentation對象,即在圖像切分時屏幕上顯示的“十”字坐標軸,利用該對象設置其關聯的vtkResliceCursor對象和設置切分的方向。SetDefaultRenderer()用于設置顯示切分結果所需要的vtkRenderer。我們這里設置為類成員變量m_Renderer,即每次鼠標進行切分的結果在當前窗口定義的可視化管線中進行顯示。同樣,在設置vtkResliceCursor對象的輸入圖像后,開啟vtkResliceCursorWidget對象:
vtkResliceCursorWidget::SetEnabled();
m_Direction為方向標志,取值分別為0,1和2,分別代表X軸,Y軸和Z軸方向,可以通過設置不同的方向值,來實現橫斷面視圖,矢狀面視圖,冠狀面視圖。
這樣一個具有圖像切分功能的控件已經完成,該控件支持用戶設置切片方向和圖像輸入,運行時在每個視圖中根據用戶設置的方向顯示相應的十字坐標軸,用戶可以拖動該十字來進行交互。當然,我們還沒有實現視圖之間的同步。
4.完善CvtkDialogDlg類
4.1 四視圖初始化
首先在CvtkDialogDlg類的初始化函數OnInitDialog函數中初始化四個視圖控件類對象:
[cpp] view plaincopy?從代碼可以看出,我們為每個視圖指定了切片的方向,然后為每個視圖的vtkImagePlaneWidget類對象指定相應的交互對象和繪制對象vtkRenderer。在前面我們也提到過,vtkImagePlaneWidget需要為其指定相應的vtkRenderer類對象,用來顯示圖像切片。這里vtkImagePlaneWidget的結果需要在三維視圖中進行顯示,因此通過SetDefaultRenderer()函數將三維視圖的vtkRenderer對象設置到每個二維視圖中的vtkImagePlaneWidget對象中,這樣即可在三維視圖中顯示三個方向的切片圖像。
另外需要注意的是,
m_SagittalView.SetResliceCursor(m_AxialView.GetResliceCursor());
m_CoronalView.SetResliceCursor(m_AxialView.GetResliceCursor());
這里分別將m_SagittalView和m_CoronalView類中的vtkResliceCursor對象都設置為m_AxialView的vtkResliceCursor對象,即在三個二維視圖中統一使用一個vtkResliceCursor。我們知道在二維視圖類中定義了vtkResliceCursorWidget對象,該對象通過用戶交互,利用vtkResliceCursor來實現圖像的交互式切分。但是在圖像切分的同時,三個方向應該保持同步,即當一個圖像切分中心發生改變時,其他兩個方向的視圖應該及時進行更新來保持同步。因此,這里將三個視圖中統一使用一個共同的vtkResliceCursor對象,便于實現視圖的同步。
4.2 四視圖同步
由于三個二維視圖使用同一個vtkResliceCursor對象,因此當任意一個vtkResliceCursor對象的圖像切分參數(即切分平面參數:中心點和法向)發生改變時,其他視圖同樣會發生改變。不過由于在視圖內部并不能直接檢測參數變化,我們需要通過VTK的Observer-Command模式來監聽參數改變的消息,并進行相應的處理。切分參數的改變,是通過用戶拖動或者旋轉視圖“十”字坐標軸來實現,此時vtkResliceCursorWidget內部會產生一個消息vtkResliceCursorWidget::ResliceAxesChangedEvent,我們只需要監聽該消息即可。因此,定義一個vtkCommand類,為vtkResliceCursorWidget::ResliceAxesChangedEvent實現相應的處理操作。
此外,當用戶改變圖像切分的坐標軸時(旋轉坐標軸或者平移坐標系),圖像切分平面會產生相應的改變,如果將新的切分平面更新到二維視圖的vtkImagePlaneWidget對象中,即可實現三維視圖的同步更新操作。基于以上設計,實現一個vtkCommand子類,來監聽vtkResliceCursorWidget::ResliceAxesChangedEvent消息,并實現相應的更新操作。
[cpp] view plaincopy
每當監聽到vtkResliceCursorWidget::ResliceAxesChangedEvent消息后,即可執行vtkResliceCursorCallback::Execute函數來實現視圖同步與刷新操作。該函數更新三個視圖中vtkImagePlaneWidget對象的切分平面參數,即利用每個視圖中vtkResliceCursorThickLineRepresentation::GetPlaneResource()
獲取當前方向的紋理映射平面對象vtkPlaneSource,并獲取平面的原點,以及定義平面內坐標軸的兩個點,
vtkPlaneSource::GetOrigin(),
vtkPlaneSource::GetPoint1();
vtkPlaneSource::GetPoint2();
將三個點坐標設置到到vtkImagePlaneWidget的切分平面對象中,通過調用函數
vtkImagePlaneWidget::UpdatePlacement()
更新切分平面的空間位置和姿態,從而得到新的圖像切面,最后刷新四個視圖完成四視圖的同步與更新。
定義完該類后,在初始化函數BOOL CvtkDialogDlg::OnInitDialog()中,定義該類對象并設置相應的監聽消息實現視圖同步。
[cpp] view plaincopy4.3 數據管理
采用樹控件來管理圖像文件,為其響應右鍵單擊消息和左鍵單擊消息。右鍵單擊消息響應樹控件的根節點的右鍵菜單功能,主要定義了圖像讀取函數,圖像保存,圖像清空等功能。當圖像讀入后,即將其設置到三個視圖中,并調用相應的CvtkView::Render()函數來更新視圖(CvtkView定義的一個視圖刷新函數)。樹節點的左鍵按下消息,實現圖像的切換;每次點擊一個圖像節點,即將當前的圖像更新至四視圖中。
至此,一個完整的四視圖圖像顯示Demo已經完成。該Demo中能夠實現橫斷面,矢狀面和冠狀面三個方向的二維切片顯示,以及三個切片的三維顯示;實現了四視圖的同步顯示,即一個圖像中的切分平面發生變化時,其他視圖會同步進行更新。在實現過程中,基于兩個VTK Widget類進行實現,分別是vtkResliceCursorWidget和vtkImagePlaneWidget。vtkResliceCursorWidget集成了一個vtkResliceCursorThickLineRepresentation對象用戶交互調整切分平面,一個vtkResliceCursor對象根據用戶設置的切分平面用于圖像切分;而vtkImagePlaneWidget同樣根據vtkResliceCursor對象根據用戶設置的切分平面利用內部定義的vtkImageReslice來計算切分圖像并進行顯示。本實例僅僅是實現了一個四視圖的Demo程序,還存在許多需要完善的地方,各位讀者有興趣的話,可以在此基礎上進行修改和完善。
?
==========歡迎轉載,轉載時請保留該聲明信息==========
版權歸@東靈工作室所有,更多信息請訪問東靈工作室
教程系列導航:http://blog.csdn.net/www_doling_net/article/details/8763686
================================================
?
總結
以上是生活随笔為你收集整理的基于VTK的MFC应用程序开发(3)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用阿基米德螺线进行数据可视化
- 下一篇: 字节顺序:高位优先(big-endian