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


        由於OpenCV不能使用,只能使用VTK庫的圖像處理庫,暫時還沒有找到其他可以全面替代的庫;

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


復制其第一篇:

注:以后將陸續分享一些對經典圖像、圖形算法的介紹,

新浪微博: @東靈工作室

郵箱:www_doling_net@163.com

ITK、VTK研究群:209215671

 

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

基礎入門篇:

1.        從零開始學習VTK

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平面切片提取,甚至是任意方向的切面提取。

 

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

版權歸@東靈工作室所有,更多信息請訪問東靈工作室





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM