Rust DICOM解析库dicom-object的使用,支持医学影像数据的读取与操作

Rust DICOM解析库dicom-object的使用,支持医学影像数据的读取与操作

DICOM-rs object是DICOM-rs生态系统中的一个子项目,旨在为DICOM对象提供高级抽象。它支持从文件或读取器(reader)中检索DICOM对象,并将其作为属性树进行分析。

安装

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

cargo add dicom-object

或者在Cargo.toml中添加:

dicom-object = "0.8.2"

基本使用示例

下面是一个完整的示例,展示如何使用dicom-object库读取和操作DICOM文件:

use dicom_object::open_file;
use dicom_core::Tag;
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    // 打开DICOM文件
    let obj = open_file("example.dcm")?;
    
    // 获取患者姓名 (0010,0010)
    if let Some(patient_name) = obj.element(Tag(0x0010, 0x0010))?.to_str() {
        println!("Patient Name: {}", patient_name);
    }
    
    // 获取患者ID (0010,0020)
    if let Some(patient_id) = obj.element(Tag(0x0010, 0x0020))?.to_str() {
        println!("Patient ID: {}", patient_id);
    }
    
    // 获取模态 (0008,0060)
    if let Some(modality) = obj.element(Tag(0x0008, 0x0060))?.to_str() {
        println!("Modality: {}", modality);
    }
    
    // 获取像素数据 (7FE0,0010)
    if let Ok(pixel_data) = obj.element(Tag(0x7FE0, 0x0010)) {
        println!("Pixel data found");
        // 这里可以进一步处理像素数据
    }
    
    Ok(())
}

高级使用示例

下面是一个更复杂的示例,展示如何遍历DICOM文件中的所有元素:

use dicom_object::open_file;
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    // 打开DICOM文件
    let obj = open_file("example.dcm")?;
    
    // 遍历所有数据元素
    for elem in obj.metadata().all() {
        println!("Tag: {:?}", elem.tag());
        println!("VR: {:?}", elem.vr());
        println!("Value: {:?}", elem.value());
        println!("---");
    }
    
    Ok(())
}

异常处理

处理DICOM文件时可能会遇到各种错误,下面展示如何进行错误处理:

use dicom_object::open_file;
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    match open_file("example.dcm") {
        Ok(obj) => {
            println!("Successfully opened DICOM file");
            // 处理文件...
        },
        Err(e) => {
            eprintln!("Failed to open DICOM file: {}", e);
            if let Some(io_err) = e.source().and_then(|e| e.downcast_ref::<std::io::Error>()) {
                eprintln!("IO Error: {}", io_err);
            }
        }
    }
    
    Ok(())
}

完整示例代码

下面是一个结合基本使用和高级功能的完整示例:

use dicom_object::open_file;
use dicom_core::{Tag, VR};
use std::error::Error;
use std::path::Path;

fn main() -> Result<(), Box<dyn Error>> {
    // 定义DICOM文件路径
    let path = Path::new("example.dcm");
    
    // 打开DICOM文件并进行错误处理
    let obj = match open_file(path) {
        Ok(obj) => {
            println!("成功打开DICOM文件: {}", path.display());
            obj
        },
        Err(e) => {
            eprintln!("无法打开DICOM文件: {}", e);
            if let Some(io_err) = e.source().and_then(|e| e.downcast_ref::<std::io::Error>()) {
                eprintln!("IO错误详情: {}", io_err);
            }
            return Err(e);
        }
    };
    
    // 打印基本信息
    println!("\nDICOM基本信息:");
    print_dicom_metadata(&obj)?;
    
    // 打印所有元素
    println!("\n遍历所有DICOM元素:");
    print_all_dicom_elements(&obj)?;
    
    Ok(())
}

