Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infer Media Storage SOP Class UID and Media Storage SOP Instance UID from the SOP Class UID and SOP Instance UID when missing #541

Merged
merged 2 commits into from
Jul 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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