Categories: Python

Python 使用 ITK 讀取、寫入、建立影像教學與範例

介紹如何在 Python 中以 ITK 模組讀取、寫入影像檔案,以及自行建立 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])

ITK 讀取、寫入影像檔案

前述以 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 設定串流分割數,控制整個處理流程的串流方式。

讀取 2D 影像序列轉為 3D 影像

若要以 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])

3D 影像轉為 2D 影像序列

若要以 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.tif2d_image_02.tif2d_image_03.tif…等。

建立 ITK 空白影像

以下是透過 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

從 NumPy 建立 ITK 影像

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

GetImageFromArrayisVector 參數若設定為 True,則會將 3D 的影像視為 2D 的向量影像。

參考資料

Share
Published by
Office Guide
Tags: ITK

Recent Posts

Python 使用 PyAutoGUI 自動操作滑鼠與鍵盤

本篇介紹如何在 Python ...

1 年 ago

Ubuntu Linux 以 WireGuard 架設 VPN 伺服器教學與範例

本篇介紹如何在 Ubuntu ...

1 年 ago

Linux 網路設定 ip 指令用法教學與範例

本篇介紹如何在 Linux 系...

1 年 ago

Linux 以 Cryptsetup、LUKS 加密 USB 隨身碟教學與範例

介紹如何在 Linux 系統中...

1 年 ago