使用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账号...

(2)


相关推荐

  • RabbitMQ的优先级队列「建议收藏」

    RabbitMQ的优先级队列「建议收藏」优先级队列队列需要设置优先级队列,消息需要设置消息的优先级。消费者需要等待消息已经发送到队列中,然后对队列中的消息进行排序,最后再去消费。Map<String,Object>arguments=newHashMap<>();arguments.put(“x-max-priority”,10);//设置优先级队列channel.queueDeclare(QUEUE_NAME,false,false,fal

  • sqrt函数原型c语言,C语言sqrt函数的实例用法讲解

    sqrt函数原型c语言,C语言sqrt函数的实例用法讲解前言继承是OOP设计中的重要概念。在C++语言中,派生类继承基类有三种继承方式:私有继承(private)、保护继承(protected)和公有继承(public)。一、继承规则继承是C++中的重要特性,派生2021-03-2218:02:41大家有没有在项目中遇到过,将一些预定义的本地结构体转换为Json字符串后,发送到网络中的情形。那我猜想下大家常规的做法:写一个函数,传入结构体的指针,然后…

  • pycharm远程部署_pycharm 远程调试

    pycharm远程部署_pycharm 远程调试在这之前你要确保服务器上已经创建好虚拟环境你本地已经安装好pycharm1创建本地文件远程服务器上已经有一个文件了。现在你在本地创建一个同名文件。服务器上的虚拟环境为DrQA,所以我在本地新建一个DrQA空文件夹。2用pycharm打开空项目3配置服务器的解释器左上角File→Setting→projectxxx→pythoninterpreter点右上角的小齿轮,然后点add选择SSHInterpreter,然后在上边填上服务器的地址、usernam

    2022年10月29日
  • Python学习路径8——Python对象2

    Python学习路径8——Python对象2

    2021年12月30日
  • centos7下kafka集群搭建

    centos7下kafka集群搭建概述集群安装或者单机安装都可以,这里介绍集群安装。Kafka本身安装包也自带了zookeeper,也可以使用其自带的zookeeper。建议试用自己安装的zookeeper,本教程试用单独安装的zookeeper。安装环境3台centos7虚拟机:10.15.21.6210.10.182.16810.10.182.169kafka_2.10-0.10.2.0zookeeper-3.4.9

  • Sharepoint MasterPage页里的31个ContentPlaceHolder占位符[通俗易懂]

    Sharepoint MasterPage页里的31个ContentPlaceHolder占位符[通俗易懂]自定义SharepointMasterPage页,共有31个ContentPlaceHolder占位符,一个也不能少因为在应用到网站或网页时,网站或网页要向MasterPage页里对应的ContentPlaceHolder里填入内容,若有的ContentPlaceHolder不需要则可以把它隐藏掉而不要将其删除。<%@Masterlanguage=”C#”%…

发表回复

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

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