Rust DICOM传输语法解析库dicom-transfer-syntax-registry的使用,支持医学影像数据的高效编解码与格式转换

Rust DICOM传输语法解析库dicom-transfer-syntax-registry的使用,支持医学影像数据的高效编解码与格式转换

概述

dicom-transfer-syntax-registry是DICOM-rs项目的一个子模块,实现了DICOM传输语法的注册表功能,并支持可选的扩展。

特性

  • 基于inventory的实现可通过Cargo特性inventory-registry启用
  • inventory允许用户在编译时以插件方式注册新的传输语法实现
  • 注意:并非所有环境都支持inventory(如WebAssembly)

安装

在项目目录中运行以下Cargo命令:

cargo add dicom-transfer-syntax-registry

或者在Cargo.toml中添加:

dicom-transfer-syntax-registry = "0.8.2"

完整示例代码

use dicom_transfer_syntax_registry::TransferSyntaxRegistry;
use dicom_core::TransferSyntax;
use dicom_encoding::adapters::PixelDataObject;
use dicom_object::FileMetaTable;

fn main() {
    // 获取默认传输语法注册表
    let registry = TransferSyntaxRegistry::default();
    
    // 查找特定的传输语法
    let ts = registry.get("1.2.840.10008.1.2.4.50").unwrap(); // JPEG Baseline
    
    // 解码示例
    let dicom_object = /* 从文件或网络获取的DICOM对象 */;
    let pixel_data = /* 获取像素数据 */;
    
    // 使用传输语法解码像素数据
    let decoded = ts.decode_pixel_data(&pixel_data).unwrap();
    
    // 编码示例
    let output = ts.encode_pixel_data(&decoded).unwrap();
    
    // 创建新的文件元信息表
    let meta = FileMetaTable::new()
        .transfer_syntax(ts.uid())
        .build();
    
    // 使用新的元信息保存DICOM文件
    // ...
}

// 自定义传输语法实现示例
#[cfg(feature = "inventory-registry")]
mod custom_ts {
    use dicom_transfer_syntax_registry::TransferSyntaxFactory;
    use dicom_core::{TransferSyntax, Uid};
    use dicom_encoding::adapters::{PixelDataObject, DecodeResult};
    
    struct CustomTransferSyntax;
    
    impl TransferSyntaxFactory for CustomTransferSyntax {
        fn create(&self) -> TransferSyntax {
            TransferSyntax::new(
                Uid::new("1.3.6.1.4.1.9590.100.1.2.1"), // 自定义UID
                "Custom Transfer Syntax",
                true,  // 是否封装
                true,  // 是否无损
                true,  // 是否像素数据
                vec![], // 实现类
            )
        }
        
        fn decode_pixel_data(&self, data: &dyn PixelDataObject) -> DecodeResult {
            // 自定义解码逻辑
            unimplemented!()
        }
    }
    
    // 使用inventory注册自定义传输语法
    inventory::submit! {
        &CustomTransferSyntax as &dyn TransferSyntaxFactory
    }
}

使用说明

  1. 默认注册表包含了DICOM标准中定义的所有传输语法
  2. 使用get方法通过UID查找特定的传输语法
  3. 每个传输语法提供了编解码像素数据的方法
  4. 可以通过inventory-registry特性注册自定义传输语法

许可证

MIT OR Apache-2.0

完整示例demo

以下是一个完整的DICOM文件编解码示例:

use dicom_transfer_syntax_registry::TransferSyntaxRegistry;
use dicom_core::TransferSyntax;
use dicom_object::open_file;
use dicom_encoding::adapters::PixelDataObject;
use std::path::Path;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 加载DICOM文件
    let obj = open_file("sample.dcm")?;
    
    // 获取传输语法注册表
    let registry = TransferSyntaxRegistry::default();
    
    // 获取文件使用的传输语法
    let meta = obj.meta();
    let ts_uid = meta.transfer_syntax();
    let ts = registry.get(ts_uid)?;
    
    // 获取像素数据
    let pixel_data = obj.element_by_name("PixelData")?.to_bytes()?;
    
    // 解码像素数据
    let decoded = ts.decode_pixel_data(&pixel_data)?;
    println!("解码成功,像素数据大小: {:?}", decoded.len());
    
    // 编码为JPEG Baseline (1.2.840.10008.1.2.4.50)
    let jpeg_ts = registry.get("1.2.840.10008.1.2.4.50")?;
    let jpeg_data = jpeg_ts.encode_pixel_data(&decoded)?;
    println!("JPEG编码成功,数据大小: {:?}", jpeg_data.len());
    
    Ok(())
}

自定义传输语法完整示例

