DICOM序列状态

什么是 CONSISTENT 状态:规范的三维采样网格

DiCube主要为静态三维医学图像服务,其核心假设是整个3D图像在空间中对应着一个规范的采样网格(regular meshgrid)。当DICOM序列处于CONSISTENT状态时,它满足以下条件:

  1. 统一的像素间距:所有切片的 PixelSpacing 相同
  2. 一致的图像尺寸:所有切片的 RowsColumns 相同
  3. 规律的切片间距:Z 轴方向上切片位置等间距
  4. 统一的图像方向:所有切片的 ImageOrientationPatient 相同
  5. 连续的实例编号InstanceNumber 从 1 开始连续递增
  6. 完整的元数据:所有必需的 DICOM 标签存在且有效
import dicube
from dicube import get_dicom_status, DicomStatus
from dicube.dicom import CommonTags

# 读取示例数据
dirname = 'dicube-testdata/dicom/sample_200'
img = dicube.load_from_dicom_folder(dirname)
meta = img.dicom_meta
status = get_dicom_status(meta)

print(f"当前数据状态: {status.value}")


# 1. 像素间距检查
spacing_consistent = meta.is_shared(CommonTags.PixelSpacing)
print(f"像素间距统一: {spacing_consistent}")
if spacing_consistent:
    spacing = meta.get_shared_value(CommonTags.PixelSpacing)
    print(f"  统一间距: {spacing}mm")

# 2. 图像尺寸检查
shape_consistent = (meta.is_shared(CommonTags.Rows) and 
                   meta.is_shared(CommonTags.Columns))
print(f"图像尺寸统一: {shape_consistent}")
if shape_consistent:
    rows = meta.get_shared_value(CommonTags.Rows)
    cols = meta.get_shared_value(CommonTags.Columns)
    print(f"  统一尺寸: {cols}×{rows}")

# 3. 图像方向检查
orientation_consistent = meta.is_shared(CommonTags.ImageOrientationPatient)
print(f"图像方向统一: {orientation_consistent}")

只有当序列满足所有这些条件时,DiCube才能为其计算准确的空间信息(Space),构建完整的三维采样网格。

非 Location 相关的状态问题

DiCube使用四种模式来分类非Location相关的字段问题:missingnon_uniformgapduplicate

1. Missing:关键字段缺失

# 演示missing问题的检测
import copy
from dicube.dicom.dicom_tags import get_tag_key

def demo_missing_problems():
    print("=== Missing 问题演示 ===")
    
    # 缺失像素间距
    test_meta1 = copy.deepcopy(meta)
    test_meta1._merged_data.pop(get_tag_key(CommonTags.PixelSpacing), None)
    status1 = get_dicom_status(test_meta1)
    print(f"删除PixelSpacing后: {status1.value}")
    
    # 缺失图像尺寸
    test_meta2 = copy.deepcopy(meta)
    test_meta2._merged_data.pop(get_tag_key(CommonTags.Columns), None)
    status2 = get_dicom_status(test_meta2)
    print(f"删除Columns后: {status2.value}")
    
    # 缺失数据类型信息
    test_meta3 = copy.deepcopy(meta)
    test_meta3._merged_data.pop(get_tag_key(CommonTags.BitsStored), None)
    status3 = get_dicom_status(test_meta3)
    print(f"删除BitsStored后: {status3.value}")

demo_missing_problems()

2. Non_uniform:字段值不一致

def demo_non_uniform_problems():
    print("\n=== Non_uniform 问题演示 ===")
    
    # 像素间距不一致
    test_meta1 = copy.deepcopy(meta)
    num_slices = test_meta1.slice_count
    spacing_values = []
    for i in range(num_slices):
        if i < num_slices // 2:
            spacing_values.append([0.5, 0.5])  # 前半段用0.5mm
        else:
            spacing_values.append([1.0, 1.0])  # 后半段用1.0mm
    test_meta1.set_nonshared_item(CommonTags.PixelSpacing, spacing_values)
    status1 = get_dicom_status(test_meta1)
    print(f"设置不一致PixelSpacing后: {status1.value}")
    
    # 图像尺寸不一致
    test_meta2 = copy.deepcopy(meta)
    cols_values = []
    for i in range(num_slices):
        if i < num_slices // 2:
            cols_values.append(512)  # 前半段512列
        else:
            cols_values.append(256)  # 后半段256列
    test_meta2.set_nonshared_item(CommonTags.Columns, cols_values)
    status2 = get_dicom_status(test_meta2)
    print(f"设置不一致Columns后: {status2.value}")

