From 16f95a2c0981adf2e30a2b8e921aacc0ef5c6f27 Mon Sep 17 00:00:00 2001 From: Eduardo Pinho Date: Tue, 10 Oct 2023 08:28:29 +0100 Subject: [PATCH 1/7] Add suppport for encapsulated uncompressed --- encoding/src/transfer_syntax/mod.rs | 5 +- transfer-syntax-registry/src/adapters/mod.rs | 2 + .../src/adapters/uncompressed.rs | 109 ++++++++++++++++++ transfer-syntax-registry/src/entries.rs | 13 ++- transfer-syntax-registry/src/lib.rs | 4 +- 5 files changed, 129 insertions(+), 4 deletions(-) create mode 100644 transfer-syntax-registry/src/adapters/uncompressed.rs diff --git a/encoding/src/transfer_syntax/mod.rs b/encoding/src/transfer_syntax/mod.rs index 39bfe16ac..6932e74d7 100644 --- a/encoding/src/transfer_syntax/mod.rs +++ b/encoding/src/transfer_syntax/mod.rs @@ -292,9 +292,10 @@ macro_rules! submit_ele_transfer_syntax { pub enum Codec { /// No codec is required for this transfer syntax. /// - /// Pixel data, if any, should be in its native, unencapsulated form. + /// Pixel data, if any, should be in its _native_, unencapsulated format. None, - /// Pixel data for this transfer syntax is encapsulated. + /// Pixel data for this transfer syntax is encapsulated + /// and likely subjected to a specific encoding process. /// The first part of the tuple struct contains the pixel data decoder, /// whereas the second item is for the pixel data encoder. /// diff --git a/transfer-syntax-registry/src/adapters/mod.rs b/transfer-syntax-registry/src/adapters/mod.rs index 3173a8677..bd7980870 100644 --- a/transfer-syntax-registry/src/adapters/mod.rs +++ b/transfer-syntax-registry/src/adapters/mod.rs @@ -16,6 +16,8 @@ pub mod jpeg; #[cfg(feature = "rle")] pub mod rle_lossless; +pub mod uncompressed; + /// **Note:** This module is a stub. /// Enable the `jpeg` feature to use this module. #[cfg(not(feature = "jpeg"))] diff --git a/transfer-syntax-registry/src/adapters/uncompressed.rs b/transfer-syntax-registry/src/adapters/uncompressed.rs new file mode 100644 index 000000000..337f5835f --- /dev/null +++ b/transfer-syntax-registry/src/adapters/uncompressed.rs @@ -0,0 +1,109 @@ +//! Support for encapsulated uncompressed via pixel data adapter. + +use dicom_core::{ + ops::{AttributeAction, AttributeOp}, + PrimitiveValue, Tag, +}; +use dicom_encoding::{ + adapters::{ + decode_error, encode_error, DecodeResult, EncodeOptions, EncodeResult, PixelDataObject, + PixelDataReader, PixelDataWriter, + }, + snafu::OptionExt, +}; + +/// Adapter for [Encapsulated Uncompressed Explicit VR Little Endian][1] +/// [1]: https://dicom.nema.org/medical/dicom/2023c/output/chtml/part05/sect_A.4.11.html +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct UncompressedAdapter; + +impl PixelDataReader for UncompressedAdapter { + fn decode(&self, src: &dyn PixelDataObject, dst: &mut Vec) -> DecodeResult<()> { + // just flatten all fragments into the output vector + let pixeldata = src + .raw_pixel_data() + .context(decode_error::MissingAttributeSnafu { name: "Pixel Data" })?; + + for fragment in pixeldata.fragments { + dst.extend_from_slice(&fragment); + } + + Ok(()) + } + + fn decode_frame( + &self, + src: &dyn PixelDataObject, + frame: u32, + dst: &mut Vec, + ) -> DecodeResult<()> { + // just copy the specific fragment into the output vector + let pixeldata = src + .raw_pixel_data() + .context(decode_error::MissingAttributeSnafu { name: "Pixel Data" })?; + + let fragment = pixeldata + .fragments + .get(frame as usize) + .context(decode_error::FrameRangeOutOfBoundsSnafu)?; + + dst.extend_from_slice(&fragment); + + Ok(()) + } +} + +impl PixelDataWriter for UncompressedAdapter { + fn encode_frame( + &self, + src: &dyn PixelDataObject, + frame: u32, + _options: EncodeOptions, + dst: &mut Vec, + ) -> EncodeResult> { + let cols = src + .cols() + .context(encode_error::MissingAttributeSnafu { name: "Columns" })?; + let rows = src + .rows() + .context(encode_error::MissingAttributeSnafu { name: "Rows" })?; + let samples_per_pixel = + src.samples_per_pixel() + .context(encode_error::MissingAttributeSnafu { + name: "SamplesPerPixel", + })?; + let bits_allocated = src + .bits_allocated() + .context(encode_error::MissingAttributeSnafu { + name: "BitsAllocated", + })?; + + let bytes_per_sample = (bits_allocated / 8) as usize; + let frame_size = + cols as usize * rows as usize * samples_per_pixel as usize * bytes_per_sample; + + // identify frame data using the frame index + let pixeldata_uncompressed = &src + .raw_pixel_data() + .context(encode_error::MissingAttributeSnafu { name: "Pixel Data" })? + .fragments[0]; + + let len_before = pixeldata_uncompressed.len(); + + let frame_data = pixeldata_uncompressed + .get(frame_size * frame as usize..frame_size * (frame as usize + 1)) + .whatever_context("Frame index out of bounds")?; + + // Copy the the data to the output + dst.extend_from_slice(&frame_data); + + // provide attribute changes + Ok(vec![ + // Encapsulated Pixel Data Value Total Length + AttributeOp::new( + Tag(0x7FE0, 0x0003), + AttributeAction::Set(PrimitiveValue::from(len_before as u64)), + ), + ]) + } +} diff --git a/transfer-syntax-registry/src/entries.rs b/transfer-syntax-registry/src/entries.rs index 67cd529b8..b635ada8e 100644 --- a/transfer-syntax-registry/src/entries.rs +++ b/transfer-syntax-registry/src/entries.rs @@ -21,7 +21,7 @@ //! hence expanding support for those transfer syntaxes //! to the registry. -use crate::create_ts_stub; +use crate::{adapters::uncompressed::UncompressedAdapter, create_ts_stub}; use byteordered::Endianness; use dicom_encoding::transfer_syntax::{AdapterFreeTransferSyntax as Ts, Codec}; @@ -62,6 +62,17 @@ pub const EXPLICIT_VR_BIG_ENDIAN: Ts = Ts::new( Codec::None, ); +/// **Fully implemented:** Encapsulated Uncompressed Explicit VR Little Endian +pub const ENCAPSULATED_UNCOMPRESSED_EXPLICIT_VR_LITTLE_ENDIAN: TransferSyntax< + NeverAdapter, + UncompressedAdapter, + UncompressedAdapter, +> = TransferSyntax::new_ele( + "1.2.840.10008.1.2.1.98", + "Encapsulated Uncompressed Explicit VR Little Endian", + Codec::EncapsulatedPixelData(Some(UncompressedAdapter), Some(UncompressedAdapter)), +); + // -- transfer syntaxes with pixel data adapters, fully supported -- /// **Implemented:** RLE Lossless diff --git a/transfer-syntax-registry/src/lib.rs b/transfer-syntax-registry/src/lib.rs index a17fb2821..2e6003e68 100644 --- a/transfer-syntax-registry/src/lib.rs +++ b/transfer-syntax-registry/src/lib.rs @@ -200,11 +200,13 @@ lazy_static! { }; use self::entries::*; - let built_in_ts: [TransferSyntax; 36] = [ + let built_in_ts: [TransferSyntax; 37] = [ IMPLICIT_VR_LITTLE_ENDIAN.erased(), EXPLICIT_VR_LITTLE_ENDIAN.erased(), EXPLICIT_VR_BIG_ENDIAN.erased(), + ENCAPSULATED_UNCOMPRESSED_EXPLICIT_VR_LITTLE_ENDIAN.erased(), + DEFLATED_EXPLICIT_VR_LITTLE_ENDIAN.erased(), JPIP_REFERENCED_DEFLATE.erased(), JPEG_BASELINE.erased(), From 467dbe6d89c8d8e90eb2103c37c6b4c6dc611575 Mon Sep 17 00:00:00 2001 From: Eduardo Pinho Date: Wed, 11 Oct 2023 21:39:35 +0100 Subject: [PATCH 2/7] [ts-registry] Fix import for no-default-features --- transfer-syntax-registry/src/entries.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/transfer-syntax-registry/src/entries.rs b/transfer-syntax-registry/src/entries.rs index b635ada8e..eb288da81 100644 --- a/transfer-syntax-registry/src/entries.rs +++ b/transfer-syntax-registry/src/entries.rs @@ -25,8 +25,10 @@ use crate::{adapters::uncompressed::UncompressedAdapter, create_ts_stub}; use byteordered::Endianness; use dicom_encoding::transfer_syntax::{AdapterFreeTransferSyntax as Ts, Codec}; +use dicom_encoding::TransferSyntax; + #[cfg(any(feature = "jpeg", feature = "rle"))] -use dicom_encoding::transfer_syntax::{NeverAdapter, TransferSyntax}; +use dicom_encoding::transfer_syntax::NeverAdapter; #[cfg(feature = "rle")] use dicom_encoding::NeverPixelAdapter; From 19c93ed263401ad35107320fdfd7018ff0ce16af Mon Sep 17 00:00:00 2001 From: Eduardo Pinho Date: Sat, 14 Oct 2023 14:25:31 +0100 Subject: [PATCH 3/7] Fix imports --- transfer-syntax-registry/src/entries.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/transfer-syntax-registry/src/entries.rs b/transfer-syntax-registry/src/entries.rs index eb288da81..d1001066a 100644 --- a/transfer-syntax-registry/src/entries.rs +++ b/transfer-syntax-registry/src/entries.rs @@ -25,10 +25,8 @@ use crate::{adapters::uncompressed::UncompressedAdapter, create_ts_stub}; use byteordered::Endianness; use dicom_encoding::transfer_syntax::{AdapterFreeTransferSyntax as Ts, Codec}; -use dicom_encoding::TransferSyntax; +use dicom_encoding::transfer_syntax::{NeverAdapter, TransferSyntax}; -#[cfg(any(feature = "jpeg", feature = "rle"))] -use dicom_encoding::transfer_syntax::NeverAdapter; #[cfg(feature = "rle")] use dicom_encoding::NeverPixelAdapter; From 3b7f1f5534bf7ec27f1a42dee9ec7987fdaab980 Mon Sep 17 00:00:00 2001 From: Eduardo Pinho Date: Sat, 14 Oct 2023 14:26:08 +0100 Subject: [PATCH 4/7] [ts-registry] Lint fix - needless borrow --- transfer-syntax-registry/src/adapters/uncompressed.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transfer-syntax-registry/src/adapters/uncompressed.rs b/transfer-syntax-registry/src/adapters/uncompressed.rs index 337f5835f..a8e514d29 100644 --- a/transfer-syntax-registry/src/adapters/uncompressed.rs +++ b/transfer-syntax-registry/src/adapters/uncompressed.rs @@ -47,7 +47,7 @@ impl PixelDataReader for UncompressedAdapter { .get(frame as usize) .context(decode_error::FrameRangeOutOfBoundsSnafu)?; - dst.extend_from_slice(&fragment); + dst.extend_from_slice(fragment); Ok(()) } From 16e139b9b44def789c34e1781db8ada25467bd84 Mon Sep 17 00:00:00 2001 From: Eduardo Pinho Date: Sun, 29 Oct 2023 17:22:48 +0000 Subject: [PATCH 5/7] [pixeldata] add test for transcoding to encapsulated uncompressed --- pixeldata/src/transcode.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/pixeldata/src/transcode.rs b/pixeldata/src/transcode.rs index 50a33aab1..d388efbfc 100644 --- a/pixeldata/src/transcode.rs +++ b/pixeldata/src/transcode.rs @@ -277,7 +277,7 @@ mod tests { use dicom_dictionary_std::uids; use dicom_object::open_file; use dicom_test_files; - use dicom_transfer_syntax_registry::entries::JPEG_EXTENDED; + use dicom_transfer_syntax_registry::entries::{JPEG_EXTENDED, ENCAPSULATED_UNCOMPRESSED_EXPLICIT_VR_LITTLE_ENDIAN}; #[cfg(feature = "native")] use dicom_transfer_syntax_registry::entries::JPEG_BASELINE; @@ -486,4 +486,26 @@ mod tests { assert_eq!(fragments.len(), 1); } } + + /// converting to Encapsulated Uncompressed Explicit VR Little Endian + /// should split each frame into separate fragments in native form + #[test] + fn test_transcode_encapsulated_uncompressed() { + let test_file = dicom_test_files::path("pydicom/SC_rgb_2frame.dcm").unwrap(); + let mut obj = open_file(test_file).unwrap(); + + // transcode to the same TS + obj.transcode(&ENCAPSULATED_UNCOMPRESSED_EXPLICIT_VR_LITTLE_ENDIAN.erased()) + .expect("Should have transcoded successfully"); + + assert_eq!(obj.meta().transfer_syntax(), ENCAPSULATED_UNCOMPRESSED_EXPLICIT_VR_LITTLE_ENDIAN.uid()); + // pixel data is encapsulated, but in native form + let pixel_data = obj.get(tags::PIXEL_DATA).unwrap(); + let fragments = pixel_data.fragments().unwrap(); + assert_eq!(fragments.len(), 2); + // each frame should have native pixel data (100x100 RGB) + assert_eq!(fragments[0].len(), 100 * 100 * 3); + assert_eq!(fragments[1].len(), 100 * 100 * 3); + } + } From 946008c5fd0ca2c7d9b5eab386afa465f61b8fe1 Mon Sep 17 00:00:00 2001 From: Eduardo Pinho Date: Sun, 29 Oct 2023 17:29:16 +0000 Subject: [PATCH 6/7] [ts-registry] Clippy lint fix - needless borrow --- transfer-syntax-registry/src/adapters/uncompressed.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transfer-syntax-registry/src/adapters/uncompressed.rs b/transfer-syntax-registry/src/adapters/uncompressed.rs index a8e514d29..f70b5f90f 100644 --- a/transfer-syntax-registry/src/adapters/uncompressed.rs +++ b/transfer-syntax-registry/src/adapters/uncompressed.rs @@ -95,7 +95,7 @@ impl PixelDataWriter for UncompressedAdapter { .whatever_context("Frame index out of bounds")?; // Copy the the data to the output - dst.extend_from_slice(&frame_data); + dst.extend_from_slice(frame_data); // provide attribute changes Ok(vec![ From 9477818ba5d6b7324d579e4324e9cb766127d609 Mon Sep 17 00:00:00 2001 From: Eduardo Pinho Date: Sun, 29 Oct 2023 17:29:28 +0000 Subject: [PATCH 7/7] [pixeldata] Clippy lint fix in transcode module - needless borrow --- pixeldata/src/transcode.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pixeldata/src/transcode.rs b/pixeldata/src/transcode.rs index d388efbfc..0295dd72f 100644 --- a/pixeldata/src/transcode.rs +++ b/pixeldata/src/transcode.rs @@ -168,8 +168,7 @@ where } // update transfer syntax - self.meta_mut() - .set_transfer_syntax(&ts); + self.meta_mut().set_transfer_syntax(ts); Ok(()) },