Python

ITK 使用 ScalarImageKmeansImageFilter 進行 K-Means 分群教學與範例

介紹如何使用 ITK 的 ScalarImageKmeansImageFilter 以 K-means 分群演算法,將影像像素值進行分群(clustering),產生標註(label)影像。

原始影像

載入原始影像。

import itk
import matplotlib.pyplot as plt

# 影像像素資料類型
PixelType = itk.SS

# 影像維度
Dimension = 2

# 影像類型
ImageType = itk.Image[PixelType, Dimension]

# 建立影像 Reader
ReaderType = itk.ImageFileReader[ImageType]
reader = ReaderType.New()
reader.SetFileName("fixed.mhd")
reader.Update()

# 讀取 Fixed 影像
image = reader.GetOutput()

# 顯示原始影像
plt.imshow(itk.GetArrayViewFromImage(image), cmap='gray')
plt.show()
原始影像

區分前景與背景

使用 ScalarImageKmeansImageFilter 以 K-means 分群演算法,將影像像素值進行分群,產生標註影像。最常見的應用就是將影像的前景與背景分開:

# 建立 ScalarImageKmeansImageFilter 
kmeansFilter = itk.ScalarImageKmeansImageFilter.New()
kmeansFilter.SetInput(image)

# 設定初始群心
kmeansFilter.AddClassWithInitialMean(itk.NumericTraits[PixelType].min())
kmeansFilter.AddClassWithInitialMean(itk.NumericTraits[PixelType].max())

# 執行 KMeans
kmeansFilter.Update()
label = kmeansFilter.GetOutput()

# 顯示結果
plt.imshow(itk.GetArrayViewFromImage(label), cmap='gray')
plt.show()
K-Means 結果

ScalarImageKmeansImageFilter 所產生的標註影像維度與大小都會跟輸入影像相同,而其中的值會以 012 等數字代表像素的分群,而若要將這些整數自動擴展至輸出值的整個範圍,可以將 SetUseNonContiguousLabels 設定為 True

細分區域

如果想要再將影像細分成更多區域,可以搭配繪製像素值分布圖來選擇更多的初始群心值。

# 繪製像素值分布圖
plt.hist(itk.GetArrayViewFromImage(image).flatten(), bins=40)
plt.show()
像素值分布圖

從像素值分布圖中可以看出像素值有三個主要的群聚區域,將這三個區域的像素值指定為初始群心,即可將影像細分為三個區域。

# 建立 ScalarImageKmeansImageFilter 
kmeansFilter = itk.ScalarImageKmeansImageFilter.New()
kmeansFilter.SetInput(image)

# 設定初始群心
kmeansFilter.AddClassWithInitialMean(0)
kmeansFilter.AddClassWithInitialMean(100)
kmeansFilter.AddClassWithInitialMean(150)

# 執行 KMeans
kmeansFilter.Update()
label = kmeansFilter.GetOutput()

# 顯示結果
plt.imshow(itk.GetArrayViewFromImage(label), cmap='gray')
plt.show()
K-Means 結果

如果希望影像標註的代碼可以依據影像區域的大小來重新排序,可以使用 RelabelImageFilter 來處理。

套疊原始與標註影像

若想要套疊原始影像與標註影像,可以使用

UCPixelType = itk.ctype('unsigned char')
UCImageType = itk.Image[UCPixelType, Dimension]

# 將標註影像轉為 unsigned char
castImageFilter = itk.CastImageFilter[ImageType, UCImageType].New()
castImageFilter.SetInput(label)

# 將原始影像轉為 unsigned char
intensityRescaler = itk.RescaleIntensityImageFilter[ImageType,
        UCImageType].New(
            Input=image,
            OutputMinimum=itk.NumericTraits[UCPixelType].min(),
            OutputMaximum=itk.NumericTraits[UCPixelType].max())

LabelType = itk.ctype('unsigned long')
LabelObjectType = itk.StatisticsLabelObject[LabelType, Dimension]
LabelMapType = itk.LabelMap[LabelObjectType]

# 將標註影像轉為 LabelMap
imageToLabelMapconverter = itk.LabelImageToLabelMapFilter[UCImageType, LabelMapType].New()
imageToLabelMapconverter.SetInput(castImageFilter.GetOutput())

# 套疊原始影像與 LabelMap
RGBImageType = itk.Image[itk.RGBPixel[itk.UC], Dimension]
overlayFilter = itk.LabelMapOverlayImageFilter[LabelMapType, UCImageType, RGBImageType].New()
overlayFilter.SetInput(imageToLabelMapconverter.GetOutput())
overlayFilter.SetFeatureImage(intensityRescaler.GetOutput())
overlayFilter.SetOpacity(0.5)

# 顯示結果
plt.imshow(itk.GetArrayViewFromImage(overlayFilter.GetOutput()))
plt.show()
套疊原始與標註影像

參考資料:ITKExamples

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