将处理后的图像保存为 DICOM

def save_as_dicom(image, original_ds, output_path):

    ds = pydicom.Dataset()

    for elem in original_ds:

        if elem.tag not in [0x7FE00010, 0x00080018, 0x00080016]:

            ds.add(elem)

    if hasattr(original_ds, 'file_meta') and hasattr(original_ds.file_meta, 'TransferSyntaxUID'):

        ds.file_meta = original_ds.file_meta.copy()

    else:

        ds.file_meta = pydicom.dataset.FileMetaDataset()

        ds.file_meta.TransferSyntaxUID = ImplicitVRLittleEndian

    ds.is_little_endian = ds.file_meta.TransferSyntaxUID.is_little_endian

    ds.is_implicit_VR = ds.file_meta.TransferSyntaxUID.is_implicit_VR

    ds.SOPInstanceUID = generate_uid()

    ds.SeriesInstanceUID = generate_uid()

    if original_ds.BitsAllocated == 16:

        if original_ds.PixelRepresentation == 0:

            pixel_data = image.astype(np.uint16)

        else:

            pixel_data = image.astype(np.int16)

    else:

        pixel_data = image.astype(np.uint16)

        ds.BitsAllocated = 16

        ds.BitsStored = 16

        ds.HighBit = 15

        ds.PixelRepresentation = 0

    ds.PixelData = pixel_data.tobytes()

    ds.Rows, ds.Columns = pixel_data.shape

    ds.save_as(output_path)

    print(f"已保存DICOM文件: {output_path}")

 

整体说明

这个函数的作用是把处理后的图像(比如添加了伪影的 CT 图像)保存成医学影像专用的 DICOM 格式文件。用大白话一步一步解释:

简述

把 DICOM 文件想象成一个 "带说明书的照片":

  1. 普通照片只有图像内容
  2. DICOM 文件除了图像,还包含病人信息、拍摄设备参数、图像尺寸等 "说明书" 信息

这个函数就像:拿一张处理后的新照片,配上原始照片的说明书(只改必要的部分),最后装成新的 "带说明书的照片"

步骤拆解

  1. 准备一个新的 "说明书外壳"

ds = pydicom.Dataset()

→ 先创建一个空的 DICOM 容器(类似一个空白的说明书模板)。

  1. 复制原始说明书的大部分内容

for elem in original_ds:

    if elem.tag not in [0x7FE00010, 0x00080018, 0x00080016]:

        ds.add(elem)

→ 把原始 DICOM 文件里的信息(病人信息、设备参数等)复制过来,但跳过 3 个特殊标签:

    1. 0x7FE00010:原始图像的像素数据(我们要用新的处理后图像)
    2. 0x00080018、0x00080016:文件的唯一标识(需要换新的)
  1. 处理文件格式信息

if hasattr(original_ds, 'file_meta'):

    ds.file_meta = original_ds.file_meta.copy()  # 复制原始格式信息

else:

    ds.file_meta = ...  # 用默认格式信息

→ 保证新文件的格式和原始文件一致(比如编码方式、字节顺序),否则其他软件可能打不开。

  1. 生成新的唯一标识

ds.SOPInstanceUID = generate_uid()

ds.SeriesInstanceUID = generate_uid()

→ 给新文件生成新的 "身份证号"(每个 DICOM 文件必须有唯一标识,不能和原始文件重复)。

  1. 处理图像数据格式

if original_ds.BitsAllocated == 16:

    pixel_data = image.astype(...)  # 按原始格式转换新图像

else:

    pixel_data = ...  # 用默认格式转换

→ 把处理后的图像数据转换成 DICOM 要求的格式(比如 16 位像素值),保证和原始文件的 "图像规格" 一致。

  1. 放入新图像并保存

ds.PixelData = pixel_data.tobytes()  # 把新图像数据放进DICOM容器

ds.Rows, ds.Columns = pixel_data.shape  # 记录图像尺寸

ds.save_as(output_path)  # 保存成文件

→ 最后把处理后的图像数据放进 DICOM 容器,记录好尺寸,保存成新的 DICOM 文件。

总结

这个函数的核心就是:"复用原始 DICOM 的大部分信息,只替换图像数据和唯一标识,最后生成一个符合标准的新 DICOM 文件"。这样保存的文件既能被医学影像软件识别,又包含了我们处理后的图像内容。

复制 DICOM 文件格式信息:确保新文件能正常打开

    # 关键修复:设置字节序和VR编码方式
    if hasattr(original_ds, 'file_meta') and hasattr(original_ds.file_meta, 'TransferSyntaxUID'):
        ds.file_meta = original_ds.file_meta.copy()
    else:
        # 手动创建文件元数据并设置默认传输语法
        ds.file_meta = pydicom.dataset.FileMetaDataset()
        ds.file_meta.TransferSyntaxUID = ImplicitVRLittleEndian  # 默认小端隐式VR