use dicom_transfer_syntax_registry::TransferSyntaxRegistry;
use dicom_core::{TransferSyntax, Uid};
use dicom_encoding::adapters::{PixelDataObject, DecodeResult};

// 启用inventory注册特性
#[cfg(feature = "inventory-registry")]
mod my_ts {
    use super::*;
    
    pub struct MyTransferSyntax;
    
    impl TransferSyntaxFactory for MyTransferSyntax {
        fn create(&self) -> TransferSyntax {
            TransferSyntax::new(
                Uid::new("1.3.6.1.4.1.9590.100.1.2.100"), // 自定义UID
                "My Custom Transfer Syntax",
                true,  // 封装格式
                true,  // 无损压缩
                true,  // 支持像素数据
                vec![], // 实现类
            )
        }
        
        fn decode_pixel_data(&self, data: &dyn PixelDataObject) -> DecodeResult {
            // 简单示例:直接返回原始数据
            Ok(data.to_bytes()?.to_vec())
        }
        
        fn encode_pixel_data(&self, data: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
            // 简单示例:直接返回输入数据
            Ok(data.to_vec())
        }
    }
    
    // 注册自定义传输语法
    inventory::submit! {
        &MyTransferSyntax as &dyn TransferSyntaxFactory
    }
}

fn main() {
    // 获取注册表实例
    let registry = TransferSyntaxRegistry::default();
    
    // 查找自定义传输语法
    if let Ok(ts) = registry.get("1.3.6.1.4.1.9590.100.1.2.100") {
        println!("找到自定义传输语法: {}", ts.name());
        
        // 测试编解码
        let test_data = vec![0u8, 1, 2, 3, 4, 5];
        let encoded = ts.encode_pixel_data(&test_data).unwrap();
        let decoded = ts.decode_pixel_data(&encoded).unwrap();
        
        assert_eq!(test_data, decoded);
        println!("自定义传输语法测试成功!");
    } else {
        println!("未找到自定义传输语法,请确保启用了inventory-registry特性");
    }
}

1 回复

Rust DICOM传输语法解析库:dicom-transfer-syntax-registry

介绍

dicom-transfer-syntax-registry 是一个专门用于处理 DICOM(医学数字成像和通信)传输语法的 Rust 库。它提供了对 DICOM 数据的高效编解码和格式转换支持,是处理医学影像数据的理想工具。

该库主要功能包括:

  • 支持多种 DICOM 传输语法的注册和管理
  • 提供高效的编解码实现
  • 支持 DICOM 数据格式转换
  • 适用于医学影像处理应用

安装

Cargo.toml 中添加依赖:

[dependencies]
dicom-transfer-syntax-registry = "0.1"

基本使用方法

1. 注册传输语法

use dicom_transfer_syntax_registry::TransferSyntaxRegistry;
use dicom_encoding::transfer_syntax::TransferSyntax;

fn main() {
    let mut registry = TransferSyntaxRegistry::new();
    
    // 注册一个传输语法
    let ts = TransferSyntax::new("1.2.840.10008.1.2", "Little Endian Explicit");
    registry.register(ts);
    
    // 检查是否已注册
    assert!(registry.contains("1.2.840.10008.1.2"));
}

2. 编解码 DICOM 数据

use dicom_transfer-syntax_registry::TransferSyntaxRegistry;
use dicom_object::mem::InMemDicomObject;

fn encode_decode_example() {
    let registry = TransferSyntaxRegistry::default(); // 使用默认注册的传输语法
    
    // 创建一个简单的 DICOM 对象
    let mut obj = InMemDicomObject::new_empty();
    obj.put_str("0008,0060", "CT").unwrap();
    
    // 编码
    let encoded = registry.encode(&obj, "1.2.840.10008.1.2.1").unwrap();
    
    // 解码
    let decoded = registry.decode(&encoded[..]).unwrap();
    
    assert_eq!(decoded.element("0008,0060").unwrap().to_str().unwrap(), "CT");
}

3. 格式转换

use dicom_transfer-syntax-registry::TransferSyntaxRegistry;

fn convert_format() {
    let registry = TransferSyntaxRegistry::default();
    
    // 假设我们有以显式 VR 小端格式编码的数据
    let explicit_le_data: Vec<u8> = get_dicom_data();
    
    // 转换为隐式 VR 小端格式
    let converted = registry.convert(
        &explicit_le_data[..],
        "1.2.840.10008.1.2",   // 源传输语法
        "1.2.840.10008.1.2.1"  // 目标传输语法
    ).unwrap();
}

高级用法

自定义编解码器

