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


Posted in Python onApril 20, 2020

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 "
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.

可以看到,由于缺失文件元信息头,无法直接读取,只能强行读取.这种情况可以直接根据提示,调整命令为:

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)都可以直接看到.这里不再赘述.

ds.get(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 = 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模块来实现.但效果有限.

借助 Matplotlib (Pydicom官方文档中使用的方法)

from matplotlib import pyplot
pyplot.imshow(ds.pixel_array,cmap=pyplot.cm.bone)
pyplot.show()

效果如图所示:

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

但真实的图像是:

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

显然颜色是有区别的.导致这种差别的原因是pyplot函数使用的cm也就是"color map" 是简单的"bone" 并不能满足医学图像的要求.

借助Image模块

data_img.show()

一条指令即可,但是效果很差,如图所示:

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

综合来看,两种方法都不是很好.

单张影像的写入

经过上面对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

单张影像的读取

有两种方法:

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,否则无法使用.具体的安装链接,可以参考这篇博文:sitk.show()与imageJ结合使用常见的问题

同一张Dicom文件使用sitk.Show()得到的效果如下图:

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

除此之外,ImageJ还有一个Tool Bar 支持对图像的进一步处理:

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

可见,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文件中本就没有这种信息,文件大小是保持相同的.
因为很多时候只是对图像进行处理,所以不再深究.

到此这篇关于使用Python对Dicom文件进行读取与写入的实现的文章就介绍到这了,更多相关Python Dicom文件进行读取与写入内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python ljust rjust center输出
Sep 06 Python
Python ORM框架SQLAlchemy学习笔记之关系映射实例
Jun 10 Python
理解Python中的With语句
Feb 02 Python
python装饰器与递归算法详解
Feb 18 Python
PyTorch上搭建简单神经网络实现回归和分类的示例
Apr 28 Python
详解python中Numpy的属性与创建矩阵
Sep 10 Python
Python 限制线程的最大数量的方法(Semaphore)
Feb 22 Python
python利用多种方式来统计词频(单词个数)
May 27 Python
使用python获取邮箱邮件的设置方法
Sep 20 Python
From CSV to SQLite3 by python 导入csv到sqlite实例
Feb 14 Python
Python Websocket服务端通信的使用示例
Feb 25 Python
python学习笔记之多进程
Aug 06 Python
python 错误处理 assert详解
Apr 20 #Python
解决Jupyter Notebook使用parser.parse_args出现错误问题
Apr 20 #Python
在ipython notebook中使用argparse方式
Apr 20 #Python
Python绘制全球疫情变化地图的实例代码
Apr 20 #Python
spyder 在控制台(console)执行python文件,debug python程序方式
Apr 20 #Python
python实现小程序推送页面收录脚本
Apr 20 #Python
在spyder IPython console中,运行代码加入参数的实例
Apr 20 #Python
You might like
《PHP边学边教》(02.Apache+PHP环境配置――上篇)
2006/12/13 PHP
php日历[测试通过]
2008/03/27 PHP
php中的观察者模式
2010/03/24 PHP
php设计模式 Delegation(委托模式)
2011/06/26 PHP
php文件上传类的分享
2017/07/06 PHP
PHP call_user_func和call_user_func_array函数的简单理解与应用分析
2019/11/25 PHP
疯掉了,尽然有js写的操作系统
2007/04/23 Javascript
JQuery+JS实现仿百度搜索结果中关键字变色效果
2011/08/02 Javascript
jQuery Jcrop插件实现图片选取功能
2011/11/23 Javascript
细说javascript函数从函数的构成开始
2013/08/29 Javascript
jquery小火箭返回顶部代码分享
2015/08/19 Javascript
实例讲解JavaScript中instanceof运算符的用法
2016/06/08 Javascript
AngularJS 执行流程详细介绍
2016/08/18 Javascript
js实现拖拽功能
2017/03/01 Javascript
jQuery插件zTree实现获取一级节点数据的方法
2017/03/08 Javascript
vue debug 二种方法
2018/09/16 Javascript
一次Webpack配置文件的分离实战记录
2018/11/30 Javascript
用Cordova打包Vue项目的方法步骤
2019/02/02 Javascript
LayUi使用switch开关,动态的去控制它是否被启用的方法
2019/09/21 Javascript
vue.js 子组件无法获取父组件store值的解决方式
2019/11/08 Javascript
如何将python中的List转化成dictionary
2016/08/15 Python
Python 多线程Threading初学教程
2017/08/22 Python
python获取本机所有IP地址的方法
2018/12/26 Python
将matplotlib绘图嵌入pyqt的方法示例
2020/01/08 Python
利用CSS3的transition属性实现滑动效果
2015/08/05 HTML / CSS
纯HTML5+CSS3制作生日蛋糕(代码易懂)
2016/11/16 HTML / CSS
Speedo速比涛中国官方网站:全球领先泳装运动品牌
2018/04/24 全球购物
小学英语教学反思案例
2014/02/04 职场文书
继承权公证书
2014/04/09 职场文书
五一活动标语
2014/06/30 职场文书
2014年财务部工作总结
2014/11/11 职场文书
会议通知格式范文
2015/04/15 职场文书
2015小学教师年度工作总结
2015/05/12 职场文书
普通员工辞职信范文
2015/05/12 职场文书
Django + Taro 前后端分离项目实现企业微信登录功能
2022/04/07 Python
Python中生成随机数据安全性、多功能性、用途和速度方面进行比较
2022/04/14 Python