醫學圖像處理中的數據讀寫

醫學圖像處理中的數據讀寫

常見的醫學圖像的格式

不管格式如何變化,對於醫學圖像而言,最終讀取到內容中的數據就是圖像的強度值信息,就類似自然圖像的RGB表示法一樣。這裏叫做強度值,因爲不同的醫學圖像例如CT、MRI他們可區分的信號的範圍和一般自然圖像不一樣。一般自然圖像256個級別就夠用了,而醫學圖像可能會有2000個級別。對於我而言,比較熟悉的有幾種常見的數據後綴。

  • dicom:常見於臨牀上醫學設備採集的信息,裏面有許多字段,我們只關心圖像相關的信息即可。
  • mha:是一種體數據的存儲格式,由一個描述數據的頭和數據組成,一般就針對醫學圖像使用。
  • mhd:和mha類似,只不過是把頭和圖像數據分開存儲了。
  • nii:NIFTI格式,常用於神經圖像,有時候也會見到hdr img這樣的文件,本質上也是這種格式,只不過是把頭和數據分開了。
  • nrrd:也是一樣包含了頭和圖像數據。
  • raw:一般只有圖像信息,其他信息需要去找對應的解析或者頭。
  • img:一般只有圖像信息,其他信息需要去找對應的解析或者頭。
    本文沒有過多的介紹這些格式,因爲大家主要的目的是做醫學圖像處理,其實也不關心他們的具體實現,可以認爲他們都是一樣的,都存儲了醫學圖像(也可能是三維圖像)信息,我們只需要調用現成的庫,將他們解析成類似三維數組的格式,就可以供我們之後的使用了。

在Python中進行讀寫

Python提供了豐富的庫來輔助我們工作,大多數情況下只需要幾行代碼就可以搞定讀寫。千萬別想不去去用C++之類的語言,只有在需要工程化的時候再考慮去用C++。

DICOM的讀寫

我從這個網站 https://www.dicomlibrary.com/ 下載了DICOM Samples DICOM的數據作爲Example。

import SimpleITK as sitk
import sys
import os
import numpy as np
import matplotlib.pyplot as plt

#本文從上述網站下載的dicom是一個序列有300+切片數據
dicomdir = "Dataset/dicom/data1/series-00000"
reader = sitk.ImageSeriesReader()

dicom_names = reader.GetGDCMSeriesFileNames(dicomdir)
reader.SetFileNames(dicom_names)

image = reader.Execute()

size = image.GetSize()
print("Image size:", size[0], size[1], size[2])

rawImg = sitk.GetArrayFromImage(image)
print("Max value:",np.max(rawImg),"Min value:",np.min(rawImg),rawImg.shape)
Image size: 512 512 361
Max value: 2948 Min value: -1000 (361, 512, 512)
#以上代碼成功讀取了dicom序列文件,下面可視化幾個切片看看
imgslicer = []
for i in range(4):
    #拓展維度,爲了可視化
    s = np.expand_dims(rawImg[i*70,:,:],2)
    #增加通道
    s = np.repeat(s,3,2)
    #這裏進行歸一化,因爲dicom這裏有3000個level所以歸一化肯定會丟失精度
    #一般的做法是給定一個區間,再歸一化,其他區域就不管了,這個叫開窗
    print(s.shape)
    s = s.astype(np.float32)
    s = (s - np.min(s))/(np.max(s) - np.min(s))
    imgslicer.append(s)

# rawImg = rawImg.transpose((1,2,0))
# rawImg = np.repeat(rawImg,3,2)
# print("Max value:",np.max(rawImg),"Min value:",np.min(rawImg),rawImg.shape)
for i in range(4):
    plt.subplot(1,4,i+1)
    plt.imshow(imgslicer[i])
(512, 512, 3)
(512, 512, 3)
(512, 512, 3)
(512, 512, 3)

img

如果你需要其他的信息,可以查閱SimpleITK的文檔,另外寫入dicom不在這裏說明了。

mha文件的讀寫

mha文件格式更加簡單和緊湊,也是個人用的比較多的一種格式,現在我們將上面使用的dicom文件轉成mha文件,來作爲我們的Example。注意,有些重複的代碼這裏就不寫了,包括import庫等,如果後面需要新的庫,會重新import。

mhadir = os.path.join("Dataset","mha")
if not os.path.exists(mhadir):
    os.mkdir(mhadir)
sitk.WriteImage(image,os.path.join(mhadir,"data1.mha"))

在得到了mha文件以後我們就可以讀取mha文件了,這裏就不進行可視化,因爲轉成numpy以後,後面的操作都是一樣的。

#讀取mha
mhaimg = sitk.ReadImage(os.path.join(mhadir,"data1.mha"))
print(mhaimg.GetSize())
print(mhaimg.GetSpacing())

