ITK學習筆記1:從一個簡單的例子出發學習ITK
看完東靈的vtk的相關教程,接下來就是對itk的學習了。不過網上itk的學習資源還是零零散散的,還是需要自己進行整合。
專案地址: Alxemade/ITKLearning
1. 參考資源
- CSDN: ljp1919的專欄-ITK學習筆記
- CSDN: rabbitbride的專欄-ITK醫學影象處理
- CSDN: inter_peng的專欄-ITK學習
- CSDN: 本blog的醫學影象處理演算法彙總
- ITK官方例子
- ITK Software Guide
- ITK 4.13.0 Documentation
- Insight Journal (ISSN 2327-770X)
- 知乎:VTK ITK OPENCV,從影象處理的角度來說,哪種用的人多?
先把這些資源稍微整理一下,以後自己查詢起來也是很方便的。
2. 從一個簡單的例子出發
按照之前學習vtk的套路,我們首先也是編寫CmakeLists和cpp檔案,然後使用cmake軟體進行編譯,然後再在vs2017裡面進行編譯我們檔案,其實方法是一樣的。這裡具體的步驟就不太詳細介紹了,這裡貼出程式碼。
2.1 源程式
這裡實現的功能也是非常簡單的,就是使用itk讀取一幅影象,然後在儲存一幅影象。
2.1.1 CmakeLists檔案
cmake_minimum_required (VERSION 2.8)
project(ITKImageReader)
find_package(ITK REQUIRED)
include(${ITK_USE_FILE})
add_executable(${PROJECT_NAME} ITKImageReader.cpp)
target_link_libraries(${PROJECT_NAME} ${ITK_LIBRARIES})
這裡程式碼沒有什麼好解釋的,常規操作。檔名是ITKImageReader
,可以參考之前的東靈的文章以及cmake的基本語法。
2.1.2 CPP檔案
#include <itkImage.h>
#include <itkImageFileReader.h>
#include <itkImageFileWriter.h>
int main(int argc, char* argv[])
{
const unsigned int Dimension = 2; //定義影象維數
//typedef unsigned char PixelType; //定義畫素型別
typedef itk::RGBPixel< unsigned char > PixelType;
typedef itk::Image< PixelType, 2 > ImageType;
typedef itk::ImageFileReader< ImageType > ReaderType;
typedef itk::ImageFileWriter< ImageType > WriterType;
ReaderType::Pointer reader = ReaderType::New();
WriterType::Pointer writer = WriterType::New();
reader->SetFileName("E:\\XC\\itk\\Examples\\Demo1_itkImageReader\\bin\\hua.jpg");
writer->SetFileName( "E:\\XC\\itk\\Examples\\Demo1_itkImageReader\\bin\\hua2.jpg");
ImageType::Pointer image = reader->GetOutput();
writer->SetInput( image );
writer->Update(); // Aliased to the Write() method to be consistent with the rest of the pipeline.
return 0;
}
雖然程式碼不是太長,但是乍一看還是覺得挺複雜的,這裡聽我給您娓娓道來。
前面幾行沒什麼好說的,主要是包含一些標頭檔案,然後我們著重從第9行開始。如果c++大牛可以不用看下面的內容了,個人還是在c++的摸索的路上~
註解:
9-12行其實功能大抵是相同的,無非就是定義一些型別。看懂程式碼最好的方法就是進入它的定義然後理解程式碼。所以我們進入第9行RGBPixel
定義:
這裡一看原來RGNPixel
是在itk
這個名稱空間裡啊,所以如果想要訪問它自然使用itk::RGBPixel
,關於名稱空間的使用,可以參考我之前的博文C++學習筆記1–名稱空間的使用, 看了大概就會明白是怎麼一回事了,這裡使用公共的itk
,應該是名稱空間的擴充。 緊接著,我們在看57,58行程式碼,這裡是一個模板類,使用預設引數unsigned short
, 程式碼class ITK_TEMPLATE_EXPORT RGBPixel:public FixedArray< TComponent, 3 >
還是一個類模板繼承類模板的情況,這個可以參考這篇部落格模板類的繼承,所以我們使用<unsigned char>
來例項化一個模板,最後利用關鍵字typedef
取一個別名。後面的10-12類似的操作。
13-17行定義二個SmartPointer
變數讀取指定路徑資料。
19-22 讀取並寫入影象資料。
2.2 一句有趣的程式碼
在閱讀ITK的原始碼的過程中,發現裡面存在大量的模板類,一個繼承一個,有點頭大。在其中,發現有一句挺有意思的,這裡貼出來,一起學習一下。
這裡比如第91行程式碼:
typedef typename TOutputImage::SizeType SizeType;
這裡typedef
用法不用多說, 就是給變數取一個別名。但是為什麼還需要出現一個typename
自己就不是很明白了。在一番google之後,也是大概瞭解為什麼這麼用。
在類外部訪問類中的名稱時,可以使用類作用域操作符,形如MyClass::name的呼叫通常存在三種:靜態資料成員、靜態成員函式和巢狀型別:
struct MyClass {
static int A;
static int B();
typedef int C;
}
MyClass::A
, MyClass::B
, MyClass::C
分別對應著上面三種。
所以在模板類的使用情況下, 只有在等到模板例項化的情況是編譯器才會知道具體是什麼型別的資料,這裡使用typename
真正目的就是讓編譯器知道這是一種資料型別,而不是靜態成員函式或者靜態成員變數。推薦兩篇介紹這個比較好的文章。
3. 結果
我們讀入hua.jpg
最後就是在當前目錄下也生成了一幅hua2.jpg
檔案。
真的當看程式碼有疑惑的時候,還是需要刨根問底的,而不是打馬虎眼。