VTK教程系列:VTK基礎及應用開發教程

        因爲OpenCV不能使用,只能使用VTK庫的圖像處理庫,暫時尚未找到其餘能夠全面替代的庫;html

       CSDN東靈工做室:http://blog.csdn.net/www_doling_net/article/details/8763686git


複製其第一篇:算法

注:之後將陸續分享一些對經典圖像、圖形算法的介紹,編程

新浪微博: @東靈工做室數據結構

郵箱:www_doling_net@163.comapp

ITK、VTK研究羣:209215671ide

 

系列一 《VTK基礎及應用開發教程》函數

基礎入門篇:工具

1.        從零開始學習VTKpost

2.        VTK編譯安裝

3.        VTK基礎概念

1)      VTK基礎概念(1)

2)      VTK基礎概念(2)

4.        VTK可視化管線

1)      VTK可視化管線(1)

2)      VTK可視化管線(2)

5.        VTK在圖像處理中的應用

1)      VTK在圖像處理中的應用(1)

2)      VTK在圖像處理中的應用(2)

3)      VTK在圖像處理中的應用(3)

4)      VTK在圖像處理中的應用(4)

5)      VTK在圖像處理中的應用(5)

6)      VTK在圖像處理中的應用(6)

7)      VTK在圖像處理中的應用(7)

8)      VTK在圖像處理中的應用(8)

9)      VTK在圖像處理中的應用(9)

6.        VTK基本數據結構

1)      VTK基本數據結構(1)

2)      VTK基本數據結構(2)

3)      VTK基本數據結構(3)

4)      VTK基本數據結構(4)

5)      VTK基本數據結構(5)

高階應用篇:

7.      基於VTKQt應用程序開發

8.      基於VTK&ITKQt應用程序開發

9.      基於VTKMFC應用程序開發

1)      基於VTKMFC應用程序開發(1)

2)      基於VTKMFC應用程序開發(2)

3)      基於VTKMFC應用程序開發(3)


基於VTK的Qt應用程序開發

分類: VTK應用示例   1916人閱讀  評論(23)  收藏  舉報

目錄(?)[+]

