使用Python对Dicom文件进行读取与写入的实现
作者:Mitty_Meng 发布时间:2022-05-30 12:18:27
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()
效果如图所示:
但真实的图像是:
显然颜色是有区别的.导致这种差别的原因是pyplot函数使用的cm也就是"color map" 是简单的"bone" 并不能满足医学图像的要求.
借助Image模块
data_img.show()
一条指令即可,但是效果很差,如图所示:
综合来看,两种方法都不是很好.
单张影像的写入
经过上面对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()得到的效果如下图:
除此之外,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文件中本就没有这种信息,文件大小是保持相同的.
因为很多时候只是对图像进行处理,所以不再深究.
来源:https://blog.csdn.net/weixin_40451627/article/details/105574348
猜你喜欢
- 一、修改操作系统核心参数在Root用户下执行以下步骤:1)修改用户的SHELL的限制,修改/etc/security/limits.conf
- 一、简介开启慢查询日志,可以让MySQL记录下查询超过指定时间的语句,通过定位分析性能的瓶颈,才能更好的优化数据库系统的性能。二、参数说明s
- 准备工作去年的时候,青云发表过关于栅格系统的文章 . 我们知道网页的栅格系统是运用固定的格子设计版面布局,使其风格工整简洁. 视觉上来说,栅
- 问题在做web端ui层自动化的时候会碰到文件上传的操作,经常有朋友问到,这里总结一下解决方案第一种:type=file的上传文件,类似如下的
- 1、安装pecl及创建快捷键(若安装php时已带可忽略这步安装步骤)# cd /usr/local/php/bin/ //可查看
- 简介Wikipedia、Facebook 和 Yahoo! 等主要 web 属性使用 LAMP 架构来为每天数百万的请求提供服务,而 Wor
- 相信为数不少的系统管理员每天都在做着同一样的工作——对数据进行备份。一旦哪一天疏忽了,而这一天系统又恰恰发生了故障,需要进行数据恢复,那么此
- 目录前言二叉树节点定义递归构建二叉树前言本文的内容是数据结构中二叉树部分最基础的,之所以写一下主要是为了方便刷题的时候,能够在自己电脑上很快
- 在ASP中,如何创建DSN? 见下:<HTML><HEAD><META&n
- 在基于 Git 的开发过程中,我们很容易遇到合并代码的情况,例如我们从 master 分支拉取了一个 feature 分支,当我们开发到一段
- Python读取配置文件-ConfigParser二次封装直接上上代码test.conf[database]connect = mysqls
- 今天小编来和大家聊一下Python当中的altair可视化模块,并且通过调用该模块来绘制一些常见的图表,借助Altair,我们可以将更多的精
- 今天主要来实现一个权限管理系统,它主要是为了给不同的用户设定不同的权限,从而实现不同权限的用户登录之后使用的功能不一样,首先先看下数据库总共
- 如下所示:import time首先导入时间模块在程序开始执行的地方写入:start=time.clock()在程序末尾写入:end=tim
- 最近一直忙,我们的注册页面还是在持续优化。今天抽时间分析了下数据,依然以主注册表单为例,对表单里3个区块、9个字段做了个小小出错排行;看看哪
- 前言随着Python3的普及,Selenium3也跟上了行程。而Selenium3最大的变化是去掉了Selenium RC,另外就是Webd
- bootstrap里面有个激活按钮的时候,按钮变成不可用的;按照官网里面的方法介绍是在button按钮加个 data-loading-tex
- Vue中的插槽(slot)在项目中用的也是比较多的,今天就来介绍一下插槽的基本使用以及Vue版本更新之后的插槽用法变化。插槽是什么?插槽就是
- 单位收集了很多word格式的调查表,领导需要收集表单里的信息,我就把所有调查表放一个文件里,写了个python小程序把所需的信息打印出来#c
- super 的工作原理如下:def super(cls, inst): mro = inst.__class__.mro() &