使⽤Python对Dicom⽂件进⾏读取与写⼊的实现Pydicom
单张影像的读取
使⽤ pydicom.dcmread() 函数进⾏单张影像的读取,返回⼀个pydicom.dataset.FileDataset对象.
import os
import pydicom
# 调⽤本地的 dicom file
folder_path = r"D:\Files\Data\Materials"
file_name = "PA1_0001.dcm"
file_path = os.path.join(folder_path,file_name)
ds = pydicom.dcmread(file_path)
在⼀些特殊情况下,⽐如直接读取从医院拿到的数据(未经任何处理)时,可能会发⽣以下报错:
raise InvalidDicomError("File is missing DICOM File Meta Information "
missing from the header. Use force=True to force reading.
可以看到,由于缺失⽂件元信息头,⽆法直接读取,只能强⾏读取.这种情况可以直接根据提⽰,调整命令为:
ds = pydicom.dcmread(file_path,force=True)
但后续还会碰到:
AttributeError: 'Dataset' object has no attribute 'TransferSyntaxUID'
在⽹上检索后发现,可以通过设置TransferSyntaxUID来解决问题:
ds.file_meta.TransferSyntaxUID = pydicom.uid.ImplicitVRLittleEndian
这样就⼤功告成了(这⾥实际上就提前接触到了下⾯读取Dicom Tags的内容了)
⼀些简单处理
读取成功后,我们可以对 Dicom⽂件进⾏⼀些简单的处理
读取并编辑Dicom Tags
可以通过两种⽅法来读取Tag的值
使⽤的Tag的Description
print(ds.PatientID,ds.StudyDate,ds.Modality)
使⽤ ds.get() 函数. 函数内参数采⽤的是Tag ID.⼏种简单的打开Dicom⽂件的软件(如RadiAnt DICOM Viewer)都可以直接看到.这⾥不再赘述.
<(0x00100020) # 这⾥得到的是PatientID
读取到相应的Tag值后, 也可以将其他的值写⼊这些Tag.只要最后保存⼀下就可以了.
借助Numpy与PIL.Image
读取Dicom⽂件后,可以借助Numpy以及图像处理库(如PIL.Image)来进⾏简单的处理.
借助Numpy
import numpy as np
data = np.array(ds.pixel_array)
注意这⾥使⽤的是 np.array() ⽽不是 np.asarray(). 因为前者的更改并不会带来原pixel_array的改变.
在转化为ndarray后可以直接进⾏简单的切割和连接,⽐如截取某⼀部分和将两张图像拼在⼀起等,之后再写⼊并保存下来即可.
借助PIL.Image
from PIL import Image
data_img = Image.fromarray(ds.pixel_array)
data_img_rotated = ate(angle=45,resample=Image.BICUBIC,fillcolor=pixel((0,0)))
这⾥展⽰的是旋转, 还有其他功能如resize等.
需要注意的是,从Numpy的ndarray转化为Image时,⼀般会发⽣变化:
print(data.dtype) # int16
data_rotated = np.array(data_img_rotated)
print(data_img) # int32
只需要指定参数就可以解决了
data_rotated = np.array(data_img_rotated,dtype = np.int16)
可视化
简单的可视化Pydicom没有直接的实现⽅法,我们可以通过上⾯借助Matplotlib以及Image模块来实现.但效果有限.借助 Matplotlib (Pydicom官⽅⽂档中使⽤的⽅法)
from matplotlib import pyplot
pyplot.imshow(ds.pixel_array,bone)
pyplot.show()
效果如图所⽰:
但真实的图像是:
显然颜⾊是有区别的.导致这种差别的原因是pyplot函数使⽤的cm也就是"color map" 是简单的"bone" 并不能满⾜医学图像的要求.
借助Image模块
data_img.show()
⼀条指令即可,但是效果很差,如图所⽰:
综合来看,两种⽅法都不是很好.
单张影像的写⼊
经过上⾯对Tag值的修改, 对图像的切割, 旋转等操作.最后需要重新写⼊该Dicom⽂件.
ds.PixelData = bytes()
ds.Rows,ds.Columns = data_rotated.shape
new_name = "dicom_rotated.dcm"
ds.save_as(os.path.join(folder_path,new_name))python安装教程非常详细
SimpleITK
SimpleITK 是从基于C++的ITK迁移到Python的,所以很多⽅法的使⽤都跟C++很相似.
import SimpleITK as sitk
单张影像的读取
有两种⽅法:
sitk.ReadImage()
这种⽅法直接返回image对象,简单易懂.但是⽆法读取Tag的值.
img = sitk.ReadImage(file_path)
print(type(img)) # <class 'SimpleITK.SimpleITK.Image'>
sitk.ImageFileReader()
这种⽅法⽐较像C++的操作风格,需要先初始化⼀个对象,然后设置⼀些参数,最后返回image.相对更复杂,但可以操作的点⽐较多file_reader = sitk.ImageFileReader()
file_reader.SetFileName(file_path) #这⾥只显⽰了必需的,还有很多可以设置的参数
data = file_reader.Execute()
# 使⽤这种⽅法读取Dicom的Tag Value
for key in file_reader.GetMetaDataKeys():
print(key,file_reader.GetMetaData(key))
以上两种⽅法返回的都是三维的对象,这与Pydicom有很⼤的不同.
data_np = sitk.GetArrayFromImage(data)
print(data_np.shape) # (1, 512, 512) = (Slice index, Rows, Columns)
序列读取
序列读取的⽅法与单张图像读取的第⼆种⽅法很相似.
(暂且只发现了⼀种⽅法读取序列,如果还有其他⽅法,请在评论区予以补充,感谢!)
series_reader = sitk.ImageSeriesReader()
fileNames = series_reader.GetGDCMSeriesFileNames(folder_name)
series_reader.SetFileNames(fileNames)
images = series_reader.Execute()
同样,返回的也是三维的对象.
⼀些简单操作
SimpleITK 包含很多图像处理如滤波的⼯具,这⾥简单介绍⼀个边缘检测⼯具和可视化⼯具
边缘检测
以Canny边缘检测算⼦为例,与读取单张图像类似,同样有两种⽅式:
sitk.CannyEdgeDetection()
由于滤波的对象必须是32位图像或者其他格式, 需要通过 sitk.Cast() 转换. 之后可以再转换回原格式.
data_32 = sitk.Cast(data,sitk.sitkFloat32)
data_edge_1 = sitk.CannyEdgeDetection(data_32,5,30,[5]*3,[0.8]*3)
sitk.CannyEdgeDetectionImageFilter()
这个操作相对⿇烦⼀些
Canny = sitk.CannyEdgeDetectionImageFilter()
Canny.SetLowerThreshold(5)
Canny.SetUpperThreshold(30)
Canny.SetVariance([5]*3)
Canny.SetMaximumError([0.5]*3)
data_edge_2 = Canny.Execute(data_32)
可视化
可视化的⽅法⾮常简单只需要⼀条指令:
sitk.Show()
但需要先安装⼯具ImageJ,否则⽆法使⽤.具体的安装链接,可以参考这篇博⽂:
同⼀张Dicom⽂件使⽤sitk.Show()得到的效果如下图:
除此之外,ImageJ还有⼀个Tool Bar ⽀持对图像的进⼀步处理:
可见,SimpleITK的可视化要⽐上⾯介绍的强⼤很多,不仅可以实现单张图像的可视化以及图像处理,还可以同时对整个序列的图像进⾏统⼀处理.
单张影像的写⼊
同样有两种⽅法
sitk.WriteImage()
new_name = "new_MR_2.dcm"
sitk.WriteImage(img,os.path.join(folder_name,new_name))
sitk.ImageFileWriter()
file_writer = sitk.ImageFileWriter()
file_writer.SetFileName(os.path.join(folder_name,new_name))
file_writer.SetImageIO(imageio="GDCMImageIO")
file_writer.Execute(img)
使⽤这两种⽅法进⾏写⼊的时候,会发现,即便什么也没有做,但得到的新Dicom⽂件要⼩于原始的Dicom⽂件.这是因为新的Dicom⽂件中没有Private Creator信息(属于Dicom Tag的内容).当然如果原始Dicom⽂件中本就没有这种信息,⽂件⼤⼩是保持相同的.

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。