这句话的作用是 **“复制原始 DICOM 文件的格式信息”**,保证新生成的 DICOM 文件能被正常打开和识别。用大白话拆解:

说明

把 DICOM 文件比作 “加密的 Word 文档”:

  1. file_meta 就像文档的 “加密方式说明”(比如用 AES 加密还是 DES 加密)
  2. TransferSyntaxUID 是说明里的 “核心密钥”(没有它就打不开文档)

这段代码就像: “先检查原始文档有没有‘加密说明’和‘核心密钥’,如果有,就把这两样东西复制到新文档里,让新文档和原始文档用一样的方式加密”。

“先检查原始文档有没有‘加密说明’和‘核心密钥’,如果有,就把这两样东西复制到新文档里,让新文档和原始文档用一样的方式加密”。

逐句说明

  1. if hasattr(original_ds, 'file_meta') and hasattr(original_ds.file_meta, 'TransferSyntaxUID')

→ 先确认两件事:

    1. 原始 DICOM 文件(original_ds)里有file_meta这个 “格式信息包”
    2. 这个包里确实包含TransferSyntaxUID这个 “文件读取规则”
  1. ds.file_meta = original_ds.file_meta.copy()

→ 如果上面两个条件都满足,就把原始文件的file_meta(格式信息包)完整复制到新文件(ds)里。

why?

DICOM 文件有严格的格式标准,不同设备生成的文件可能用不同的 “编码规则”(比如数据存储顺序、压缩方式)。如果新文件的格式规则和原始文件不一致,医学影像软件可能会报错 “无法打开”。

通过复制原始文件的file_meta,可以确保新文件和原始文件 “用同样的规则编码”,避免出现格式不兼容的问题。

这就像你抄作业时,不仅要抄答案,还要抄对 “格式要求”(比如老师要求用 A4 纸、黑色笔):

这两行代码保证了新文件的 “底层读写规则” 和它的 “格式说明” 完全一致,避免出现数据混乱。

确保 DICOM 图像数据格式兼容与通用

这段代码是在处理图像的像素格式”,确保新生成的 DICOM 文件里的图像数据格式和原始文件兼容(或者符合通用标准)。用大白话解释:

先理解两个关键概念:

  1. BitsAllocated:每个像素分配了多少位(bit)的存储空间。比如 16 位(相当于每个像素用 16 个 “0/1” 二进制数表示),能记录的明暗细节更多。
  2. PixelRepresentation:像素值是 “无符号” 还是 “有符号”。
    1. 0 = 无符号(uint16):像素值只能是 0 及以上的正数(比如 0-65535)。
    2. 1 = 有符号(int16):像素值可以是负数(比如 - 32768 到 32767),医学图像中有时用来表示不同组织的相对密度。

代码逻辑拆解:“跟着原始文件的格式走,没有就用通用格式”

  1. 先看原始文件是不是 16 位格式

if original_ds.BitsAllocated == 16:

→ 检查原始 DICOM 文件的像素是不是用 16 位存储的(这是医学图像最常见的格式)。

  1. 如果是 16 位,再看像素值是正还是可负
    1. PixelRepresentation == 0(无符号):

pixel_data = image.astype(np.uint16)

→ 把处理后的图像转换成 16 位无符号整数格式(只能存正数),和原始文件保持一致。

    1. PixelRepresentation != 0(有符号):

pixel_data = image.astype(np.int16)

→ 转换成 16 位有符号整数格式(可以存负数),匹配原始文件。

  1. 如果原始文件不是 16 位(比如 8 位)

else:

    pixel_data = image.astype(np.uint16)  # 强制转换成16位无符号格式

    # 同时给新文件设置16位格式的参数

    ds.BitsAllocated = 16  # 分配16位空间

    ds.BitsStored = 16     # 实际存储16位数据

    ds.HighBit = 15        # 最高位是第15位(0开始计数,15=16-1

    ds.PixelRepresentation = 0  # 无符号(只存正数)

→ 统一转换成 16 位无符号格式(这是 DICOM 的通用标准),并手动设置新文件的格式参数,确保软件能正确识别。

why?

图像的像素格式就像 “数据的语言”:

  1. 如果原始文件用 16 位无符号格式,新文件也必须用同样的格式,否则软件会把像素值读错(比如把亮的地方显示成暗的)。
  2. 如果原始文件格式特殊(比如 8 位),就转换成通用的 16 位格式,保证大部分医学软件都能正常显示。

简单说,这段代码是为了让新文件的图像数据 “说和原始文件一样的语言”(或者说通用语言),避免出现图像显示错乱。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值