Enet4 / dicom-rs

Rust implementation of the DICOM standard
https://dicom-rs.github.io
Apache License 2.0
402 stars 75 forks source link

Iterate over all tags with an AttributeSelector #415

Open kaspermarstal opened 10 months ago

kaspermarstal commented 10 months ago

Hi, how do I

  1. Build an InMemDicomObject with nested sequences in code?
  2. Iterate over all data elements in the InMemDicomObject (including sequences) whilst keeping track of the path of the data elements? Can I somehow get access to an AttributeSelector and its list of steps when iterating?

Keep up the great work. Very impressive.

Enet4 commented 10 months ago

Thank you for your interest in DICOM-rs!

  1. Build an InMemDicomObject with nested sequences in code?

With the attribute operations API, this is possible by creating an empty sequence first:

// create Scheduled Procedure Step Sequence if it does not exist yet
obj.apply(AttributeOp::new(
     tags::SCHEDULED_PROCEDURE_STEP_SEQUENCE,
     AttributeAction::SetIfMissing(PrimitiveValue::Empty),
))?;

// set Scheduled Procedure Step Sequence -> Modality
obj.apply(AttributeOp::new(
     (tags::SCHEDULED_PROCEDURE_STEP_SEQUENCE, tags::MODALITY),
     AttributeAction::Set(PrimitiveValue::from("CT")),
))?;

The ergonomics of this could be improved in the future, so that the first step is no longer necessary.

The alternative without AttributeOp is to construct the data set sequence manually and then insert it onto the DICOM object

obj.put(DataElement::new(
    tags::SHEDULED_PROCEDURE_STEP_SEQUENCE,
    VR::SQ,
    DataSetSequence::from(vec![
        InMemDicomObject::from_element_iter([
            DataElement::new(tags::MODALITY, VR::CS, "CT"),
        ])
    ]),
));
  1. Iterate over all data elements in the InMemDicomObject (including sequences) whilst keeping track of the path of the data elements? Can I somehow get access to an AttributeSelector and its list of steps when iterating?

There is only an implementation for iteration over the root data set elements, which does not provide the corresponding attribute selectors, nor does it traverse recursively into nested data sets. I agree that it would be interesting to experiment with this. I will use this issue to track this capability. Would you be interested in working on this, with a bit of mentoring?

kaspermarstal commented 9 months ago

Sure. I'll let you know when I have the time (need to finish this first).

naterichman commented 7 months ago

Not sure if this helps, but I had a need to do that recently and this is what I came up with:

fn get_all_entries(
    file: &DefaultDicomObject    
) -> Result<Vec<AttributeSelector>, ReadError> {

    let mut all_paths = Vec::<Vec<AttributeSelectorStep>>::new();
    file.iter().for_each(|element|
        process_entry(&element, Vec::new(), &mut all_paths, &mut leafs)
    );
    let all_paths = all_paths
        .into_iter()
        .map(|e| AttributeSelector::new(e))
        .collect::<Option<Vec<_>>>()
        .ok_or(ReadError::DicomError(DicomReadError::Other("Could not create attribute selector".to_string())))?;
    Ok(all_paths)
}

fn process_entry(
    de: &InMemElement,
    mut path: Vec<AttributeSelectorStep>,
    all_paths: &mut Vec<Vec<AttributeSelectorStep>>,
) {
    let tag = de.header().tag;

    match de.value() {
        DicomValue::Primitive(_v) => {
            // Push the full path
            path.push(AttributeSelectorStep::Tag(tag));
            all_paths.push(path);
        },
        DicomValue::Sequence(v) => {
            let mut path = path.clone();
            for (num, ds) in v.items().iter().enumerate() {
                path.push(AttributeSelectorStep::Nested{
                    tag: tag,
                    item: num as u32
                });
                for el in ds.iter() {
                    process_entry(el, path.clone(), all_paths);
                }
            }
        },
        DicomValue::PixelSequence(_v) => return
    }
}
kaspermarstal commented 5 months ago

Thanks for taking your time to post this @naterichman!