VTK附帶的程序示例中大可能是基於控制檯的,做爲可視化開發工具包,VTK也能夠與不少流行的GUI開發工具整合,好比MFC、Qt(題外話:Qt已經被Digia從諾基亞手中收購了,Qt如今的連接是: http://qt-project.org/,也有已經編譯好的版本: http://code.google.com/p/qt-msvc-installer/downloads/list直接下載安裝。可能由於大學課程裏會教授MFC的內容,一些非計算機專業的會偏向於採用MFC,我的以爲,對於非計算機專業而言,若是必定要選擇一種GUI工具作開發的話,建議用Qt,容易上手,學習週期短)、FLTK( http://www.fltk.org/,FLTK也是跨平臺的,是一種比較輕便的GUI工具,VTK官方發佈版本沒有提供對FLTK的接口,但能夠藉助類 vtkFlRenderWindowInteractor,來實現VTK與FLTK的整合)等等,VTK的源碼目錄裏(VTK-5.10\Examples\GUI)包含有VTK與Qt、MFC、Tcl等工具的整合。考慮到VTK對Qt的特殊照顧(VTK提供了不少針對Qt的類能夠很是方便地與Qt整合),以及Qt自身的一些性質(如易用性、跨平臺等),咱們參考了VTK自帶的一些例子,給出了VTK與Qt整合的詳細步驟。

1.   CMakeLists.txt文件

咱們已經知道了VTK工程的管理是用CMake的,而Qt自身有qmake工具,若是對於一些小工程而言,單純的Qt程序用qmake來構建工程,確實很方便,但若是隨着工程複雜度的增長以及工程依賴其餘的函數庫時,使用CMake來管理工程或許是一個明智的選擇。並且隨着你對CMake語法的瞭解,你會發現用CMake來管理工程是一件很是棒的事情。

咱們先看看對於單純的Qt工程,怎麼來寫CMakeLists.txt腳本文件。

1.1 用CMake來管理Qt工程

官方對於這個話題給出的解釋在這裏。咱們引用一下這篇博文的圖,而後給出每句CMakeLists.txt腳本的註釋,結合這個圖以及腳本的註釋,相信你應該能明白了。

 

[plain]  view plain copy
  1. #----------------------------------------------  
  2. # 下面這兩行,沒什麼好解釋的  
  3. cmake_minimum_required( VERSION 2.8 )  
  4. project( YourProjectName )  
  5.    
  6. #----------------------------------------------  
  7. # 下面這兩行,也沒什麼好解釋的  
  8. find_package( Qt4 REQUIRED )  
  9. include( ${QT_USE_FILE} )  
  10.    
  11. #----------------------------------------------  
  12. # 程序全部源文件。<TODO:在此處添加源文件>  
  13. # 定義變量Project_SRCS,其值爲所列的文件列表  
  14. SET( Project_SRCS  
  15.     main.cpp  
  16.   )  
  17.    
  18. #----------------------------------------------  
  19. # 程序全部UI文件。<TODO:在此處添加UI文件>  
  20. # 定義變量Project_UIS,其值爲所列的文件列表  
  21. SET( Project_UIS  
  22.     YourQtWindows.ui  
  23. )  
  24.    
  25. #----------------------------------------------  
  26. # 全部包含Q_OBJECT的頭文件。<TODO:在此處添加頭文件>  
  27. # 定義變量Project_MOC_HDRS,其值爲所列的文件列表  
  28. SET( Project_MOC_HDRS  
  29.     YourQtProjectFiles.h  
  30. )  
  31.    
  32. #-----------------------------------------------  
  33. # 經過Qt的uic.exe生成UI文件對應的ui_XXXX.h文件  
  34. # 將生成的ui_XXXX.h文件放在變量Project_UIS_H裏,  
  35. # QT4_WRAP_UI就是幹這個事情。  
  36. QT4_WRAP_UI( Project_UIS_H ${Project_UIS} )  
  37.    
  38. #-----------------------------------------------  
  39. # 經過Qt的moc.exe生成包含Q_OBJECT的頭文件對應的  
  40. # moc_XXXX.cxx文件,將生成的moc_XXXX.cxx文件放在  
  41. # 變量Project_MOC_SRCS裏。QT4_WRAP_CPP就是幹這個事情。  
  42. QT4_WRAP_CPP( Project_MOC_SRCS ${Project_MOC_HDRS} )  
  43.    
  44. #-----------------------------------------------  
  45. # Qt的MOC和UIC程序生成的moc_XXXX.cxx和ui_XXXX.h  
  46. # 等文件是存放在CMake的「Where to build the binaries"  
  47. # 裏指定的目錄裏,因此必須都這些路徑包含進來。  
  48. INCLUDE_DIRECTORIES( ${Project_SOURCE_DIR}  
  49.                      ${CMAKE_CURRENT_BINARY_DIR}  
  50.                    )  
  51.    
  52. #-----------------------------------------------                            
  53. # Qt程序若是有資源文件(*.qrc),要包含資源文件,  
  54. # 而後用Qt的rcc.exe生成相應的qrc_XXXX.cpp文件。  
  55. # QT4_ADD_RESOURCES就是幹這個事情。  
  56. SET( Project_RCCS YourProject.qrc)  
  57. QT4_ADD_RESOURCES( Project_RCC_SRCS ${Project_RCCS})  
  58.    
  59. #-----------------------------------------------  
  60. # 根據程序的cpp文件、頭文件以及中間生成的ui_XXXX.h、  
  61. # moc_XXXX.cxx、qrc_XXXX.cxx等生成可執行文件,並連接  
  62. # Qt的動態庫(Qt的動態庫都定義在QT_LIBRARIES變量裏了)  
  63. ADD_EXECUTABLE( YourProjectName  
  64.                 ${Project_SRCS}  
  65.                 ${Project_UIS_H}  
  66.                 ${Project_MOC_SRCS}  
  67.                 ${Project_RCC_SRCS}                             
  68.               )  
  69. TARGET_LINK_LIBRARIES ( YourProjectName ${QT_LIBRARIES} )  

1.2 用CMake來管理Qt與VTK工程

咱們在上面的基礎上添加VTK相關的CMake腳本文件,以下:

[plain]  view plain copy
  1. #----------------------------------------------------------------------------------  
  2. cmake_minimum_required( VERSION 2.8 )  
  3. project( CombineQtAndVTK )  
  4.    
  5. #----------------------------------------------------------------------------------  
  6. find_package( VTK REQUIRED )  
  7. find_package( Qt4 REQUIRED )  
  8.    
  9. include( ${VTK_USE_FILE} )  
  10. include( ${QT_USE_FILE} )  
  11.    
  12. #----------------------------------------------------------------------------------  
  13. SET( PROJECT_SRCS  
  14.     main.cpp  
  15.     ProjectMainWindow.cpp  
  16.     )  
  17.    
  18. SET( PROJECT_UIS  
  19.     ProjectMainWindow.ui  
  20. )  
  21.    
  22. SET( PROJECT_MOC_HDRS  
  23.   ProjectMainWindow.h  
  24. )  
  25.    
  26. #----------------------------------------------------------------------------------  
  27. QT4_WRAP_UI( PROJECT_UIS_H  
  28.              ${PROJECT_UIS}  
  29.            )  
  30.    
  31. QT4_WRAP_CPP( PROJECT_MOC_SRCS  
  32.               ${PROJECT_MOC_HDRS}  
  33.             )  
  34.    
  35. #----------------------------------------------------------------------------------  
  36. INCLUDE_DIRECTORIES( ${PROJECT_SOURCE_DIR}  
  37.                      ${CMAKE_CURRENT_BINARY_DIR}  
  38.                      ${VTK_DIR}  
  39.                    )  
  40.    
  41. ADD_EXECUTABLE( CombineQtAndVTK  
  42.                 ${PROJECT_SRCS}  
  43.                 ${PROJECT_UIS_H}  
  44.                 ${PROJECT_MOC_SRCS}  
  45.               )  
  46.    
  47. TARGET_LINK_LIBRARIES ( CombineQtAndVTK  
  48.   ${VTK_LIBRARIES}  
  49.   QVTK  
  50.   )  

以上的腳本除了紅色字體標註的跟1.1註釋的不太像以外,其餘的都同樣,再也不解釋。

1.3 CMake腳本里增長工程環境變量的加載

不少非計算機專業的用戶在使用VTK進行編程時,常常會碰到相似下圖所示的一些錯誤。

 

碰到這樣的錯誤之後,可能不少用戶就不知道怎麼處理了,其實上面的提示信息已經寫得很是清楚了,就是缺乏「vtkCommon.dll」文件。可是又有人會說:個人電腦裏明明有這個文件存在啊,爲何會找不到呢?

通常的解決方法多是:

方法一:將缺乏的dll文件所有拷貝的工程的Debug或者Release目錄下(拷貝的時候要注意你編譯的VTK是Debug版本的仍是Release版本的,若是拷錯的話,又會出現其餘不可預知的錯誤了)。可是這個方法是你每建一個工程,運行工程以前得把缺乏的動態庫文件又要拷貝過去,若是你不嫌麻煩的話,能夠採用。

方法二:將缺乏的dll文件所有拷貝到Windows系統的目錄下,即C:\Windows\system32或者C:\Windows\system目錄下,這個方法是你拷貝一次,之後再基於你拷貝的VTK動態庫的工程運行的時候問題都解決了。但它一樣有一個問題,假如你電腦裏的VTK升級成別的版本,從新編譯了一份動態庫,或者是同時在你電腦裏編譯了好幾個版本的VTK,這個時候就有點凌亂了。

爲何這兩種方法均可以解決問題?原來動態編譯的程序在啓動的時候,會搜索程序所在的目錄以及系統環境變量PATH所列的目錄,若是這些目錄有該程序須要的動態庫時,就加載它們,若是沒有,就提示沒法加載相應動態庫的錯誤。

能夠在工程的CMakeLists.txt文件裏添加一些腳本,把系統的PATH環境變量做一些更改,在工程啓動以前加載這些環境變量。也就是(在工程的CMakeLists.txt最後添加):

[plain]  view plain copy
  1. #-----------------------------------------------------------------------------------  
  2. # Construct a list of paths containing runtime directories for project applications on Windows  
  3. set(PROJECT_RUNTIME_PATH  "${VTK_LIBRARY_DIRS}/@VS_BUILD_TYPE@;  
  4. ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/@VS_BUILD_TYPE@"  
  5.     )  
  6. if(QT4_FOUND)  
  7.   set(PROJECT_RUNTIME_PATH "${PROJECT_RUNTIME_PATH};${QT_LIBRARY_DIR}/../bin")  
  8. endif()  
  9.    
  10. include(CreateWindowsBatchScript.cmake)  
  11.    
  12. # If we are under Windows, create two batch files which correctly  
  13. # set up the environment for the application and for Visual Studio  
  14. if(WIN32)  
  15.   set(VS_SOLUTION_FILE "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.sln")  
  16.   foreach(VS_BUILD_TYPE debug release)  
  17.     CreateWindowsBatchScript("${CMAKE_SOURCE_DIR}/StartVS.bat.in"  
  18.       ${PROJECT_BINARY_DIR}/StartVS_${VS_BUILD_TYPE}.bat  
  19.       ${VS_BUILD_TYPE})  
  20.   endforeach()  
  21. endif(WIN32)  


以上的腳本也不是特別複雜,但提到了兩個文件:CreateWindowsBatchScript.cmake以及StartVS.bat.in。這兩個文件的內容分別是:

CreateWindowsBatchScript.cmake:

 

[plain]  view plain copy
  1. function(CreateWindowsBatchScript in out build_type)  
  2.   if(VTK_DIR)  
  3.     set(VTK_BIN_DIR "${VTK_DIR}/bin/${build_type}")  
  4.   else()  
  5.     set(VTK_BIN_DIR)  
  6.   endif()  
  7.    
  8.   set(VS_BUILD_TYPE ${build_type})  
  9.   configure_file(${in} ${out} @ONLY)  
  10.   # substitute again  
  11.   configure_file(${out} ${out} @ONLY)  
  12. endfunction()  

StartVS.bat.in

 

[plain]  view plain copy
  1. @set CL=/D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE  
  2. @set LINK=/LARGEADDRESSAWARE  
  3.    
  4. PATH=@PROJECT_RUNTIME_PATH@;%PATH%  
  5. "@VS_SOLUTION_FILE@"  

將工程經過CMake的configure->generate之後,便可生成StartVS_debug.bat和StartVS_release.bat兩個腳本文件。若是你要編譯、運行Debug版本的工程,即雙擊StartVS_debug.bat文件打開對應的工程,同理,Release版本的也同樣。一旦按這種方式打開相應的工程,就不用再擔憂相似「沒法加載***.dll文件」的錯誤了。若是你的程序還增長了ITK等函數庫,也能夠照着上面的腳本做相應的修改。

注意:使用時將CreateWindowsBatchScript.cmakeStartVS.bat.in兩個文件與工程的CMakeLists.txt放在同一級目錄裏。即相似下圖的目錄結構:

 

2.   用QVTKWidget整合Qt&VTK

Qt與VTK的整合可使用VTK提供的類QVTKWidget,看這個類名就知道這個類其實就是一個Qt裏的Widget (QVTKWidget派生自QWidget),因此能夠把它看成普通的Qt裏的Widget來使用,甚至能夠在Qt Designer裏像Qt的其餘標準控件同樣拖來拖去。

2.1 在Qt Designer裏集成

要實現QVTKWidget在Qt Designer裏像Qt的其餘標準控件同樣拖來拖去,須要把編譯生成的QVTKWidgetPlugin.dll/QVTKWidgetPlugin.lib(Release版本)複製到Qt的安裝目錄裏的plugins\designer目錄下。完了之後,你會在Qt Designer裏面看到以下的控件:

 

2.2 讀入一幅圖像,並在Qt界面上顯示

接下來,咱們來完成一個小功能,就是讀入一幅JPG圖像,而後在Qt界面上,用VTK來顯示。功能很是簡單,程序也很是簡單。上代碼:

ProjectMainWindow.h:

[cpp]  view plain copy
  1. #ifndef Project_MainWindow_H  
  2. #define Project_MainWindow_H  
  3.    
  4. #include <QMainWindow>  
  5. #include "ui_ProjectMainWindow.h"  
  6.    
  7. #include <vtkSmartPointer.h>  
  8.    
  9. class vtkImageViewer2;  
  10. class vtkRenderer;  
  11.    
  12.    
  13. class ProjectMainWindow : public QMainWindow, public Ui::ProjectMainWindow  
  14. {  
  15.        Q_OBJECT  
  16.    
  17. public:  
  18.        ProjectMainWindow();  
  19.        ~ProjectMainWindow();  
  20.    
  21. private slots:  
  22.        //響應打開圖像文件的槽函數  
  23.        void onOpenSlot();  
  24.    
  25. private:  
  26.        vtkSmartPointer< vtkImageViewer2 > m_pImageViewer;  
  27.        vtkSmartPointer< vtkRenderer > m_pRenderder;  
  28. };  
  29.    
  30. #endif  

ProjectMainWindow.cpp:

[cpp]  view plain copy
  1. #include "ProjectMainWindow.h"  
  2.    
  3. #include <QFileDialog>  
  4. #include <QDir>  
  5.    
  6. #include <vtkRenderWindow.h>  
  7. #include <vtkRenderer.h>  
  8. #include <vtkImageViewer2.h>  
  9. #include <QVTKWidget.h>  
  10. #include <vtkJPEGReader.h>  
  11. #include <vtkImageActor.h>  
  12.    
  13. ProjectMainWindow::ProjectMainWindow()  
  14. {  
  15.        setupUi(this);  
  16.    
  17.        m_pImageViewer  = vtkSmartPointer< vtkImageViewer2 >::New();  
  18.        m_pRenderder      = vtkSmartPointer< vtkRenderer >::New();  
  19.    
  20.        // 設置m_QVTKWidget的渲染器  
  21.        m_QVTKWidget->GetRenderWindow()->AddRenderer(m_pRenderder);  
  22.    
  23.        //鏈接打開的信號與相應的槽  
  24.        connect( m_OpenAction, SIGNAL( triggered() ), this, SLOT( onOpenSlot() ) );  
  25. }  
  26.    
  27. ProjectMainWindow::~ProjectMainWindow()  
  28. {  
  29. }  
  30.    
  31. void ProjectMainWindow::onOpenSlot()  
  32. {  
  33.        QString filter;  
  34.        filter = "JPEG image file (*.jpg *.jpeg)";  
  35.    
  36.        QDir dir;  
  37.        QString fileName = QFileDialog::getOpenFileName( this,   
  38.                                  QString(tr("打開圖像")), dir.absolutePath() , filter );  
  39.        if ( fileName.isEmpty() == true ) return;  
  40.    
  41.        // 支持帶中文路徑的讀取  
  42.        QByteArray ba = fileName.toLocal8Bit();  
  43.        const char *fileName_str = ba.data();  
  44.    
  45.        // 用vtkJPEGReader讀取JPG圖像  
  46.        vtkSmartPointer<vtkJPEGReader> reader = vtkSmartPointer<vtkJPEGReader>::New();  
  47.        reader->SetFileName(fileName_str);  
  48.    
  49.        // 將reader的輸出做爲m_pImageViewer的輸入,並設置m_pImageViewer與渲染器m_pRenderer的關聯  
  50.        m_pImageViewer->SetInput(reader->GetOutput());  
  51.        m_pImageViewer->UpdateDisplayExtent();  
  52.        m_pImageViewer->SetRenderWindow(m_QVTKWidget->GetRenderWindow());  
  53.        m_pImageViewer->SetRenderer(m_pRenderder);  
  54.        m_pImageViewer->SetupInteractor(m_QVTKWidget->GetRenderWindow()->GetInteractor());  
  55.        m_pImageViewer->SetSliceOrientationToXY(); //默認就是這個方向的  
  56.        m_pImageViewer->GetImageActor()->InterpolateOff();  
  57.        m_pRenderder->ResetCamera();  
  58.        m_pRenderder->DrawOn();  
  59.        m_QVTKWidget->GetRenderWindow()->Render();  
  60. }  

程序運行結果:

 

2.3 用vtkEventQtSlotConnect實現VTK事件與Qt槽的鏈接

類vtkEventQtSlotConnect能夠實現VTK的事件與Qt的槽函數的鏈接,VTK的事件主要在vtkCommand.h文件裏定義,包括鼠標單擊、鼠標雙擊、鼠標移動等等,如:

vtkCommand::ProgressEvent

vtkCommand::ErrorEvent

vtkCommand::WarningEvent

vtkCommand::PickEvent

vtkCommand::StartPickEvent

vtkCommand::EndPickEvent

vtkCommand::CharEvent

vtkCommand::KeyPressEvent

vtkCommand::KeyReleaseEvent

vtkCommand::LeftButtonPressEvent

vtkCommand::LeftButtonReleaseEvent

vtkCommand::MouseMoveEvent

……

具體的代碼實現:

[cpp]  view plain copy
  1. private slots:  
  2.        //響應鼠標移動的消息,實時輸出鼠標的當前位置  
  3.        void updateCoords(vtkObject* obj);  
  4.    
  5. private:  
  6.        vtkEventQtSlotConnect* m_Connections;  


源文件:

[cpp]  view plain copy
  1. //構造函數裏:  
  2.        m_Connections = vtkEventQtSlotConnect::New();  
  3.        m_Connections->Connect(m_QVTKWidget->GetRenderWindow()->GetInteractor(),  
  4.               vtkCommand::MouseMoveEvent,  
  5.               this,  
  6.               SLOT(updateCoords(vtkObject*)));  
  7.    
  8. //槽函數的實現  
  9. void ProjectMainWindow::updateCoords(vtkObject* obj)  
  10. {  
  11.        // 獲取交互器  
  12.        vtkRenderWindowInteractor* iren = vtkRenderWindowInteractor::SafeDownCast(obj);  
  13.    
  14.        // 獲取鼠標的當前位置  
  15.        int event_pos[2];  
  16.        iren->GetEventPosition(event_pos);  
  17.    
  18.        QString str;  
  19.        str.sprintf("x=%d : y=%d", event_pos[0], event_pos[1]);  
  20.        m_StatusBar->showMessage(str);  
  21. }  


程序運行結果:

 

示例代碼及該博文文檔下載地址:http://download.csdn.net/detail/www_doling_net/5137375



(3)二維圖像處理:05-VTK在圖像處理中的應用(4)

分類: VTK系列教程   948人閱讀  評論(4)  收藏  舉報

目錄(?)[+]

5.7 區域提取

5.7.1 提取感興趣區域 

感興趣區域(Volum of Interest)是指圖像內部的一個子區域。在VTK中vtkExtractVOI類實現由用戶指定的區域範圍提取圖像的子圖像。該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();

 

上例代碼實現了提取一副圖像的子區域。首先讀取一個圖像,並獲取圖像的維數。而後定義vtkExtractVOI對象,該對象接收兩個輸入一個是圖像數據,第二個是區域大小。設置區域大小的函數原型:

void SetVOI(int _arg1, int _arg2, int _arg3, int _arg4, int _arg5, int _arg6)

void SetVOI(int _arg[])

其參數是提取的區域各個方向的大小,共6個參數,依次表示x方向最小值,x方向最大值,y方向最小值,y方向最大值,z方向最小值和z方向最大值。上例中因爲讀取的是二維圖像,所以z方向的區域爲[0,0],而在x方向範圍爲[ dims[0]/4 , 3*dims[0]/4 ],y方向範圍爲[ dims[1]/4 , 3*dims[1]/4 ],即提取圖像原圖中間1/4圖像。執行結果以下:

 

圖5.18 提取感興趣區域

5.7.2 三維圖像切片提取

切片是指三維圖像中的一個切面對應的圖像。切面能夠是過圖像內部一點且平行於XY、YZ、XZ平面的平面,也能夠是任意的過三維圖像內部一點任意方向的平面。經過提取切片能夠方便的瀏覽和分析圖像內部組織結構,是醫學圖像瀏覽軟件中的一個重要的功能。在VTK中vtkImageReslice類實現圖像切片提取功能。下面首先看一段切片提取的代碼。

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讀取一副醫學三維圖像,並獲取獲得圖像範圍(extent),原點和像素間隔;由這三個參數能夠計算圖像的中心位置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 }; 提取斜切切片

 

