使用pydicom实现Dicom文件读取与CT图像窗宽窗位调整

使用pydicom实现Dicom文件读取与CT图像窗宽窗位调整1.前言为了能够在Labelme上对Dicom图像进行编辑,这里对python环境下Dicom文件的读取进行了研究。在Dicom图像中CT的窗宽窗位是一个很重要的概念,但是找了半天在pydicom中没有相关设置函数,这里跟DCMTK还不一样。但是可以根据两个tag得到CT图像的CT值,那就是(0028|1052):rescaleintercept和(0028|1053):rescales…

大家好,又见面了,我是你们的朋友全栈君。

1. 前言

为了能够在Labelme上对Dicom图像进行编辑,这里对python环境下Dicom文件的读取进行了研究。在Dicom图像中CT的窗宽窗位是一个很重要的概念,但是找了半天在pydicom中没有相关设置函数,这里跟DCMTK还不一样。但是可以根据两个tag得到CT图像的CT值,那就是(0028|1052):rescale intercept和(0028|1053):rescale slope。则按照下面的算子得到CT图像,进而就可以调整窗宽窗位了

Hu = pixel * slope + intercept

至于那个部位的窗宽窗位是多少各位看官就可以自行百度了。

2. 代码实现

# -*- coding=utf-8 -*-
import matplotlib.pyplot as plt
import pydicom
import pydicom.uid
import sys
import PIL.Image as Image
from PyQt5 import QtGui
import os

step1:读取Dicom图像数据与得到CT值图像(CT图)

