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 committed Dec 3, 2023
1 parent a0c9f47 commit 2908dd3
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 50 deletions.
3 changes: 3 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 @@ -153,6 +153,7 @@ pub use dicom_dictionary_std::StandardDataDictionary;
pub type DefaultDicomObject<D = StandardDataDictionary> = FileDicomObject<mem::InMemDicomObject<D>>;

use dicom_core::header::Header;
use dicom_encoding::Codec;
use dicom_encoding::adapters::{PixelDataObject, RawPixelData};
use dicom_encoding::{text::SpecificCharacterSet, transfer_syntax::TransferSyntaxIndex};
use dicom_parser::dataset::{DataSetWriter, IntoTokens};
Expand Down Expand Up @@ -427,21 +428,34 @@ where
uid: self.meta.transfer_syntax.clone(),
})?;
let cs = SpecificCharacterSet::Default;
let mut dset_writer = DataSetWriter::with_ts_cs(to, ts, cs).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_cs(adapter, ts, cs).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_cs(to, ts, cs).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 @@ -460,14 +474,27 @@ where
uid: self.meta.transfer_syntax.clone(),
})?;
let cs = SpecificCharacterSet::Default;
let mut dset_writer = DataSetWriter::with_ts_cs(to, ts, cs).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_cs(adapter, ts, cs).context(CreatePrinterSnafu)?;

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

Ok(())

// write object
dset_writer
.write_sequence((&self.obj).into_tokens())
.context(PrintDataSetSnafu)?;
} else {
let mut dset_writer = DataSetWriter::with_ts_cs(to, ts, cs).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 @@ -481,7 +508,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 @@ -491,14 +518,27 @@ where
uid: self.meta.transfer_syntax.clone(),
})?;
let cs = SpecificCharacterSet::Default;
let mut dset_writer = DataSetWriter::with_ts_cs(to, ts, cs).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_cs(adapter, ts, cs).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_cs(to, ts, cs).context(CreatePrinterSnafu)?;

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

Ok(())
}
}
}

Expand Down
54 changes: 38 additions & 16 deletions object/src/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,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 @@ -251,7 +251,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 @@ -621,7 +621,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 @@ -641,12 +641,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 @@ -1375,7 +1382,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 @@ -1408,17 +1417,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)?;
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_cs(adapter, ts, cs).context(CreatePrinterSnafu)?;

// write object
dset_writer
.write_sequence(self.into_tokens())
.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 @@ -1430,7 +1452,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 @@ -68,4 +68,6 @@ optional = true
features = ["native"]

[dev-dependencies]
dicom-object = { path = "../object" }
dicom-pixeldata = { path = "../pixeldata" }
dicom-test-files = "0.2.1"
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();
assert_eq!(buf.len(), metadata(path).unwrap().len() as usize);
}

0 comments on commit 2908dd3

Please sign in to comment.