Skip to content

Commit

Permalink
ENH: Add reading and writing adapters for deflated TS
Browse files Browse the repository at this point in the history
  • Loading branch information
naterichman authored and Enet4 committed Dec 7, 2024
1 parent 57d8907 commit 4c1cd85
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 51 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

80 changes: 60 additions & 20 deletions object/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ pub use dicom_dictionary_std::StandardDataDictionary;
pub type DefaultDicomObject<D = StandardDataDictionary> = FileDicomObject<mem::InMemDicomObject<D>>;

use dicom_core::header::{GroupNumber, Header};
use dicom_encoding::Codec;
use dicom_encoding::adapters::{PixelDataObject, RawPixelData};
use dicom_encoding::transfer_syntax::TransferSyntaxIndex;
use dicom_parser::dataset::{DataSetWriter, IntoTokens};
Expand Down Expand Up @@ -475,21 +476,34 @@ where
.with_context(|| WriteUnsupportedTransferSyntaxSnafu {
uid: self.meta.transfer_syntax.clone(),
})?;
let mut dset_writer = DataSetWriter::with_ts(to, ts).context(CreatePrinterSnafu)?;
if let Codec::Dataset(Some(adapter))= ts.codec() {
let adapter = adapter.adapt_writer(Box::new(to));
let mut dset_writer = DataSetWriter::with_ts(adapter, ts).context(CreatePrinterSnafu)?;

// We use the default options, because only the inner object knows if something needs to change
dset_writer
.write_sequence((&self.obj).into_tokens())
.context(PrintDataSetSnafu)?;
// write object
dset_writer
.write_sequence((&self.obj).into_tokens())
.context(PrintDataSetSnafu)?;

Ok(())
Ok(())

} else {
let mut dset_writer = DataSetWriter::with_ts(to, ts).context(CreatePrinterSnafu)?;

// write object
dset_writer
.write_sequence((&self.obj).into_tokens())
.context(PrintDataSetSnafu)?;

Ok(())
}
}

/// Write the entire object as a DICOM file
/// into the given writer.
/// Preamble, magic code, and file meta group will be included
/// before the inner object.
pub fn write_all<W: Write>(&self, to: W) -> Result<(), WriteError> {
pub fn write_all<W: Write + 'static>(&self, to: W) -> Result<(), WriteError> {
let mut to = BufWriter::new(to);

// write preamble
Expand All @@ -507,14 +521,27 @@ where
.with_context(|| WriteUnsupportedTransferSyntaxSnafu {
uid: self.meta.transfer_syntax.clone(),
})?;
let mut dset_writer = DataSetWriter::with_ts(to, ts).context(CreatePrinterSnafu)?;
if let Codec::Dataset(Some(adapter))= ts.codec() {
let adapter = adapter.adapt_writer(Box::new(to));
let mut dset_writer = DataSetWriter::with_ts(adapter, ts).context(CreatePrinterSnafu)?;

// write object
dset_writer
.write_sequence((&self.obj).into_tokens())
.context(PrintDataSetSnafu)?;

Ok(())

// We use the default options, because only the inner object knows if something needs to change
dset_writer
.write_sequence((&self.obj).into_tokens())
.context(PrintDataSetSnafu)?;
} else {
let mut dset_writer = DataSetWriter::with_ts(to, ts).context(CreatePrinterSnafu)?;

Ok(())
// write object
dset_writer
.write_sequence((&self.obj).into_tokens())
.context(PrintDataSetSnafu)?;

Ok(())
}
}