demo_non_uniform_problems()

3. Duplicate:重复值

def demo_duplicate_problems():
    print("\n=== Duplicate 问题演示 ===")
    
    # 实例编号重复
    test_meta = copy.deepcopy(meta)
    num_slices = test_meta.slice_count
    # 所有切片都使用相同的实例编号
    duplicate_numbers = [1] * num_slices
    test_meta.set_nonshared_item(CommonTags.InstanceNumber, duplicate_numbers)
    status = get_dicom_status(test_meta)
    print(f"设置重复InstanceNumber后: {status.value}")
    
    # 展示重复检测逻辑
    instance_numbers = test_meta.get_values(CommonTags.InstanceNumber)
    unique_count = len(set(instance_numbers))
    total_count = len(instance_numbers)
    print(f"  实例编号: {instance_numbers[:5]}... (总共{total_count}个)")
    print(f"  唯一值数量: {unique_count}")
    print(f"  检测逻辑: unique_count({unique_count}) < total_count({total_count}) = {unique_count < total_count}")

demo_duplicate_problems()

4. Gap:数值跳跃

def demo_gap_problems():
    print("\n=== Gap 问题演示 ===")
    
    # 实例编号跳跃
    test_meta = copy.deepcopy(meta)
    num_slices = test_meta.slice_count
    
    # 创建有跳跃的实例编号序列:1,2,3,5,6,7,8...
    gap_numbers = list(range(1, num_slices + 1))
    for i in range(3, len(gap_numbers)):  # 从第4个开始,所有编号+1
        gap_numbers[i] += 1
    
    test_meta.set_nonshared_item(CommonTags.InstanceNumber, gap_numbers)
    status = get_dicom_status(test_meta)
    print(f"设置跳跃InstanceNumber后: {status.value}")
    
    # 展示跳跃检测逻辑
    instance_numbers = test_meta.get_values(CommonTags.InstanceNumber)
    sorted_numbers = sorted([int(x) for x in instance_numbers])
    print(f"  排序后的实例编号: {sorted_numbers[:8]}...")
    
    # 检查连续性
    diffs = [sorted_numbers[i+1] - sorted_numbers[i] for i in range(len(sorted_numbers)-1)]
    print(f"  相邻差值: {diffs[:7]}...")
    gap_detected = not all(d == 1 for d in diffs)
    print(f"  检测逻辑: 存在非1的差值 = {gap_detected}")

demo_gap_problems()

Location 相关的状态问题

Location相关的问题主要涉及切片在三维空间中的位置排列,这些问题会破坏规范采样网格的连续性。

1. Missing Location:位置信息缺失

当DICOM序列中既缺少ImagePositionPatient又缺少SliceLocation字段时,DiCube无法确定切片的空间位置。

检测逻辑:需要ImagePositionPatient OR SliceLocation,当两者都缺失时 → MISSING_LOCATION

2. Dwelling Location:位置停滞

这是位置序列中出现重复值的情况,表示多个切片具有相同的空间位置。

数值示例

  • 正常序列:[1.0, 2.0, 3.0, 4.0, 5.0] (间距为1.0)
  • 停滞序列:[1.0, 2.0, 2.0, 3.0, 4.0] (第2和第3个位置相同)
  • 位置差值:[1.0, 0.0, 1.0, 1.0]

检测逻辑:当位置差值中存在零值时 → DWELLING_LOCATION

3. Reversed Location:位置方向混乱

Z轴位置序列中同时存在正向和反向移动,表示扫描方向不一致或序列被打乱。

数值示例

  • 正常序列:[1.0, 2.0, 3.0, 4.0, 5.0] (单调递增)
  • 混乱序列:[1.0, 2.0, 3.0, 4.0, 2.0, 5.0, 6.0] (第5个位置倒退)
  • 位置差值:[1.0, 1.0, 1.0, -2.0, 3.0, 1.0]