注意使用這些變換矩陣的時候,須要將第四列替換爲切片通過圖像的一個點座標,上例中將圖像的中心添加到axialElements矩陣,並經過函數SetResliceAxes設置變換矩陣,SetOutputDimensionality(2)指定輸出的圖像爲一個二維圖像;而函數SetInterpolationModeToLinear()則指定了切面提取中的差值方式爲線性差值,另外該類中還提供了其餘的差值方式:

SetInterpolationModeToNearestNeighbor():最近鄰方式

SetInterpolationModeToCubic():三次線性差值

設置完畢後,執行Update()便可完成切面計算。運行結果以下圖:

 

圖5.19 切片提取

5.7.3 擴展

學習三維圖像切面的提取後,咱們在上節的程序上作一個擴展,實現一個稍微複雜的程序——經過滑動鼠標來切換三維圖像切片,這也是醫學圖像處理軟件中一個很基本的功能。實現該功能難點是怎樣在VTK中控制鼠標來實時提取圖像切片。在前面的章節中已經介紹觀察者/命令(Observer/Command)模式,咱們也採用這種機制來實現。VTK中鼠標消息是在交互類型對象(interactorstyle)中響應,所以經過爲交互類型對象(interactorstyle)添加觀察者(observer)來監聽相應的消息,當消息觸發時,由命令模式執行相應的回調函數。閒話少說,放代碼。

  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類,並覆蓋父類函數Execute()。該類提供了兩個接口:SetImageReslice和SetInteractor。SetImageReslice用以設置vtkImageSlice對象,vtkImageSlice根據設置的變換矩陣提取三維圖像切片。SetInteractor用以設置vtkRenderWindowInteractor,vtkRenderWindowInteractor類對象負責每次提取切片後刷新視圖。

