一、介紹
獲取DRR圖像是醫療圖像配准里面的一個重要的前置步驟:它的主要目的是,通過CT三維圖像,獲取模擬X射線影像,這個過程也被稱為數字影響重建。
在2D/3D的配准流程里面,需要首先通過CT三維圖像,能夠獲取任意位置的DRR圖像,然后去與已經獲取的X光平面圖像配准。
配准過程如下(下面的描述是不准確的,我只是描述了一種情況,即基於灰度的圖像配准算法的過程,並且可能會有紕漏):
1- 定義一個評價函數:即相似性測度函數,通過這個函數評價當前是否已經達到了配准的要求。
2- 不斷通過調整輸入參數得到不同角度下的DRR圖像。
3-通過1里面的評價函數判斷是否停止搜索
實際上這是一個循環,終止條件就是是否滿足相似性測度函數。
具體流程可以參考下面的流程圖:
二、DRR例子運行
在ITK5.0里面有一個相關的例子,我也是跑了好久才把程序跑通的,跑通以后就覺得做了許多的無用功......
說到底還是菜,跑一個程序就要兩周,醉了(好吧,吐槽完畢^-^)
1-找到例子,構建工程
按照之前第一個博客的過程安裝ITK之后,我們找到這個例子所在的位置:
ITK-5.0/Examples/Filtering/DigitallyReconstructedRadiograph1.cxx
例子的代碼也可以在這里找到:
把這個代碼復制出來單獨的構建一個工程(也是按照第一個博客里面的方法),但是這個時候的工程是無法運行的。
2- 代碼第一個問題
當我們運行程序的時候,程序無法運行, 提示說是因為沒有定義一個IOFactory
那么現在問題來了,IOFactory是和要寫入的圖像類型綁定的,
我們要把這個當做什么類型的文件寫入呢?
這個時候的writer是三維圖像,所以我們是不能夠寫成 bmp, jpg, 等等圖像的,我就是在這里嘗試很很多遍。
后來我們經過觀察發現,在程序里面有一個寫入到文件的一個代碼。

#ifdef WRITE_CUBE_IMAGE_TO_FILE const char *filename = "body.gipl"; using WriterType = itk::ImageFileWriter< InputImageType >; WriterType::Pointer writer = WriterType::New(); itk::GiplImageIOFactory::RegisterOneFactory(); writer->SetFileName( filename ); writer->SetInput( image ); try { std::cout << "Writing image: " << filename << std::endl; writer->Update(); } catch( itk::ExceptionObject & err ) { std::cerr << "ERROR: ExceptionObject caught !" << std::endl; std::cerr << err << std::endl; return EXIT_FAILURE; } #endif
最開始我根本沒有觀察過這個,但是后來發現是存在GiplIOFactory的,於是嘗試了一下,發現就可以了。
2.1 解決方法:
也就是說,我們需要做兩件事情:
<1>聲明一個GiplIOFactory
#include "itkGiplImageIOFactory.h"
itk::GiplImageIOFactory::RegisterOneFactory();
<2>傳入文件的寫入參數,比如我傳入的是 "1.gipl" (注意這里的后綴需要時 .gipl, 需要注意如何在VS里面如何傳入參數。)
這個時候程序已經可以成功運行了,並且DRR圖像被寫入到了工程所在的文件夾。
2.2-圖像觀察
這個時候我們已經可以觀察一下這個文件到底生成了一個什么東西:
我們通過3D slicer打開這個gipl文件,可以觀察到這個三維圖像和生成的DRR圖像:
<1> 三維圖像
<2>DRR圖像
可以看到,例子自己建立的圖像其實是一個中空的正方體。
3-第二個問題
但是我希望實現的是讀取的DRR圖像,所以需要讀取自己的CT圖像。
目前我擁有的是一系列的CT圖像,他們只是2D圖像,而歷程里面的reader則是讀取的三維圖像。
所以需要添加代碼,讀取自己的CT圖像,然后轉換為3D圖像。
添加代碼如下:

//定義像素類型,圖像類型,三維有符號數,定義指針 typedef signed short PixelType; const unsigned int Dimension = 3; typedef itk::Image< PixelType, Dimension > ImageType; typedef itk::ImageSeriesReader< ImageType > ReaderType; //聲明讀、寫 DICOM 圖 像 的 itk::GDCMImageIO對象 //itk::GDCMSeriesFileNames對象將生成並將構成所有體數據的切片的文件名進行排序 typedef itk::GDCMImageIO ImageIOType; typedef itk::GDCMSeriesFileNames NamesGeneratorType; ImageIOType::Pointer gdcmIO = ImageIOType::New(); NamesGeneratorType::Pointer namesGenerator = NamesGeneratorType::New(); //設置讀取路徑 //用文件名發生器生成被讀的文件名和被寫的文件名 namesGenerator->SetInputDirectory("D:\\Files\\Data\\3219032438350584179-8\\DICOM\\S258070\\S20"); const ReaderType::FileNamesContainer& filenames = namesGenerator->GetInputFileNames(); //設置DICOM圖像IO對象和被讀的文件名的列表 ReaderType::Pointer reader = ReaderType::New(); reader->SetImageIO(gdcmIO); reader->SetFileNames(filenames);
這個就是第三個博客里面的讀取程序,唉,傷心,浪費這么久。
具體對於這個程序的參數講解參加下一個博客六
三、參考鏈接
1-博客:https://blog.csdn.net/inter_peng/article/details/52155073
2-第一個圖來源:基於灰度的二維三維圖像配准方法及其在骨科導航手術中的應用