使用Python对Dicom文件进行读取与写入

Pydicom

单张影像的读取

使用 pydicom.dcmread() 函数进行单张影像的读取,返回一个pydicom.dataset.FileDataset对象.html

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)

在一些特殊状况下,好比直接读取从医院拿到的数据(未经任何处理)时,可能会发生如下报错:python

raise InvalidDicomError("File is missing DICOM File Meta Information "
pydicom.errors.InvalidDicomError: File is missing DICOM File Meta Information header or the 'DICM' prefix is missing from the header. Use force=True to force reading.

能够看到,因为缺失文件元信息头,没法直接读取,只能强行读取.这种状况能够直接根据提示,调整命令为:git

ds = pydicom.dcmread(file_path,force=True)

但后续还会碰到:github

AttributeError: 'Dataset' object has no attribute 'TransferSyntaxUID'

在网上检索后发现,能够经过设置TransferSyntaxUID来解决问题:markdown

ds.file_meta.TransferSyntaxUID = pydicom.uid.ImplicitVRLittleEndian

这样就大功告成了(这里实际上就提早接触到了下面读取Dicom Tags的内容了)函数

一些简单处理

读取成功后,咱们能够对 Dicom文件 进行一些简单的处理工具

读取并编辑Dicom Tags

能够经过两种方法来读取Tag的值ui

  1. 使用的Tag的Description
print(ds.PatientID,ds.StudyDate,ds.Modality)
  1. 使用 ds.get() 函数. 函数内参数采用的是Tag ID.几种简单的打开Dicom文件的软件(如RadiAnt DICOM Viewer)均可以直接看到.这里再也不赘述.
ds.get(0x00100020) # 这里获得的是PatientID 

读取到相应的Tag值后, 也能够将其余的值写入这些Tag.只要最后保存一下就能够了.spa

借助Numpy与PIL.Image

读取Dicom文件后,能够借助Numpy以及图像处理库(如PIL.Image)来进行简单的处理..net

  1. 借助Numpy
import numpy as np
data = np.array(ds.pixel_array)

注意这里使用的是 np.array() 而不是 np.asarray(). 由于前者的更改并不会带来原pixel_array的改变.
在转化为ndarray后 能够直接进行简单的切割和链接,好比截取某一部分和将两张图像拼在一块儿等,以后再写入并保存下来便可.

  1. 借助PIL.Image
from PIL import Image
data_img = Image.fromarray(ds.pixel_array)
data_img_rotated = data_img.rotate(angle=45,resample=Image.BICUBIC,fillcolor=data_img.getpixel((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模块来实现.但效果有限.

  1. 借助 Matplotlib (Pydicom官方文档中使用的方法)
from matplotlib import pyplot
pyplot.imshow(ds.pixel_array,cmap=pyplot.cm.bone)
pyplot.show()

效果如图所示:
使用Matplotlib进行Dicom文件的可视化
但真实的图像是:

真实MR图像
显然颜色是有区别的.致使这种差异的缘由是pyplot函数使用的cm也就是"color map" 是简单的"bone" 并不能知足医学图像的要求.

  1. 借助Image模块
data_img.show()

一条指令便可,可是效果不好,如图所示:

使用Image模块进行可视化
综合来看,两种方法都不是很好.

单张影像的写入

通过上面对Tag值的修改, 对图像的切割, 旋转等操做.最后须要从新写入该Dicom文件.

ds.PixelData = data_rotated.tobytes()
ds.Rows,ds.Columns = data_rotated.shape
new_name = "dicom_rotated.dcm"
ds.save_as(os.path.join(folder_path,new_name))

SimpleITK

SimpleITK 是从基于C++的ITK迁移到Python的,因此不少方法的使用都跟C++很类似.

import SimpleITK as sitk

单张影像的读取

有两种方法:

  1. sitk.ReadImage()
    这种方法直接返回image对象,简单易懂.可是没法读取Tag的值.
img = sitk.ReadImage(file_path)
print(type(img)) # <class 'SimpleITK.SimpleITK.Image'>
  1. 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边缘检测算子为例,与读取单张图像相似,一样有两种方式:

  1. 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)
  1. 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,不然没法使用.具体的安装连接,能够参考这篇博文:sitk.show()与imageJ结合使用常见的问题

同一张Dicom文件使用sitk.Show()获得的效果以下图:
使用ImageJ进行可视化的结果
除此以外,ImageJ还有一个Tool Bar 支持对图像的进一步处理:

ImageJ Tool Bar
可见,SimpleITK的可视化要比上面介绍的强大不少,不只能够实现单张图像的可视化以及图像处理,还能够同时对整个序列的图像进行统一处理.

单张影像的写入

一样有两种方法

  1. sitk.WriteImage()
new_name = "new_MR_2.dcm"
sitk.WriteImage(img,os.path.join(folder_name,new_name))
  1. 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文件中本就没有这种信息,文件大小是保持相同的.
由于不少时候只是对图像进行处理,因此再也不深究.


如有错误,还请指出,谢谢!

发布了1 篇原创文章 · 获赞 2 · 访问量 98
相关文章
相关标签/搜索