// 打印DICOM基本元数据
fn print_dicom_metadata(obj: &dicom_object::FileDicomObject) -> Result<(), Box<dyn Error>> {
    // 患者信息
    if let Some(patient_name) = obj.element(Tag(0x0010, 0x0010))?.to_str() {
        println!("患者姓名: {}", patient_name);
    }
    
    if let Some(patient_id) = obj.element(Tag(0x0010, 0x0020))?.to_str() {
        println!("患者ID: {}", patient_id);
    }
    
    // 检查信息
    if let Some(modality) = obj.element(Tag(0x0008, 0x0060))?.to_str() {
        println!("检查模态: {}", modality);
    }
    
    if let Some(study_date) = obj.element(Tag(0x0008, 0x0020))?.to_str() {
        println!("检查日期: {}", study_date);
    }
    
    // 图像信息
    if let Ok(rows) = obj.element(Tag(0x0028, 0x0010))?.to_int::<u16>() {
        println!("图像行数: {}", rows);
    }
    
    if let Ok(cols) = obj.element(Tag(0x0028, 0x0011))?.to_int::<u16>() {
        println!("图像列数: {}", cols);
    }
    
    Ok(())
}

// 打印所有DICOM元素
fn print_all_dicom_elements(obj: &dicom_object::FileDicomObject) -> Result<(), Box<dyn Error>> {
    for elem in obj.metadata().all() {
        let tag = elem.tag();
        let vr = elem.vr();
        
        // 打印元素标签和VR
        println!("Tag: {:?} VR: {:?}", tag, vr);
        
        // 根据VR类型打印值
        match vr {
            VR::AE | VR::AS | VR::AT | VR::CS | VR::DA | VR::DS | 
            VR::DT | VR::FL | VR::FD | VR::IS | VR::LO | VR::LT | 
            VR::OB | VR::OD | VR::OF | VR::OL | VR::OW | VR::PN | 
            VR::SH | VR::SL | VR::SQ | VR::SS | VR::ST | VR::TM | 
            VR::UC | VR::UI | VR::UL | VR::UN | VR::UR | VR::US | 
            VR::UT => {
                if let Ok(str_value) = elem.to_str() {
                    println!("Value: {}", str_value);
                } else {
                    println!("Value: [二进制数据]");
                }
            },
            _ => println!("Value: [复杂类型数据]"),
        }
        
        println!("---");
    }
    
    Ok(())
}

注意事项

  • dicom-object是DICOM-rs项目的一部分,通常与父crate dicom一起使用
  • 该库支持MIT或Apache-2.0许可证
  • 当前版本为0.8.2,适用于Rust 1.72.0及以上版本

1 回复

Rust DICOM解析库dicom-object使用指南

概述

dicom-object是一个用于处理DICOM(医学数字成像和通信)文件的Rust库,它提供了读取、解析和操作DICOM数据的功能。DICOM是医学影像领域广泛使用的标准格式,包含图像数据和丰富的元数据。

安装

在Cargo.toml中添加依赖:

[dependencies]
dicom-object = "0.4"
dicom-core = "0.5"

基本使用方法

读取DICOM文件

use dicom_object::open_file;
use std::path::Path;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 打开DICOM文件
    let obj = open_file("example.dcm")?;
    
    // 获取元数据
    let meta = obj.meta();
    println!("Transfer Syntax: {:?}", meta.transfer_syntax());
    
    // 访问数据集
    let dataset = obj.into_inner();
    if let Some(patient_name) = dataset.element_by_name("PatientName")?.to_str() {
        println!("Patient Name: {}", patient_name);
    }
    
    Ok(())
}

创建DICOM对象

use dicom_object::InMemDicomObject;
use dicom_core::dicom_value;
use dicom_core::header::{DataElementHeader, VR};
use dicom_core::Tag;

fn create_dicom_object() -> InMemDicomObject {
    let mut obj = InMemDicomObject::new_empty();
    
    // 添加PatientName元素
    obj.put(DataElementHeader::new(Tag(0x0010, 0x0010), VR::PN), 
           dicom_value!(Str, "John Doe"));
    
    // 添加PatientID元素
    obj.put(DataElementHeader::new(Tag(0x0010, 0x0020), VR::LO), 
           dicom_value!(Str, "12345"));
    
    obj
}

遍历DICOM元素

use dicom_object::open_file;

fn print_all_elements() -> Result<(), Box<dyn std::error::Error>> {
    let obj = open_file("example.dcm")?;
    let dataset = obj.into_inner();
    
    for elem in dataset.iter() {
        println!("Tag: {:?}, VR: {:?}, Value: {:?}", 
                elem.tag(), 
                elem.vr(), 
                elem.value());
    }
    
    Ok(())
}