#轉爲numpy
rawmhaimg = sitk.GetArrayFromImage(mhaimg)
print(rawmhaimg.shape)

#保存mha
sitk.WriteImage(mhaimg,os.path.join(mhadir,"data1_cpy.mha"))
(512, 512, 361)
(0.58984375, 0.58984375, 0.5)
(361, 512, 512)

mhd文件的讀寫

mhd文件類似於mha文件,只不過是將文件分開進行了存儲,我們繼續將之前的文件轉爲mhd作爲Example

mhddir = os.path.join("Dataset","mhd")
if not os.path.exists(mhddir):
    os.mkdir(mhddir)
sitk.WriteImage(image,os.path.join(mhddir,"data1.mhd"))
print(os.listdir(mhddir))
['data1.mhd', 'data1.raw']
#讀取mhd
mhdimg = sitk.ReadImage(os.path.join(mhddir,"data1.mhd"))
print(mhdimg.GetSize())
print(mhdimg.GetSpacing())

#轉爲numpy
rawmhdimg = sitk.GetArrayFromImage(mhdimg)
print(rawmhdimg.shape)

#保存mhd
sitk.WriteImage(mhaimg,os.path.join(mhddir,"data1_cpy.mhd"))
(512, 512, 361)
(0.58984375, 0.58984375, 0.5)
(361, 512, 512)

nii文件的讀寫

繼續使用dicom讀取的數據進行讀寫說明

niidir = os.path.join("Dataset","nii")
if not os.path.exists(niidir):
    os.mkdir(niidir)
sitk.WriteImage(image,os.path.join(niidir,"data1.nii.gz"))
print(os.listdir(niidir))
['data1.nii.gz']
#讀取nii
niiimg = sitk.ReadImage(os.path.join(niidir,"data1.nii.gz"))
print(niiimg.GetSize())
print(niiimg.GetSpacing())

#轉爲numpy
rawniiimg = sitk.GetArrayFromImage(niiimg)
print(rawniiimg.shape)

#保存nii
sitk.WriteImage(niiimg,os.path.join(niidir,"data1_cpy.nii.gz"))
(512, 512, 361)
(0.58984375, 0.58984375, 0.5)
(361, 512, 512)

nrrd文件讀寫

nrrddir = os.path.join("Dataset","nrrd")
if not os.path.exists(nrrddir):
    os.mkdir(nrrddir)
sitk.WriteImage(image,os.path.join(nrrddir,"data1.nrrd"))
print(os.listdir(nrrddir))
['data1.nrrd']
#讀取nrrd
nrrdimg = sitk.ReadImage(os.path.join(nrrddir,"data1.nrrd"))
print(nrrdimg.GetSize())
print(nrrdimg.GetSpacing())

#轉爲numpy
rawnrrdimg = sitk.GetArrayFromImage(nrrdimg)
print(rawnrrdimg.shape)

#保存nii
sitk.WriteImage(nrrdimg,os.path.join(nrrddir,"data1_cpy.nrrd"))
(512, 512, 361)
(0.58984375, 0.58984375, 0.5)
(361, 512, 512)

raw/img文件的讀取

關於raw文件的讀取可能會相對來說要麻煩一些,因爲raw格式只存數據,至於如何解析,需要額外的說明,這個說明可能就是像mhd那樣有一個文件來說明,或者有些是在網站或者文檔裏直接給出的,需要自己簡單處理一下,之間就碰到過,拿到的數據是raw/img的後綴,只有純數據信息,而關於尺寸、數據類型等信息都是這個數據的網站上給出的,因此需要自己簡單處理一下。本文可以利用mhd格式中那個raw文件來說明一下。

#我們來看一下之前那個數據的類型是什麼,
#隨便找一個numpy數組看一下
print(rawmhaimg.dtype)
int16
#利用np從文件讀取文件
rawpath = os.path.join(mhddir,"data1.raw")
mdata = np.fromfile(rawpath,dtype=np.int16)
#reshape數據,這裏要注意讀入numpy的時候,對應是(z,y,x)
mdata = mdata.reshape((361, 512, 512))
mrawimg = sitk.GetImageFromArray(mdata)
#設置pixel spacing
mrawimg.SetSpacing((0.58984375, 0.58984375, 0.5))
#輸出文件,我們將其保存爲mha
sitk.WriteImage(mrawimg,os.path.join(mhadir,"data1_raw2mha.mha"))
#讀取一下看看
rawtest = sitk.ReadImage(os.path.join(mhadir,"data1_raw2mha.mha"))
print(rawtest.GetSize())
print(rawtest.GetSpacing())
(512, 512, 361)
(0.58984375, 0.58984375, 0.5)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章