介紹如何在 Python 中以 ITK 模組讀取、寫入影像檔案,以及自行建立 ITK 影像的方法。
對於普通的影像檔案,可以使用 ITK 模組提供的 imread
函數來快速載入,其呼叫之後會立即執行載入檔案的動作,直接傳回影像內容:
import itk # 快速載入影像 image = itk.imread("image.tif")
若要快速寫入影像檔案,則可使用 imwrite
函數,其呼叫之後也是立即執行影像寫入動作:
# 快速寫入影像 itk.imwrite(image, "output.mha")
imread
在載入影像時會自動判斷像素類型與影像維度,所以設計者不需要另外指定,但在建立後續的 filter 時可能會需要指定影像的類型,這時候可使用 type
取得影像類型:
# 取得影像類型 ImageType = type(image) # 建立影像統計 Filter statsFilter = itk.StatisticsImageFilter[ImageType].New() statsFilter.SetInput(image) statsFilter.Update() # 顯示像素值範圍 print("Min = {}, Max = {}".format(statsFilter.GetMinimum(), statsFilter.GetMaximum()))
Min = 2, Max = 255
imread
載入影像類型imread
預設會自動判斷影像的維度與像素類型,但也可以手動指定像素類型:
import itk # 指定影像類型 PixelType = itk.F ImageType = itk.Image[PixelType, 2] # 快速載入影像 image = itk.imread("image.tif", PixelType) # 建立影像統計 Filter statsFilter = itk.StatisticsImageFilter[ImageType].New() statsFilter.SetInput(image) statsFilter.Update() # 顯示像素值範圍 print("Min = {}, Max = {}".format(statsFilter.GetMinimum(), statsFilter.GetMaximum()))
Min = 2.0, Max = 255.0
imread
讀取影像序列imread
也可以用來讀取多張影像組成的影像序列,最常見的狀況就是讀取多張 2D 切面影像檔案,組成一張 3D 影像:
# 影像序列 imageSeries = ["image_1.tif", "image_2.tif", "image_3.tif"] # 快速載入影像序列 image = itk.imread(imageSeries) # 顯示影像解析度 print(image.GetLargestPossibleRegion().GetSize())
itkSize3 ([256, 256, 3])
前述以 imread
讀取影像的方式雖然方便,但是它是一次讀取整張影像,不支援 ITK 串流(streaming)的處理方式,若要使用到串流等進階功能,就要改用 ImageFileReader
這種標準 ITK 的影像讀取方式:
import itk # 像素類型 PixelType = itk.UC # 影像維度 Dimension = 3 # 影像類型 ImageType = itk.Image[PixelType, Dimension] # 建立 ImageFileReader reader = itk.ImageFileReader[ImageType].New() # 設定檔案名稱 reader.SetFileName("image.tif") # 實際讀取檔案 reader.Update() # 取得影像內容 image = reader.GetOutput()
若要以標準的 ITK 方式寫入影像,可以使用 ImageFileWriter
:
# 建立 ImageFileWriter writer = itk.ImageFileWriter[ImageType].New() # 設定影像來源 writer.SetInput(reader.GetOutput()) # 設定輸出檔案名稱 writer.SetFileName("output.mha") # 設定串流處理 #writer.SetNumberOfStreamDivisions(4) # 啟用壓縮 writer.SetUseCompression(True) # 實際寫入檔案 writer.Update()
通常在使用 ITK 串流處理資料時,都會在最後的 ImageFileWriter
中以 SetNumberOfStreamDivisions
設定串流分割數,控制整個處理流程的串流方式。
若要以 ITK 標準的方式讀取多張 2D 切面影像檔案,然後組成一張 3D 影像,可以使用 ImageSeriesReader
來處理:
# 像素類型 PixelType = itk.UC # 影像維度 Dimension = 3 # 影像類型 ImageType = itk.Image[PixelType, Dimension] # 影像序列檔案名稱 imageSeries = ["image_1.tif", "image_2.tif", "image_3.tif"] # 建立 ImageSeriesReader seriesReader = itk.ImageSeriesReader[ImageType].New() # 設定影像序列檔案名稱 seriesReader.SetFileNames(imageSeries) # 載入影像序列 seriesReader.Update() # 取得影像內容 image = seriesReader.GetOutput() # 顯示影像解析度 print(image.GetLargestPossibleRegion().GetSize())
itkSize3 ([256, 256, 3])
若要以 ITK 標準的方式將一張 3D 影像採用多張 2D 切面影像的方式儲存,可以使用 ImageSeriesWriter
來處理:
# 像素類型 PixelType = itk.UC # 輸入影像類型 InputImageType = itk.Image[PixelType, 3] # 輸出影像類型 OutputImageType = itk.Image[PixelType, 2] # 讀取 3D 影像 image = itk.imread("3d_volume.tif", PixelType) # 輸出影像序列 seriesWriter = itk.ImageSeriesWriter[InputImageType, OutputImageType].New() seriesWriter.SetInput(image) seriesWriter.SetSeriesFormat("2d_image_%02d.tif") seriesWriter.Update()
這裡的 SetSeriesFormat
是設定輸出檔案名稱板模,板模的寫法請參考 C 語言的 printf
函數說明。
轉出的影像序列檔案名稱會是 2d_image_01.tif
、2d_image_02.tif
、2d_image_03.tif
…等。
以下是透過 ITK 的 Image
物件設定影像維度、類型、大小等屬性,自行配置記憶體空間,建立空白 ITK 影像的範例。
import itk # 影像維度 Dimension = 3 # 像素類型 PixelType = itk.ctype('unsigned char') # 影像類型 ImageType = itk.Image[PixelType, Dimension] # 建立影像物件 image = ImageType.New() # 設定影像起始索引 start = itk.Index[Dimension]() start[0] = 0 # X 軸起始索引值 start[1] = 0 # Y 軸起始索引值 start[2] = 0 # Z 軸起始索引值 # 設定影像大小 size = itk.Size[Dimension]() size[0] = 200 # X 軸影像長度 size[1] = 200 # Y 軸影像長度 size[2] = 200 # Z 軸影像長度 # 建立 Image Region 物件 region = itk.ImageRegion[Dimension]() region.SetSize(size) region.SetIndex(start) # 透過 Image Region 物件設定影像大小 image.SetRegions(region) # 配置記憶體空間 image.Allocate() # 輸出影像資訊 print(image)
Image (0x293bab0) RTTI typeinfo: itk::Image<unsigned char, 3u> Reference Count: 1 Modified Time: 9 Debug: Off Object Name: Observers: none Source: (none) Source output name: (none) Release Data: Off Data Released: False Global Release Data: Off PipelineMTime: 0 UpdateMTime: 0 RealTimeStamp: 0 seconds LargestPossibleRegion: Dimension: 3 Index: [0, 0, 0] Size: [200, 200, 200] BufferedRegion: Dimension: 3 Index: [0, 0, 0] Size: [200, 200, 200] RequestedRegion: Dimension: 3 Index: [0, 0, 0] Size: [200, 200, 200] Spacing: [1, 1, 1] Origin: [0, 0, 0] Direction: 1 0 0 0 1 0 0 0 1 IndexToPointMatrix: 1 0 0 0 1 0 0 0 1 PointToIndexMatrix: 1 0 0 0 1 0 0 0 1 Inverse Direction: 1 0 0 0 1 0 0 0 1 PixelContainer: ImportImageContainer (0x1d40a20) RTTI typeinfo: itk::ImportImageContainer<unsigned long, unsigned char> Reference Count: 1 Modified Time: 10 Debug: Off Object Name: Observers: none Pointer: 0x7f9da7d97010 Container manages memory: true Size: 8000000 Capacity: 8000000
ITK 也可以透過 GetImageFromArray
直接從 NumPy 的陣列資料建立影像:
import itk import numpy # 建立 NumPy 陣列 npArr = numpy.zeros([30, 20, 10], dtype=numpy.uint8) # 從 NumPy 陣列建立 ITK 影像 image = itk.GetImageFromArray(npArr, isVector=False) # 輸出影像資訊 print(image)
Image (0x55848c0) RTTI typeinfo: itk::Image<unsigned char, 3u> Reference Count: 1 Modified Time: 713 Debug: Off Object Name: Observers: none Source: (none) Source output name: (none) Release Data: Off Data Released: False Global Release Data: Off PipelineMTime: 0 UpdateMTime: 0 RealTimeStamp: 0 seconds LargestPossibleRegion: Dimension: 3 Index: [0, 0, 0] Size: [10, 20, 30] BufferedRegion: Dimension: 3 Index: [0, 0, 0] Size: [10, 20, 30] RequestedRegion: Dimension: 3 Index: [0, 0, 0] Size: [10, 20, 30] Spacing: [1, 1, 1] Origin: [0, 0, 0] Direction: 1 0 0 0 1 0 0 0 1 IndexToPointMatrix: 1 0 0 0 1 0 0 0 1 PointToIndexMatrix: 1 0 0 0 1 0 0 0 1 Inverse Direction: 1 0 0 0 1 0 0 0 1 PixelContainer: ImportImageContainer (0x74bf810) RTTI typeinfo: itk::ImportImageContainer<unsigned long, unsigned char> Reference Count: 1 Modified Time: 714 Debug: Off Object Name: Observers: none Pointer: 0x7a76180 Container manages memory: true Size: 6000 Capacity: 6000
GetImageFromArray
的 isVector
參數若設定為 True
,則會將 3D 的影像視為 2D 的向量影像。