使用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实现求最大公约数及判断素数的方法
May 26 Python
详解Python编程中包的概念与管理
Oct 16 Python
python中正则表达式的使用方法
Feb 25 Python
异步任务队列Celery在Django中的使用方法
Jun 07 Python
python实现批量修改图片格式和尺寸
Jun 07 Python
python连接mongodb密码认证实例
Oct 16 Python
从numpy数组中取出满足条件的元素示例
Nov 26 Python
关于Python3 lambda函数的深入浅出
Nov 27 Python
Python 中@property的用法详解
Jan 15 Python
python虚拟环境模块venv使用及示例
Mar 04 Python
解决pycharm中的run和debug失效无法点击运行
Jun 09 Python
pytest fixtures装饰器的使用和如何控制用例的执行顺序
Jan 28 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
GBK的页面输出JSON格式的php函数
2010/02/16 PHP
关于尾递归的使用详解
2013/05/02 PHP
Codeigniter整合Tank Auth权限类库详解
2014/06/12 PHP
windows的文件系统机制引发的PHP路径爆破问题分析
2014/07/28 PHP
php绘制一个扇形的方法
2015/01/24 PHP
Zend Framework分发器用法示例
2016/12/11 PHP
laravel 框架实现无限级分类的方法示例
2019/10/31 PHP
新手常遇到的一些jquery问题整理
2010/08/16 Javascript
精通Javascript系列之数据类型 字符串
2011/06/08 Javascript
浅谈Javascript鼠标和滚轮事件
2012/06/27 Javascript
JavaScript实现汉字转换为拼音的库文件示例
2016/12/22 Javascript
浅谈vue中数据双向绑定的实现原理
2017/09/14 Javascript
浅谈Webpack多页应用HMR卡住问题
2019/04/24 Javascript
在Vue mounted方法中使用data变量详解
2019/11/05 Javascript
vue操作dom元素的3种方法示例
2020/09/20 Javascript
[05:17]DOTA2睡衣妹卖萌求签名 CJ第二天全明星影像
2013/07/28 DOTA
pygame学习笔记(3):运动速率、时间、事件、文字
2015/04/15 Python
详解Python Socket网络编程
2016/01/05 Python
Python判断列表是否已排序的各种方法及其性能分析
2016/06/20 Python
python+pillow绘制矩阵盖尔圆简单实例
2018/01/16 Python
python实现画一颗树和一片森林
2018/06/25 Python
python实现字符串加密 生成唯一固定长度字符串
2019/03/22 Python
python命名空间(namespace)简单介绍
2019/08/10 Python
python opencv根据颜色进行目标检测的方法示例
2020/01/15 Python
Python json转字典字符方法实例解析
2020/04/13 Python
tensorflow/core/platform/cpu_feature_guard.cc:140] Your CPU supports instructions that this T
2020/06/22 Python
英国内衣连锁店:Boux Avenue
2018/01/24 全球购物
Zooplus罗马尼亚:宠物食品和配件
2019/11/02 全球购物
写出SQL四条最基本的数据操作语句(DML)
2012/12/12 面试题
人力资源经理的岗位职责范本
2014/02/28 职场文书
《沙漠中的绿洲》教学反思
2014/04/24 职场文书
教师求职信
2014/06/17 职场文书
公司总经理助理岗位职责
2014/07/09 职场文书
大学生心理健康教育心得体会
2016/01/12 职场文书
浅谈vue2的$refs在vue3组合式API中的替代方法
2021/04/18 Vue.js
python实战之用emoji表情生成文字
2021/05/08 Python