下面咱們重點來看一下Execute函數,該函數提供了具體的切片提取功能。在該函數裏面,主要監聽了三個消息:

vtkCommand::LeftButtonPressEvent,

vtkCommand::LeftButtonReleaseEvent,

vtkCommand::MouseMoveEvent,

前兩個消息分別是鼠標左鍵的按下和彈起消息。當鼠標左鍵按下時,就設置切片提取標誌爲1,而當彈起時,將標誌置爲0。這樣在鼠標移動時,只有在肯定切片提取標誌爲1時,執行切片提取功能。

vtkCommand::MouseMoveEvent即爲鼠標移動消息。當檢測到該消息時,首先檢查切片提取標誌,當爲1時提取切片。提取切片時,須要爲vtkImageSlice對象設置變換矩陣。這裏在函數開始時,首先獲取了鼠標滑動的先後兩次點的位置lastPos和currPos。而後根據兩點的Y座標差deltaY,計算新的中心點center並變換至vtkImageSlice當前變換矩陣中,獲得變換中心點,將其設置到原來的變換矩陣matrix中,並設置到vtkImageSlice中,最後執行interactor->Render()便可不斷的根據鼠標移動刷新圖像。

Command對象定義完畢後,便可爲交互對象InteractorStyle添加觀察者,響應鼠標消息。這裏能夠在上節的程序上進行修改,前面代碼一致,只須要在最後添加以下代碼:

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添加觀察者來監控相應的消息,這裏主要是三個消息:

vtkCommand::LeftButtonPressEvent,

vtkCommand::LeftButtonReleaseEvent,

vtkCommand::MouseMoveEvent,

當響應到這三個消息時,當即執行vtkImageInteractionCallback的Execute函數,以便實現切片的實時提取和更新。完成之後,運行程序,當鼠標在圖像上移動時,會發現圖像會跟着鼠標的移動而變化,神奇吧?有興趣的話,還能夠實現YZ平面、XZ平面切片提取,甚至是任意方向的切面提取。

 

==========歡迎轉載,轉載時請保留該聲明信息==========

版權歸@東靈工做室全部,更多信息請訪問東靈工做室

相關文章
相關標籤/搜索