检测逻辑:当位置差值中同时存在正值和负值时 → REVERSED_LOCATION

4. Gap Location:位置跳跃

Z轴位置序列中出现不规律的大间隙,破坏了等间距采样的假设。

数值示例

  • 正常序列:[10.0, 15.0, 20.0, 25.0, 30.0] (间距为5.0)
  • 跳跃序列:[10.0, 15.0, 20.0, 35.0, 40.0] (第4个位置跳跃)
  • 位置差值:[5.0, 5.0, 15.0, 5.0]
  • 平均间距:5.0
  • 相对偏差:[0%, 0%, 200%, 0%]

检测逻辑:当某个间距偏离平均值超过50%时 → GAP_LOCATION

DiCube的兼容性处理策略

DiCube尽力在各种异常情况下保持兼容性,采用”尽力而为”的原则来处理有问题的DICOM数据。当检测到数据质量问题时,DiCube仍然能够加载和处理数据,但会相应地限制某些功能。

处理策略细节

无法计算Space信息的状态

  • MISSING_SPACINGNON_UNIFORM_SPACING:像素间距问题
  • MISSING_ORIENTATIONNON_UNIFORM_ORIENTATION:图像方向问题
  • MISSING_LOCATIONREVERSED_LOCATIONDWELLING_LOCATIONGAP_LOCATION:位置信息问题

处理方式

  1. 像素数据:正常加载和处理,应用必要的rescale变换
  2. 元数据:完整保留所有DICOM标签信息,确保往返转换的完整性
  3. 空间信息:当检测到空间相关问题时,将Space设置为None并警告用户
  4. 功能限制:无法进行需要精确空间信息的操作,如空间变换、重采样、配准等

这种设计确保了DiCube在面对现实世界中不完美的DICOM数据时,仍能提供基本的图像处理功能,同时明确告知用户哪些高级功能不可用。

错误优先级:只报告最严重的问题

DiCube的状态检查遵循严格的优先级原则:按照严重程度从高到低依次检查,只报告发现的第一个(最严重的)问题。这种设计避免了信息过载,有助于用户专注于解决最关键的问题。

检查优先级顺序

  1. Series UID问题(最高优先级)
    • MISSING_SERIES_UID:缺失序列标识符
    • NON_UNIFORM_SERIES_UID:序列标识符不统一
  2. Instance Number问题
    • MISSING_INSTANCE_NUMBER:缺失实例编号
    • DUPLICATE_INSTANCE_NUMBERS:实例编号重复
    • GAP_INSTANCE_NUMBER:实例编号跳跃
  3. 数据类型问题
    • MISSING_DTYPE:缺失数据类型信息
    • NON_UNIFORM_DTYPE:数据类型不一致
  4. 空间参数问题
    • MISSING_SPACING:缺失像素间距
    • NON_UNIFORM_SPACING:像素间距不一致
    • MISSING_SHAPE:缺失图像尺寸
    • NON_UNIFORM_SHAPE:图像尺寸不一致
    • MISSING_ORIENTATION:缺失图像方向
    • NON_UNIFORM_ORIENTATION:图像方向不一致
  5. 位置问题
    • MISSING_LOCATION:缺失位置信息
    • REVERSED_LOCATION:位置方向混乱
    • DWELLING_LOCATION:位置停滞
    • GAP_LOCATION:位置跳跃
  6. 其他问题
    • NON_UNIFORM_RESCALE_FACTOR:校正参数不一致
  7. 理想状态
    • CONSISTENT:所有检查通过

总结

DicomStatus系统通过系统化的状态检查,确保DiCube能够:

  1. 识别理想状态:CONSISTENT状态代表规范的三维采样网格
  2. 分类问题类型:用missing、non_uniform、gap、duplicate四种模式描述非Location问题
  3. 检测空间异常:专门处理dwelling、reversed、gap等Location相关问题
  4. 智能兼容处理:在数据有问题时仍能部分工作,但合理限制功能
  5. 优先级管理:按严重程度报告问题,指导用户逐步修复

这个系统为DiCube的可靠性和鲁棒性提供了重要保障,确保在各种真实世界的数据质量情况下都能给出合理的处理方案。