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

Add support for encapsulated uncompressed #423

Merged
merged 7 commits into from
Oct 30, 2023
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
5 changes: 3 additions & 2 deletions encoding/src/transfer_syntax/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,9 +292,10 @@ macro_rules! submit_ele_transfer_syntax {
pub enum Codec<D, R, W> {
/// 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.
///
Expand Down
27 changes: 24 additions & 3 deletions pixeldata/src/transcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,7 @@ where
}

// update transfer syntax
self.meta_mut()
.set_transfer_syntax(&ts);
self.meta_mut().set_transfer_syntax(ts);

Ok(())
},
Expand Down Expand Up @@ -277,7 +276,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;

Expand Down Expand Up @@ -486,4 +485,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);
}

}
2 changes: 2 additions & 0 deletions transfer-syntax-registry/src/adapters/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"))]
Expand Down
109 changes: 109 additions & 0 deletions transfer-syntax-registry/src/adapters/uncompressed.rs
Original file line number Diff line number Diff line change
@@ -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<u8>) -> 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<u8>,
) -> 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<u8>,
) -> EncodeResult<Vec<AttributeOp>> {
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)),
),
])
}
}
15 changes: 13 additions & 2 deletions transfer-syntax-registry/src/entries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@
//! 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};

#[cfg(any(feature = "jpeg", feature = "rle"))]
use dicom_encoding::transfer_syntax::{NeverAdapter, TransferSyntax};

#[cfg(feature = "rle")]
use dicom_encoding::NeverPixelAdapter;

Expand Down Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion transfer-syntax-registry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
Loading