/// Write the file meta group set into the given writer.
Expand All @@ -528,7 +555,7 @@ where
/// without preamble, magic code, nor file meta group.
///
/// The transfer syntax is selected from the file meta table.
pub fn write_dataset<W: Write>(&self, to: W) -> Result<(), WriteError> {
pub fn write_dataset<W: Write + 'static>(&self, to: W) -> Result<(), WriteError> {
let to = BufWriter::new(to);

// prepare encoder
Expand All @@ -537,14 +564,27 @@ where
.with_context(|| WriteUnsupportedTransferSyntaxSnafu {
uid: self.meta.transfer_syntax.clone(),
})?;
let mut dset_writer = DataSetWriter::with_ts(to, ts).context(CreatePrinterSnafu)?;
if let Codec::Dataset(Some(adapter))= ts.codec() {
let adapter = adapter.adapt_writer(Box::new(to));
let mut dset_writer = DataSetWriter::with_ts(adapter, ts).context(CreatePrinterSnafu)?;

// write object
dset_writer
.write_sequence((&self.obj).into_tokens())
.context(PrintDataSetSnafu)?;

// write object
dset_writer
.write_sequence((&self.obj).into_tokens())
.context(PrintDataSetSnafu)?;
Ok(())

Ok(())
} else {
let mut dset_writer = DataSetWriter::with_ts(to, ts).context(CreatePrinterSnafu)?;

// write object
dset_writer
.write_sequence((&self.obj).into_tokens())
.context(PrintDataSetSnafu)?;

Ok(())
}
}
}