use dicom_transfer-syntax_registry::TransferSyntaxRegistry;
use dicom_encoding::transfer_syntax::AdapterFreeTransferSyntax;

fn custom_codec() {
    let mut registry = TransferSyntaxRegistry::new();
    
    // 创建自定义传输语法
    let custom_ts = AdapterFreeTransferSyntax::new(
        "1.2.3.4.5",
        "My Custom TS",
        vec!["1.2.840.10008.1.2"], // 兼容的传输语法
        // 这里需要提供实际的编解码实现
        my_encoder_fn,
        my_decoder_fn,
    );
    
    registry.register(custom_ts);
}

批量处理 DICOM 文件

use dicom_transfer-syntax_registry::TransferSyntaxRegistry;
use std::path::Path;

fn batch_convert(input_dir: &Path, output_dir: &Path) {
    let registry = TransferSyntaxRegistry::default();
    
    for entry in input_dir.read_dir().unwrap() {
        let entry = entry.unwrap();
        let path = entry.path();
        
        if path.extension().map(|e| e == "dcm").unwrap_or(false) {
            let data = std::fs::read(&path).unwrap();
            
            // 转换为JPEG有损压缩格式
            let converted = registry.convert(
                &data[..],
                "1.2.840.10008.1.2",    // 假设原始是隐式VR小端
                "1.2.840.10008.1.2.4.50" // JPEG有损压缩
            ).unwrap();
            
            let output_path = output_dir.join(entry.file_name());
            std::fs::write(output_path, converted).unwrap();
        }
    }
}

注意事项

  1. 该库需要与 dicom-objectdicom-encoding 等其他 DICOM 相关库配合使用
  2. 处理大型医学影像时要注意内存使用情况
  3. 格式转换可能会导致数据精度损失(特别是转换为有损压缩格式时)

总结

dicom-transfer-syntax-registry 为 Rust 开发者提供了强大的 DICOM 数据处理能力,特别适合医学影像处理和 PACS 系统开发。通过灵活的传输语法注册和管理机制,开发者可以轻松实现各种 DICOM 数据的编解码和格式转换需求。

完整示例

下面是一个完整的示例,展示如何使用 dicom-transfer-syntax-registry 处理 DICOM 文件:

use dicom_transfer_syntax_registry::TransferSyntaxRegistry;
use dicom_object::mem::InMemDicomObject;
use std::path::Path;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 1. 初始化传输语法注册表
    let mut registry = TransferSyntaxRegistry::new();
    
    // 注册常用传输语法
    registry.register(TransferSyntax::new(
        "1.2.840.10008.1.2", 
        "Little Endian Implicit"
    ));
    registry.register(TransferSyntax::new(
        "1.2.840.10008.1.2.1", 
        "Little Endian Explicit"
    ));
    
    // 2. 创建并编码一个DICOM对象
    let mut dicom_obj = InMemDicomObject::new_empty();
    dicom_obj.put_str("0008,0060", "CT")?;  // 模态
    dicom_obj.put_str("0010,0010", "张三")?; // 患者姓名
    
    // 编码为显式VR小端格式
    let encoded_data = registry.encode(&dicom_obj, "1.2.840.10008.1.2.1")?;
    
    // 3. 解码DICOM数据
    let decoded_obj = registry.decode(&encoded_data[..])?;
    
    // 验证解码后的数据
    assert_eq!(decoded_obj.element("0008,0060")?.to_str()?, "CT");
    assert_eq!(decoded_obj.element("0010,0010")?.to_str()?, "张三");
    
    // 4. 批量转换DICOM文件
    let input_dir = Path::new("input_dicom");
    let output_dir = Path::new("output_dicom");
    
    if !output_dir.exists() {
        std::fs::create_dir(output_dir)?;
    }
    
    for entry in input_dir.read_dir()? {
        let entry = entry?;
        let path = entry.path();
        
        if path.extension().map(|e| e == "dcm").unwrap_or(false) {
            let data = std::fs::read(&path)?;
            
            // 转换为JPEG有损压缩格式
            let converted = registry.convert(
                &data[..],
                "1.2.840.10008.1.2",    // 假设原始是隐式VR小端
                "1.2.840.10008.1.2.4.50" // JPEG有损压缩
            )?;
            
            let output_path = output_dir.join(entry.file_name());
            std::fs::write(output_path, converted)?;
        }
    }
    
    Ok(())
}

这个完整示例展示了:

  1. 如何初始化传输语法注册表
  2. 如何创建和编码DICOM对象
  3. 如何解码DICOM数据
  4. 如何进行批量文件转换

所有操作都包含错误处理,确保在实际应用中能够正确处理各种异常情况。

回到顶部