dicom-rs icon indicating copy to clipboard operation
dicom-rs copied to clipboard

Iterate over all tags with an AttributeSelector

Open kaspermarstal opened this issue 2 years ago • 4 comments

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.

kaspermarstal avatar Sep 12 '23 18:09 kaspermarstal

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?

Enet4 avatar Sep 13 '23 15:09 Enet4

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

kaspermarstal avatar Sep 14 '23 13:09 kaspermarstal

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
    }
}

naterichman avatar Nov 16 '23 14:11 naterichman

Thanks for taking your time to post this @naterichman!

kaspermarstal avatar Jan 29 '24 23:01 kaspermarstal