Skip to content

Commit

Permalink
Merge pull request #541 from chrisbll971/master
Browse files Browse the repository at this point in the history
Infer Media Storage SOP Class UID and Media Storage SOP Instance UID from the SOP Class UID and SOP Instance UID when missing
  • Loading branch information
Enet4 authored Jul 21, 2024
2 parents 90ce1c6 + 3134d65 commit cf484db
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 22 deletions.
6 changes: 6 additions & 0 deletions object/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,12 @@ pub enum ReadError {
#[snafu(backtrace)]
source: crate::meta::Error,
},
#[snafu(display("Could not parse sop attribute"))]
ParseSopAttribute {
#[snafu(source(from(dicom_core::value::ConvertValueError, Box::from)))]
source: Box<dicom_core::value::ConvertValueError>,
backtrace: Backtrace,
},
#[snafu(display("Could not create data set parser"))]
CreateParser {
#[snafu(backtrace)]
Expand Down
94 changes: 81 additions & 13 deletions object/src/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ use crate::{
CreatePrinterSnafu, DicomObject, ElementNotFoundSnafu, FileDicomObject, InvalidGroupSnafu,
MissingElementValueSnafu, MissingLeafElementSnafu, NoSpaceSnafu, NoSuchAttributeNameSnafu,
NoSuchDataElementAliasSnafu, NoSuchDataElementTagSnafu, NotASequenceSnafu, OpenFileSnafu,
ParseMetaDataSetSnafu, PrematureEndSnafu, PrepareMetaTableSnafu, PrintDataSetSnafu,
PrivateCreatorNotFoundSnafu, PrivateElementError, ReadError, ReadFileSnafu,
ParseMetaDataSetSnafu, ParseSopAttributeSnafu, PrematureEndSnafu, PrepareMetaTableSnafu,
PrintDataSetSnafu, PrivateCreatorNotFoundSnafu, PrivateElementError, ReadError, ReadFileSnafu,
ReadPreambleBytesSnafu, ReadTokenSnafu, ReadUnsupportedTransferSyntaxSnafu,
UnexpectedTokenSnafu, WithMetaError, WriteError,
};
Expand Down Expand Up @@ -365,22 +365,42 @@ where
}

// read metadata header
let meta = FileMetaTable::from_reader(&mut file).context(ParseMetaDataSetSnafu)?;
let mut meta = FileMetaTable::from_reader(&mut file).context(ParseMetaDataSetSnafu)?;

// read rest of data according to metadata, feed it to object
if let Some(ts) = ts_index.get(&meta.transfer_syntax) {
let mut dataset = DataSetReader::new_with_ts(file, ts).context(CreateParserSnafu)?;
let obj = InMemDicomObject::build_object(
&mut dataset,
dict,
false,
Length::UNDEFINED,
read_until,
)?;

Ok(FileDicomObject {
meta,
obj: InMemDicomObject::build_object(
&mut dataset,
dict,
false,
Length::UNDEFINED,
read_until,
)?,
})
// if Media Storage SOP Class UID is empty attempt to infer from SOP Class UID
if meta.media_storage_sop_class_uid().is_empty() {
if let Some(elem) = obj.get(tags::SOP_CLASS_UID) {
meta.media_storage_sop_class_uid = elem
.value()
.to_str()
.context(ParseSopAttributeSnafu)?
.to_string();
}
}

// if Media Storage SOP Instance UID is empty attempt to infer from SOP Instance UID
if meta.media_storage_sop_instance_uid().is_empty() {
if let Some(elem) = obj.get(tags::SOP_INSTANCE_UID) {
meta.media_storage_sop_instance_uid = elem
.value()
.to_str()
.context(ParseSopAttributeSnafu)?
.to_string();
}
}

Ok(FileDicomObject { meta, obj })
} else {
ReadUnsupportedTransferSyntaxSnafu {
uid: meta.transfer_syntax,
Expand Down Expand Up @@ -2447,6 +2467,54 @@ mod tests {
assert_eq!(elem1, &another_patient_name);
}

#[test]
fn infer_media_sop_from_dataset_sop_elements() {
let sop_instance_uid = "1.4.645.313131";
let sop_class_uid = "1.2.840.10008.5.1.4.1.1.2";
let mut obj = InMemDicomObject::new_empty();

obj.put(DataElement::new(
Tag(0x0008, 0x0018),
VR::UI,
dicom_value!(Strs, [sop_instance_uid]),
));
obj.put(DataElement::new(
Tag(0x0008, 0x0016),
VR::UI,
dicom_value!(Strs, [sop_class_uid]),
));

let file_object = obj.with_exact_meta(
FileMetaTableBuilder::default()
.transfer_syntax("1.2.840.10008.1.2.1")
// Media Storage SOP Class and Instance UIDs are missing and set to an empty string
.media_storage_sop_class_uid("")
.media_storage_sop_instance_uid("")
.build()
.unwrap(),
);

// create temporary file path and write object to that file
let dir = tempfile::tempdir().unwrap();
let mut file_path = dir.into_path();
file_path.push(format!("{}.dcm", sop_instance_uid));

file_object.write_to_file(&file_path).unwrap();

// read the file back to validate the outcome
let saved_object = open_file(file_path).unwrap();

// verify that the empty string media storage sop instance and class UIDs have been inferred from the sop instance and class UID
assert_eq!(
saved_object.meta().media_storage_sop_instance_uid(),
sop_instance_uid
);
assert_eq!(
saved_object.meta().media_storage_sop_class_uid(),
sop_class_uid
);
}

#[test]
fn inmem_object_get_opt() {
let another_patient_name = DataElement::new(
Expand Down
19 changes: 10 additions & 9 deletions object/src/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -972,16 +972,17 @@ impl FileMetaTableBuilder {
// Missing information version, will assume (00H, 01H). See #28
[0, 1],
);
let media_storage_sop_class_uid =
self.media_storage_sop_class_uid
.context(MissingElementSnafu {
alias: "MediaStorageSOPClassUID",
})?;
let media_storage_sop_class_uid = self.media_storage_sop_class_uid.unwrap_or_else(|| {
tracing::warn!("MediaStorageSOPClassUID is missing. Defaulting to empty string.");
String::default()
});
let media_storage_sop_instance_uid =
self.media_storage_sop_instance_uid
.context(MissingElementSnafu {
alias: "MediaStorageSOPInstanceUID",
})?;
self.media_storage_sop_instance_uid.unwrap_or_else(|| {
tracing::warn!(
"MediaStorageSOPInstanceUID is missing. Defaulting to empty string."
);
String::default()
});
let transfer_syntax = self.transfer_syntax.context(MissingElementSnafu {
alias: "TransferSyntax",
})?;
Expand Down

0 comments on commit cf484db

Please sign in to comment.