have_numpy = True
try:
import numpy
except ImportError:
have_numpy = False
raise
sys_is_little_endian = (sys.byteorder == 'little')
NumpySupportedTransferSyntaxes = [
pydicom.uid.ExplicitVRLittleEndian,
pydicom.uid.ImplicitVRLittleEndian,
pydicom.uid.DeflatedExplicitVRLittleEndian,
pydicom.uid.ExplicitVRBigEndian,
]
# 支持的传输语法
def supports_transfer_syntax(dicom_dataset):
""" Returns ------- bool True if this pixel data handler might support this transfer syntax. False to prevent any attempt to try to use this handler to decode the given transfer syntax """
return (dicom_dataset.file_meta.TransferSyntaxUID in
NumpySupportedTransferSyntaxes)
def needs_to_convert_to_RGB(dicom_dataset):
return False
def should_change_PhotometricInterpretation_to_RGB(dicom_dataset):
return False
# 加载Dicom图像数据
def get_pixeldata(dicom_dataset):
"""If NumPy is available, return an ndarray of the Pixel Data. Raises ------ TypeError If there is no Pixel Data or not a supported data type. ImportError If NumPy isn't found NotImplementedError if the transfer syntax is not supported AttributeError if the decoded amount of data does not match the expected amount Returns ------- numpy.ndarray The contents of the Pixel Data element (7FE0,0010) as an ndarray. """
if (dicom_dataset.file_meta.TransferSyntaxUID not in
NumpySupportedTransferSyntaxes):
raise NotImplementedError("Pixel Data is compressed in a "
"format pydicom does not yet handle. "
"Cannot return array. Pydicom might "
"be able to convert the pixel data "
"using GDCM if it is installed.")
# 设置窗宽窗位
#dicom_dataset.
if not have_numpy:
msg = ("The Numpy package is required to use pixel_array, and "
"numpy could not be imported.")
raise ImportError(msg)
if 'PixelData' not in dicom_dataset:
raise TypeError("No pixel data found in this dataset.")
# Make NumPy format code, e.g. "uint16", "int32" etc
# from two pieces of info:
# dicom_dataset.PixelRepresentation -- 0 for unsigned, 1 for signed;
# dicom_dataset.BitsAllocated -- 8, 16, or 32
if dicom_dataset.BitsAllocated == 1:
# single bits are used for representation of binary data
format_str = 'uint8'
elif dicom_dataset.PixelRepresentation == 0:
format_str = 'uint{}'.format(dicom_dataset.BitsAllocated)
elif dicom_dataset.PixelRepresentation == 1:
format_str = 'int{}'.format(dicom_dataset.BitsAllocated)
else:
format_str = 'bad_pixel_representation'
try:
numpy_dtype = numpy.dtype(format_str)
except TypeError:
msg = ("Data type not understood by NumPy: "
"format='{}', PixelRepresentation={}, "
"BitsAllocated={}".format(
format_str,
dicom_dataset.PixelRepresentation,
dicom_dataset.BitsAllocated))
raise TypeError(msg)
if dicom_dataset.is_little_endian != sys_is_little_endian:
numpy_dtype = numpy_dtype.newbyteorder('S')
pixel_bytearray = dicom_dataset.PixelData
if dicom_dataset.BitsAllocated == 1:
# if single bits are used for binary representation, a uint8 array
# has to be converted to a binary-valued array (that is 8 times bigger)
try:
pixel_array = numpy.unpackbits(
numpy.frombuffer(pixel_bytearray, dtype='uint8'))
except NotImplementedError:
# PyPy2 does not implement numpy.unpackbits
raise NotImplementedError(
'Cannot handle BitsAllocated == 1 on this platform')
else:
pixel_array = numpy.frombuffer(pixel_bytearray, dtype=numpy_dtype)
length_of_pixel_array = pixel_array.nbytes
expected_length = dicom_dataset.Rows * dicom_dataset.Columns
if ('NumberOfFrames' in dicom_dataset and
dicom_dataset.NumberOfFrames > 1):
expected_length *= dicom_dataset.NumberOfFrames
if ('SamplesPerPixel' in dicom_dataset and
dicom_dataset.SamplesPerPixel > 1):
expected_length *= dicom_dataset.SamplesPerPixel
if dicom_dataset.BitsAllocated > 8:
expected_length *= (dicom_dataset.BitsAllocated // 8)
padded_length = expected_length
if expected_length & 1:
padded_length += 1
if length_of_pixel_array != padded_length:
raise AttributeError(
"Amount of pixel data %d does not "
"match the expected data %d" %
(length_of_pixel_array, padded_length))
if expected_length != padded_length:
pixel_array = pixel_array[:expected_length]
if should_change_PhotometricInterpretation_to_RGB(dicom_dataset):
dicom_dataset.PhotometricInterpretation = "RGB"
if dicom_dataset.Modality.lower().find('ct') >= 0:  # CT图像需要得到其CT值图像
pixel_array = pixel_array * dicom_dataset.RescaleSlope + dicom_dataset.RescaleIntercept  # 获得图像的CT值
pixel_array = pixel_array.reshape(dicom_dataset.Rows, dicom_dataset.Columns*dicom_dataset.SamplesPerPixel)
return pixel_array, dicom_dataset.Rows, dicom_dataset.Columns

step2:对于CT图像设置窗宽窗位

# 调整CT图像的窗宽窗位
def setDicomWinWidthWinCenter(img_data, winwidth, wincenter, rows, cols):
img_temp = img_data
img_temp.flags.writeable = True
min = (2 * wincenter - winwidth) / 2.0 + 0.5
max = (2 * wincenter + winwidth) / 2.0 + 0.5
dFactor = 255.0 / (max - min)
for i in numpy.arange(rows):
for j in numpy.arange(cols):
img_temp[i, j] = int((img_temp[i, j]-min)*dFactor)
min_index = img_temp < 0
img_temp[min_index] = 0
max_index = img_temp > 255
img_temp[max_index] = 255
return img_temp

step3:获取Dicom中的tag信息

第一种方式:

# 加载Dicom图片中的Tag信息
def loadFileInformation(filename):
information = {}
ds = pydicom.read_file(filename)
information['PatientID'] = ds.PatientID
information['PatientName'] = ds.PatientName
information['PatientBirthDate'] = ds.PatientBirthDate
information['PatientSex'] = ds.PatientSex
information['StudyID'] = ds.StudyID
information['StudyDate'] = ds.StudyDate
information['StudyTime'] = ds.StudyTime
information['InstitutionName'] = ds.InstitutionName
information['Manufacturer'] = ds.Manufacturer
print(dir(ds))
print(type(information))
return information

第二种方式

dcm = pydicom.dcmread(fileanme)  # 加载Dicom数据
print(dcm[0x0008, 0x0060])
>>(0008, 0060) Modality                            CS: 'MR'
print(dcm[0x0008, 0x0060].VR)
>>CS
print(dcm[0x0008, 0x0060].value)
>>MR

step4:Dicom图像数据转换为PIL.Image

dcm = pydicom.dcmread(fileanme)  # 加载Dicom数据
dcm_img = Image.fromarray(img_data)  # 将Numpy转换为PIL.Image
dcm_img = dcm_img.convert('L')
# 保存为jpg文件,用作后面的生成label用
dcm_img.save('temp.jpg')
# 显示图像
dcm_img.show()

3. 结果展示

调整了窗宽窗位的脑部CT图像:
这里写图片描述

4. 参考资料

  1. Pydicom User Guide
  2. 【医学影像】窗宽窗位与其处理方法
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/148747.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(1)
blank

相关推荐

  • 树莓派视觉小车 — 人脸追踪(人脸识别、PID控制舵机运动)[通俗易懂]

    树莓派视觉小车 — 人脸追踪(人脸识别、PID控制舵机运动)[通俗易懂]效果展示基础理论(人脸识别)人脸检测算法按照方法可以被分为两大类,基于特征的算法、基于图像的算法。1、基于特征的算法基于特征的算法就是通过提取图像中的特征和人脸特征进行匹配,如果匹配上了就说明是人脸,反之则不是。提取的特征是人为设计的特征,例如Haar,FHOG,特征提取完之后,再利用分类器去进行判断。通俗的说就是采用模板匹配,就是用人脸的模板图像与待检测的图像中的各个位置进行匹配,匹配的内容就是提取的特征,然后再利用分类器进行判断是否有人脸。…

  • 如何查看顶级域名有几个二级域名

    如何查看顶级域名有几个二级域名

    2021年10月21日
  • Linux 简单命令操作 笔记

    Linux 简单命令操作 笔记

  • python趣味编程100例pdf(python简单实例)

    1#题目:一球从100米高度自由落下,每次落地后反跳回原高度的一半;再落下,求它在第10次落地时,共经过多少米?第10次反弹多高?代码:23h=04li=[]5foriinrange(1,11):6s=100/(2**(i-1))7li.append(s)89forxinli:10h+=x11print(li)12print(…

  • MYSQL explain执行计划解读

    MYSQL explain执行计划解读

  • 阿里云ddns过程记录

    阿里云ddns过程记录申请了阿里云一年的动态域名,收费的,闲置了几个月,用openwrt一直没成功,最近研究asterisk部署,有个公网的ddns还是方便不少,所以把闲置的域名得拾起来了,过程如下1.开启阿里云后台权限(在访问控制菜单中,文章最后有链接)AliyunDNSReadOnlyAccessAliyunDNSFullAccess2.下载脚本运行GitHub-risfeng/aliyun-ddns-shell:阿里云域名解析动态更新IPShell脚本阿里云域名解析动态更新IPSh

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号