博主简介:CSDN博客专家、CSDN平台优质创作者,高级开发工程师,数学专业,10年以上C/C++, C#, Java等多种编程语言开发经验,拥有高级工程师证书;擅长C/C++、C#等开发语言,熟悉Java常用开发技术,能熟练应用常用数据库SQL server,Oracle,mysql,postgresql等进行开发应用,熟悉DICOM医学影像及DICOM协议,业余时间自学JavaScript,Vue,qt,python等,具备多种混合语言开发能力。撰写博客分享知识,致力于帮助编程爱好者共同进步。欢迎关注、交流及合作,提供技术支持与解决方案。
技术合作请加本人wx(注明来自csdn):xt20160813
DICOM(Digital Imaging and Communications in Medicine,医学数字成像和通信标准)是医学影像领域的核心标准,用于存储、传输和处理医学影像及其相关信息。自1985年由美国放射学会(ACR)和国家电气制造商协会(NEMA)首次发布以来,DICOM已成为连接医学影像设备、PACS(Picture Archiving and Communication System)、工作站和医院信息系统的基石。其广泛应用涵盖医院影像科、远程医疗、医学研究以及医疗人工智能(AI)等领域。
DICOM标准的复杂性源于其多功能性:它不仅定义了影像数据的存储格式,还涵盖了元数据管理、通信协议以及医疗工作流的集成。本文将全面解析DICOM标准的核心组成部分,包括文件结构、元数据、影像数据、通信协议及其实际应用场景,并通过详细的示例代码和案例分析帮助读者深入理解其实现方式。文章将分为七个主要部分,涵盖从基础概念到高级应用,再到未来发展趋势的完整内容。
DICOM是一个国际标准,旨在确保医学影像设备和系统之间的互操作性。其主要目标包括:
DICOM标准由多个部分组成(称为“Parts”),每个部分专注于特定功能。以下是几个核心部分的概述:
这些部分共同构成了DICOM标准的完整框架,确保其在技术实现上的严谨性和灵活性。
DICOM标准在以下场景中得到广泛应用:
与传统影像格式(如JPEG、PNG)相比,DICOM具有以下特点:
DICOM文件是DICOM标准的核心,包含影像数据和元数据两大部分。理解DICOM文件结构是掌握DICOM标准的基础。
一个DICOM文件通常由以下部分组成:
文件元信息使用组号0002
的标签,例如:
(0002,0000)
:文件元信息长度。(0002,0010)
:传输语法UID,决定数据的编码方式。DICOM数据集由多个**数据元素(Data Element)**组成,每个数据元素包含以下字段:
(0010,0010)
表示患者姓名。数据集采用键值对形式存储信息,标签决定了数据的语义。例如:
(0010,0010) PN "Doe^John" # 患者姓名
(0028,0010) US 512 # 图像行数
(7FE0,0010) OB [像素数据] # 影像数据
影像数据存储在数据元素(7FE0,0010)
(Pixel Data)中,通常是压缩或未压缩的像素值。DICOM支持多种像素数据格式:
影像数据的编码方式由**传输语法(Transfer Syntax)**决定,常见传输语法包括:
1.2.840.10008.1.2.1
,最常用的编码方式,VR明确指定。1.2.840.10008.1.2
,VR由数据字典推断。1.2.840.10008.1.2.4.50
(JPEG基线),用于减少文件大小。1.2.840.10008.1.2.4.90
,支持无损和有损压缩。以下是一个简化的DICOM文件结构示例,展示了文件元信息和数据集的部分内容:
[128字节文件前缀: 00...00]
[DICOM前缀: DICM]
[文件元信息]
(0002,0000) UL 192 # 文件元信息长度
(0002,0001) OB 00\01 # 文件版本
(0002,0010) UI 1.2.840.10008.1.2.1 # 传输语法(显式VR小端)
(0002,0012) UI 1.2.3.4 # 实现类UID
[数据集]
(0008,0016) UI 1.2.840.10008.5.1.4.1.1.2 # SOP类UID(CT图像)
(0010,0010) PN "Doe^John" # 患者姓名
(0010,0020) LO "12345" # 患者ID
(0028,0010) US 512 # 图像行数
(0028,0011) US 512 # 图像列数
(0028,0100) US 16 # 位分配(16位像素)
(7FE0,0010) OW [像素数据] # 影像数据
DICOM文件的灵活性体现在其支持嵌套结构和私有数据:
SQ
的标签实现,例如(3006,0020)
表示放疗结构集序列,包含多个子数据集。0009,xxxx
)添加非标准数据,需配合私有数据字典解析。DICOM数据字典(PS3.6)是DICOM标准的核心,定义了所有标准标签及其含义。每个标签由一个组号和元素号组成,例如:
(0010,0010)
:患者姓名,组号0010
表示患者信息。(0028,0010)
:图像行数,组号0028
表示影像参数。(0008,0060)
:模态(Modality),如CT、MR、US(超声)。标签分为以下类型:
0009,xxxx
)。数据字典还定义了每个标签的值表示(VR)、值多重性(VM, Value Multiplicity)和条件性:
PN
(人名)、DA
(日期)。1
(单一值)、1-n
(多个值)。值表示定义了数据元素的格式和约束,常见的VR包括:
某些VR支持显式和隐式编码:
DICOM元数据采用层级结构,分为:
序列允许DICOM表示复杂关系。例如,一个检查(Study)包含多个系列(Series),每个系列包含多个图像(Instance)。这种层级结构通过以下标签体现:
(0020,000D)
:检查UID,标识唯一的检查。(0020,000E)
:系列UID,标识唯一的系列。(0008,0018)
:SOP实例UID,标识唯一的图像。元数据在以下场景中发挥关键作用:
像素数据存储在(7FE0,0010)
(Pixel Data)中,通常是未压缩的原始像素值或压缩格式。像素数据的属性由以下元数据描述:
(0028,0010)
和(0028,0011)
,定义图像尺寸。(0028,0100)
,如8位、16位,表示每个像素的存储位数。(0028,0101)
,表示实际使用的位数。(0028,0002)
,如1(灰度)或3(RGB)。(0028,0004)
,如“MONOCHROME2”(灰度)或“RGB”。像素数据可以是以下类型:
DICOM支持多种压缩格式,以减少文件大小和传输时间:
1.2.840.10008.1.2.4.70
。1.2.840.10008.1.2.5
。1.2.840.10008.1.2.4.50
。1.2.840.10008.1.2.4.90
。压缩方式由传输语法决定,解压缩需要特定的库支持,例如:
以下是一个使用Python和pydicom库读取DICOM文件、提取元数据和像素数据的示例代码,并实现基本的图像处理:
import pydicom
import numpy as np
import matplotlib.pyplot as plt
from scipy.ndimage import gaussian_filter
# 读取DICOM文件
dcm_file = "sample.dcm"
try:
dataset = pydicom.dcmread(dcm_file)
except Exception as e:
print(f"读取DICOM文件失败: {e}")
exit()
# 提取元数据
patient_name = dataset.get((0x0010, 0x0010), "Unknown").value # 患者姓名
study_date = dataset.get((0x0008, 0x0020), "Unknown").value # 检查日期
modality = dataset.get((0x0008, 0x0060), "Unknown").value # 模态
rows = dataset.get((0x0028, 0x0010), 0).value # 图像行数
cols = dataset.get((0x0028, 0x0011), 0).value # 图像列数
pixel_spacing = dataset.get((0x0028, 0x0030), [1.0, 1.0]).value # 像素间距
print(f"患者姓名: {patient_name}")
print(f"检查日期: {study_date}")
print(f"模态: {modality}")
print(f"图像尺寸: {rows}x{cols}")
print(f"像素间距: {pixel_spacing}")
# 提取像素数据
if hasattr(dataset, 'pixel_array'):
pixel_data = dataset.pixel_array # 获取numpy数组
# 基本图像处理:高斯平滑
smoothed_data = gaussian_filter(pixel_data, sigma=1)
# 显示原始和处理后的图像
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.imshow(pixel_data, cmap='gray')
plt.title(f"原始影像\n患者: {patient_name}")
plt.axis('off')
plt.subplot(1, 2, 2)
plt.imshow(smoothed_data, cmap='gray')
plt.title("高斯平滑后影像")
plt.axis('off')
plt.tight_layout()
plt.show()
else:
print("无像素数据")
# 处理序列数据(示例:放疗结构集)
if (0x3006, 0x0020) in dataset:
structure_set = dataset[(0x3006, 0x0020)]
print("放疗结构集序列:")
for i, item in enumerate(structure_set):
print(f"序列项 {i+1}: {item}")
代码注释:
pydicom.dcmread
读取DICOM文件,返回一个Dataset
对象。get
方法提供默认值以防标签缺失。pixel_array
属性将像素数据转换为NumPy数组,便于处理。scipy.ndimage.gaussian_filter
进行高斯平滑,展示基本的图像处理。matplotlib
显示原始和处理后的影像。以下是一个生成简单DICOM文件的示例代码,展示如何创建包含元数据和像素数据的文件:
import pydicom
from pydicom.dataset import Dataset, FileDataset
from pydicom.uid import ExplicitVRLittleEndian
import numpy as np
import datetime
# 创建文件元信息
file_meta = Dataset()
file_meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.2' # CT图像SOP类
file_meta.MediaStorageSOPInstanceUID = pydicom.uid.generate_uid() # 生成唯一UID
file_meta.TransferSyntaxUID = ExplicitVRLittleEndian # 显式VR小端
file_meta.ImplementationClassUID = pydicom.uid.generate_uid()
# 创建数据集
ds = Dataset()
ds.file_meta = file_meta
# 添加患者信息
ds.PatientName = "Test^Patient"
ds.PatientID = "12345"
ds.PatientBirthDate = "19800101"
ds.PatientSex = "M"
# 添加检查信息
ds.StudyInstanceUID = pydicom.uid.generate_uid()
ds.SeriesInstanceUID = pydicom.uid.generate_uid()
ds.SOPInstanceUID = file_meta.MediaStorageSOPInstanceUID
ds.SOPClassUID = file_meta.MediaStorageSOPClassUID
ds.StudyDate = datetime.datetime.now().strftime("%Y%m%d")
ds.StudyTime = datetime.datetime.now().strftime("%H%M%S")
ds.Modality = "CT"
# 添加影像参数
ds.Rows = 256
ds.Columns = 256
ds.BitsAllocated = 16
ds.BitsStored = 16
ds.HighBit = 15
ds.PixelRepresentation = 0 # 无符号整数
ds.SamplesPerPixel = 1
ds.PhotometricInterpretation = "MONOCHROME2"
ds.PixelSpacing = [1.0, 1.0]
# 生成模拟像素数据
pixel_data = np.random.randint(0, 1000, (256, 256), dtype=np.uint16)
ds.PixelData = pixel_data.tobytes()
# 保存DICOM文件
filename = "generated.dcm"
dcm_file = FileDataset(filename, ds, preamble=b"\0" * 128)
dcm_file.save_as(filename)
print(f"DICOM文件已保存: {filename}")
代码注释:
FileDataset
保存DICOM文件,包含128字节前缀。DICOM不仅定义了文件格式,还包括一组网络服务,用于设备间通信。常见服务包括:
这些服务基于DICOM消息交换协议(PS3.7),使用TCP/IP传输,端口通常为104或11112。
DICOM通信基于服务对象对(SOP, Service-Object Pair),每个SOP定义了服务(如存储)和对象(如CT图像)。SOP类由唯一的UID标识,例如:
SOP类分为存储SOP类和查询/检索SOP类,分别用于数据传输和信息查询。
以下是一个使用pynetdicom
库实现DICOM C-STORE的示例:
from pynetdicom import AE, sop_class
from pynetdicom.sop_class import CTImageStorage
# 初始化应用实体(AE)
ae = AE(ae_title=b"MY_AE")
# 添加支持的SOP类
ae.add_requested_context(CTImageStorage)
# 连接到远程PACS
try:
assoc = ae.associate("192.168.1.100", 104, ae_title=b"PACS_AE")
except Exception as e:
print(f"连接失败: {e}")
exit()
if assoc.is_established:
# 读取DICOM文件
dataset = pydicom.dcmread("sample.dcm")
# 发送C-STORE请求
status = assoc.send_c_store(dataset)
if status:
print(f"C-STORE成功,状态: {status.Status}")
else:
print("C-STORE失败")
# 释放连接
assoc.release()
else:
print("连接失败")
代码注释:
associate
方法连接到PACS服务器。send_c_store
发送DICOM文件。以下是一个使用pynetdicom
实现C-FIND查询的示例,查询特定患者的影像:
from pynetdicom import AE, sop_class
from pynetdicom.sop_class import PatientRootQueryRetrieveInformationModelFind
# 初始化应用实体
ae = AE(ae_title=b"MY_AE")
# 添加支持的SOP类
ae.add_requested_context(PatientRootQueryRetrieveInformationModelFind)
# 连接到PACS
try:
assoc = ae.associate("192.168.1.100", 104, ae_title=b"PACS_AE")
except Exception as e:
print(f"连接失败: {e}")
exit()
if assoc.is_established:
# 创建C-FIND查询数据集
ds = Dataset()
ds.QueryRetrieveLevel = "PATIENT" # 查询级别:患者
ds.PatientName = "Doe^John" # 查询条件:患者姓名
ds.PatientID = "" # 可选:患者ID
ds.StudyInstanceUID = "" # 返回字段
ds.StudyDate = "" # 返回字段
# 发送C-FIND请求
responses = assoc.send_c_find(ds, PatientRootQueryRetrieveInformationModelFind)
# 处理响应
for (status, dataset) in responses:
if status and dataset:
print(f"查询结果: 患者姓名={dataset.PatientName}, 患者ID={dataset.PatientID}")
else:
print("无匹配结果")
# 释放连接
assoc.release()
else:
print("连接失败")
代码注释:
send_c_find
发送查询请求,迭代处理响应。PACS(影像存档与通信系统)是DICOM的主要应用场景,用于存储、检索和分发医学影像。PACS系统通过DICOM协议与以下设备交互:
PACS的工作流包括:
DICOM文件为医学影像AI提供标准化的输入数据,广泛应用于以下任务:
常用工具包括:
案例研究:肺结节检测
某研究团队使用DICOM格式的胸部CT影像训练YOLOv5模型检测肺结节。流程如下:
DICOM文件支持3D重建和可视化,常见应用包括:
常用工具包括:
案例研究:颅骨3D重建
某医院使用DICOM格式的头颅CT影像进行颅骨3D重建,辅助颅脑手术规划。流程如下:
DICOM-RT是DICOM标准的扩展,专门用于放疗领域,定义了以下对象:
案例研究:前列腺癌放疗
某放疗中心使用DICOM-RT文件制定前列腺癌治疗计划:
为应对DICOM处理中的性能瓶颈,可采用以下优化策略:
multiprocessing
模块处理大型CT数据集。示例代码:并行读取DICOM文件
import pydicom
import glob
import multiprocessing
from concurrent.futures import ProcessPoolExecutor
def read_dicom_file(file_path):
try:
dataset = pydicom.dcmread(file_path)
return {
"file": file_path,
"patient_name": dataset.get((0x0010, 0x0010), "Unknown").value,
"rows": dataset.get((0x0028, 0x0010), 0).value,
"cols": dataset.get((0x0028, 0x0011), 0).value
}
except Exception as e:
return {"file": file_path, "error": str(e)}
# 获取DICOM文件列表
dcm_files = glob.glob("dicom_folder/*.dcm")
# 并行读取
with ProcessPoolExecutor(max_workers=multiprocessing.cpu_count()) as executor:
results = list(executor.map(read_dicom_file, dcm_files))
# 打印结果
for result in results:
if "error" in result:
print(f"文件 {result['file']} 读取失败: {result['error']}")
else:
print(f"文件 {result['file']}: 患者姓名={result['patient_name']}, 尺寸={result['rows']}x{result['cols']}")
代码注释:
glob
获取指定文件夹中的DICOM文件。ProcessPoolExecutor
并行读取文件,利用多核CPU加速处理。DICOM标准是医学影像领域的基石,其文件结构、元数据管理和通信协议为医疗设备和系统的互操作性提供了保障。通过深入理解DICOM文件结构、元数据、影像数据和通信协议,开发者可以构建高效的医学影像应用。本文从基础概念到高级应用,结合示例代码和案例研究,全面解析了DICOM标准的实现方式和实际价值。
未来,随着云技术、AI和DICOMweb的快速发展,DICOM标准将在更广泛的场景中发挥作用。希望本文提供的详细解析和实用代码为读者提供了深入学习DICOM的坚实基础。
提示:若需要更加深入详细的学习DICOM医学影象相关知识,请查看专栏:https://blog.csdn.net/martian665/category_12814545.html