高级功能

处理像素数据

use dicom_object::open_file;
use dicom_pixeldata::PixelDecoder;

fn extract_pixel_data() -> Result<(), Box<dyn std::error::Error>> {
    let obj = open_file("ct_scan.dcm")?;
    
    // 解码像素数据
    let pixel data = obj.decode_pixel_data()?;
    let image = pixel_data.to_dynamic_image()?;
    
    // 获取图像属性
    println!("Image dimensions: {}x{}", 
            image.width(), 
            image.height());
    println!("Bits per sample: {}", 
            pixel_data.bits_allocated());
    
    Ok(())
}

修改DICOM数据

use dicom_object::open_file;

fn anonymize_dicom() -> Result<(), Box<dyn std::error::Error>> {
    let mut obj = open_file("patient_data.dcm")?;
    let dataset = obj.into_inner_mut();
    
    // 匿名化患者信息
    dataset.put(DataElementHeader::new(Tag(0x0010, 0x0010), 
               dicom_value!(Str, "Anonymous"));
    dataset.put(DataElementHeader::new(Tag(0x0010, 0x0020), 
               dicom_value!(Str, "00000"));
    
    // 保存修改后的文件
    obj.write_to_file("anonymous_data.dcm")?;
    
    Ok(())
}

错误处理

dicom-object提供了详细的错误类型,建议使用Rust的?操作符进行错误传播:

use dicom_object::{Error, open_file};

fn process_dicom() -> Result<(), Error> {
    let obj = open_file("invalid.dcm")?;
    // 处理数据...
    Ok(())
}

fn main() {
    if let Err(e) = process_dicom() {
        eprintln!("Failed to process DICOM file: {}", e);
    }
}

性能考虑

对于大型DICOM文件(如包含大量图像的系列),可以使用流式处理:

use dicom_object::open_file;
use dicom_object::FileDicomObject;
use dicom_object::mem::InMemDicomObject;

fn process_large_file() -> Result<(), Box<dyn std::error::Error>> {
    // 仅加载元数据
    let obj: FileDicomObject<_> = open_file("large_series.dcm")?;
    
    // 按需加载特定元素
    if let Some(series_desc) = obj.element_by_name("SeriesDescription")?.to_str() {
        println!("Series Description: {}", series_desc);
    }
    
    // 完全加载到内存
    let in_mem_obj: InMemDicomObject = obj.into();
    
    Ok(())
}

完整示例代码

以下是一个完整的DICOM文件处理示例,包含读取、修改和保存功能:

use dicom_object::{open_file, InMemDicomObject};
use dicom_core::{dicom_value, header::{DataElementHeader, VR}, Tag};
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    // 1. 读取DICOM文件
    let mut obj = open_file("input.dcm")?;
    
    // 2. 显示基本信息
    let meta = obj.meta();
    println!("SOP Class UID: {}", meta.sop_class.uid());
    println!("Transfer Syntax: {:?}", meta.transfer_syntax());
    
    // 3. 访问患者信息
    let dataset = obj.into_inner();
    if let Some(patient_name) = dataset.element_by_name("PatientName")?.to_str() {
        println!("Original Patient Name: {}", patient_name);
    }
    
    // 4. 创建内存中的DICOM对象并修改
    let mut new_obj = InMemDicomObject::new_empty();
    
    // 复制元数据
    new_obj.meta = meta.clone();
    
    // 添加修改后的患者信息
    new_obj.put(
        DataElementHeader::new(Tag(0x0010, 0x0010), VR::PN),
        dicom_value!(Str, "Anonymous")
    );
    
    // 5. 保存修改后的文件
    new_obj.write_to_file("output.dcm")?;
    println!("DICOM file saved successfully");
    
    Ok(())
}

总结

dicom-object库为Rust开发者提供了强大的DICOM数据处理能力,从简单的元数据读取到复杂的像素数据操作。通过结合Rust的安全性和性能特点,它是医学影像处理应用的理想选择。

回到顶部