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

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

(1)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • java事务回滚案例_java事务控制

    java事务回滚案例_java事务控制疑问,确实像往常一样在service上添加了注解 @Transactional,为什么查询数据库时还是发现有数据不一致的情况,想想肯定是事务没起作用,出现异常的时候数据没有回滚。于是就对相关代码进行了一番测试,结果发现一下踩进了两个坑,确实是事务未回滚导致的数据不一致。下面总结一下经验教训:Spring事务的管理操作方法编程式的事务管理实际应用中很少使用通过

    2022年8月30日
    7
  • vb中如何连接mysql_vb怎么连接数据库「建议收藏」

    vb怎么连接数据库[编辑]概述我们对数据库进行操作时,一方面要使用户可以在程序界面上对需要的数据进行访问;另一方面可以对数据库中的数据进行各种操作,最终的操作结果还要反馈给用户。一、Data控件1.设置数据控件的属性2.将Data控件连到一个特定的数据库和其中的一个表上3.在VB中建立一个标准的工程4.在窗体中添加控件(Data1就是一个Data控件)5.改变控件的一些属性6.对Data控件进行设…

    2022年4月16日
    61
  • Jenkins(4)docker容器内部修改jenkins容器时间「建议收藏」

    Jenkins(4)docker容器内部修改jenkins容器时间「建议收藏」前言用docker搭建的Jenkins环境时间显示和我们本地时间相差8个小时,需修改容器内部的系统时间查看时间查看系统时间date-R进入docker容器内部,查看容器时间dockere

    2022年7月28日
    14
  • HDU 4883 TIANKENG’s restaurant (贪心)

    HDU 4883 TIANKENG’s restaurant (贪心)

    2022年1月26日
    106
  • mysql如何做到读写分离_MySQL读写分离如何实现?

    主要说下读写分离,当我们的数据量很大时,数据库服务器的压力变大,这时候我们需要从架构方面来解决这一问题,在一个网站中读的操作很多,写的操作很少,这时候我们需要配置读写分离,把读操作和写操作分离出来,最大程度的利用好数据库服务器。读写分离的实现原理就是在执行SQL语句的时候,判断到底是读操作还是写操作,把读的操作转向到读服务器上(从服务器,一般是多台),写的操作转到写的服务器上(主服务器,一般是一台…

    2022年4月6日
    51
  • RMAN传输表空间迁移数据

    RMAN传输表空间迁移数据

    2022年1月29日
    36

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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