Expand Down
55 changes: 38 additions & 17 deletions object/src/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ impl InMemDicomObject<StandardDataDictionary> {
cs: SpecificCharacterSet,
) -> Result<Self, ReadError>
where
S: Read,
S: Read + 'static,
{
Self::read_dataset_with_dict_ts_cs(from, StandardDataDictionary, ts, cs)
}
Expand All @@ -252,7 +252,7 @@ impl InMemDicomObject<StandardDataDictionary> {
#[inline]
pub fn read_dataset_with_ts<S>(from: S, ts: &TransferSyntax) -> Result<Self, ReadError>
where
S: Read,
S: Read + 'static,
{
Self::read_dataset_with_dict_ts_cs(
from,
Expand Down Expand Up @@ -662,7 +662,7 @@ where
ts: &TransferSyntax,
) -> Result<Self, ReadError>
where
S: Read,
S: Read + 'static,
D: DataDictionary,
{
Self::read_dataset_with_dict_ts_cs(from, dict, ts, SpecificCharacterSet::default())
Expand All @@ -682,12 +682,19 @@ where
cs: SpecificCharacterSet,
) -> Result<Self, ReadError>
where
S: Read,
S: Read + 'static,
D: DataDictionary,
{
let from = BufReader::new(from);
let mut dataset = DataSetReader::new_with_ts_cs(from, ts, cs).context(CreateParserSnafu)?;
InMemDicomObject::build_object(&mut dataset, dict, false, Length::UNDEFINED, None)
if let Codec::Dataset(Some(adapter)) = ts.codec() {
let adapter = adapter.adapt_reader(Box::new(from));
let mut dataset =
DataSetReader::new_with_ts_cs(adapter, ts, cs).context(CreateParserSnafu)?;
InMemDicomObject::build_object(&mut dataset, dict, false, Length::UNDEFINED, None)
} else {
let mut dataset = DataSetReader::new_with_ts_cs(from, ts, cs).context(CreateParserSnafu)?;
InMemDicomObject::build_object(&mut dataset, dict, false, Length::UNDEFINED, None)
}
}

// Standard methods follow. They are not placed as a trait implementation
Expand Down Expand Up @@ -1768,7 +1775,9 @@ where
/// in which then that character set will be used.
///
/// Note: [`write_dataset_with_ts`] and [`write_dataset_with_ts_cs`]
/// may be easier to use.
/// may be easier to use and _will_ apply a dataset adapter (such as
/// DeflatedExplicitVRLittleEndian (1.2.840.10008.1.2.99)) whereas this
/// method will _not_
///
/// [`write_dataset_with_ts`]: #method.write_dataset_with_ts
/// [`write_dataset_with_ts_cs`]: #method.write_dataset_with_ts_cs
Expand Down Expand Up @@ -1802,18 +1811,30 @@ where
cs: SpecificCharacterSet,
) -> Result<(), WriteError>
where
W: Write,
W: Write + 'static,
{
// prepare data set writer
let mut dset_writer = DataSetWriter::with_ts_cs(to, ts, cs).context(CreatePrinterSnafu)?;
let required_options = IntoTokensOptions::new(self.charset_changed);
if let Codec::Dataset(Some(adapter)) = ts.codec() {
let adapter = adapter.adapt_writer(Box::new(to));
// prepare data set writer
let mut dset_writer = DataSetWriter::with_ts(adapter, ts).context(CreatePrinterSnafu)?;

// write object
dset_writer
.write_sequence(self.into_tokens_with_options(required_options))
.context(PrintDataSetSnafu)?;
// write object
dset_writer
.write_sequence(self.into_tokens())
.context(PrintDataSetSnafu)?;

Ok(())
Ok(())
} else {
// prepare data set writer
let mut dset_writer = DataSetWriter::with_ts_cs(to, ts, cs).context(CreatePrinterSnafu)?;

// write object
dset_writer
.write_sequence(self.into_tokens())
.context(PrintDataSetSnafu)?;

Ok(())
}
}

/// Write this object's data set into the given writer,
Expand All @@ -1825,7 +1846,7 @@ where
/// after which the text encoder is overridden accordingly.
pub fn write_dataset_with_ts<W>(&self, to: W, ts: &TransferSyntax) -> Result<(), WriteError>
where
W: Write,
W: Write + 'static,
{
self.write_dataset_with_ts_cs(to, ts, SpecificCharacterSet::default())
}
Expand Down
2 changes: 2 additions & 0 deletions transfer-syntax-registry/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,5 @@ features = ["native"]

[dev-dependencies]
dicom-test-files = "0.3"
dicom-object = { path = "../object" }
dicom-pixeldata = { path = "../pixeldata" }
46 changes: 32 additions & 14 deletions transfer-syntax-registry/tests/deflate.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,38 @@

use std::{io::BufReader, fs::File};
use std::{io::BufReader, fs::{metadata, File}};

// use dicom_object::OpenFileOptions;
// use dicom_pixeldata::PixelDecoder;

// #[test]
// fn test_read_data_with_preamble() {
// let path = dicom_test_files::path("pydicom/image_dfl.dcm").expect("test DICOM file should exist");
// let source = BufReader::new(File::open(path).unwrap());
use dicom_object::OpenFileOptions;
use dicom_pixeldata::PixelDecoder;

// // should read preamble even though it's from a reader
// let object = OpenFileOptions::new()
// .from_reader(source)
// .expect("Should read from source successfully");
#[test]
fn test_read_data_deflated() {
let path = dicom_test_files::path("pydicom/image_dfl.dcm").expect("test DICOM file should exist");
let source = BufReader::new(File::open(path).unwrap());

// let res = object.decode_pixel_data();
// println!("{:?}", res);
// should read preamble even though it's from a reader
let object = OpenFileOptions::new()
.from_reader(source)
.expect("Should read from source successfully");

// }
let res = object.decode_pixel_data().unwrap();
assert_eq!((
res.rows() as usize *
res.columns() as usize *
res.number_of_frames() as usize), res.data().len() as usize);
}

#[test]
fn write_deflated(){
let path = dicom_test_files::path("pydicom/image_dfl.dcm").expect("test DICOM file should exist");
let source = BufReader::new(File::open(path.clone()).unwrap());

// should read preamble even though it's from a reader
let object = OpenFileOptions::new()
.from_reader(source)
.expect("Should read from source successfully");

let mut buf = Vec::<u8>::new();
object.write_all(&mut buf).unwrap();

Check failure on line 36 in transfer-syntax-registry/tests/deflate.rs

View workflow job for this annotation

GitHub Actions / Test (default) (1.72.0)

`buf` does not live long enough
assert_eq!(buf.len(), metadata(path).unwrap().len() as usize);

Check failure on line 37 in transfer-syntax-registry/tests/deflate.rs

View workflow job for this annotation

GitHub Actions / Test (default) (1.72.0)

cannot borrow `buf` as immutable because it is also borrowed as mutable
}

0 comments on commit 4c1cd85

Please sign in to comment.