Skip to content

Commit

Permalink
[ts-registry] adapt JPEG module to support the latest adapter API
Browse files Browse the repository at this point in the history
  • Loading branch information
Enet4 committed Jul 8, 2023
1 parent d61402e commit 8b4954b
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 26 deletions.
84 changes: 62 additions & 22 deletions transfer-syntax-registry/src/adapters/jpeg.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
//! Support for JPG image decoding.
use dicom_core::ops::{AttributeAction, AttributeOp};
use dicom_core::Tag;
use dicom_encoding::adapters::{
decode_error, DecodeResult, EncodeOptions, EncodeResult, PixelDataObject, PixelDataReader,
PixelDataWriter,
decode_error, encode_error, DecodeResult, EncodeOptions, EncodeResult, PixelDataObject,
PixelDataReader, PixelDataWriter,
};
use dicom_encoding::snafu::prelude::*;
use jpeg_decoder::Decoder;
Expand Down Expand Up @@ -112,9 +114,10 @@ impl PixelDataReader for JpegAdapter {
name: "BitsAllocated",
})?;

if bits_allocated != 8 && bits_allocated != 16 {
whatever!("BitsAllocated other than 8 or 16 is not supported");
}
ensure_whatever!(
bits_allocated == 8 || bits_allocated == 16,
"BitsAllocated other than 8 or 16 is not supported"
);

let nr_frames = src.number_of_frames().unwrap_or(1) as usize;

Expand Down Expand Up @@ -178,40 +181,77 @@ impl PixelDataWriter for JpegAdapter {
frame: u32,
options: EncodeOptions,
dst: &mut Vec<u8>,
) -> EncodeResult<Vec<dicom_core::ops::AttributeOp>> {
let cols = src.cols().unwrap();
let rows = src.rows().unwrap();
let samples_per_pixel = src.samples_per_pixel().unwrap();
let bits_allocated = src.bits_allocated().unwrap();
) -> 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 nr_frames = src.number_of_frames().unwrap_or(1) as usize;
ensure_whatever!(
bits_allocated == 8 || bits_allocated == 16,
"BitsAllocated other than 8 or 16 is not supported"
);

let quality = options.quality.unwrap_or(85);

let frame_size = (
cols * rows * samples_per_pixel * (bits_allocated / 8)
) as usize;
let frame_size = (cols * rows * samples_per_pixel * (bits_allocated / 8)) as usize;

let color_type = match samples_per_pixel {
1 => ColorType::Luma,
3 => ColorType::Rgb,
4 => ColorType::Rgba,
_ => todo!("Implement error handling for wrong samples per pixel")
_ => whatever!("Unsupported samples per pixel: {}", samples_per_pixel),
};

let photometric_interpretation = match samples_per_pixel {
1 => "MONOCHROME2",
3 => "RGB",
_ => unreachable!(),
};

// record dst length before encoding to know full jpeg size
let len_before = dst.len();

// Encode the data
let frame_uncompressed = src.fragment(frame as usize).unwrap();
let encoder = jpeg_encoder::Encoder::new(&mut *dst, quality);
let frame_uncompressed = src
.fragment(frame as usize)
.context(encode_error::FrameRangeOutOfBoundsSnafu)?;
let mut encoder = jpeg_encoder::Encoder::new(&mut *dst, quality);
encoder.set_progressive(false);
encoder
.encode(&frame_uncompressed, cols, rows, color_type)
.unwrap();
.whatever_context("JPEG encoding failed")?;

let compressed_frame_size = dst.len() - len_before;

// TODO
Ok(vec![])

let compression_ratio = frame_size as f64 / compressed_frame_size as f64;
let compression_ratio = format!("{:.6}", compression_ratio);

// provide attribute changes
Ok(vec![
// lossy iamge compression
AttributeOp::new(Tag(0x0028, 0x2110), AttributeAction::SetStr("01".into())),
// lossy image compression ratio
AttributeOp::new(
Tag(0x0028, 0x2112),
AttributeAction::PushStr(compression_ratio.into()),
),
// Photometric interpretation
AttributeOp::new(
Tag(0x0028, 0x0004),
AttributeAction::SetStr(photometric_interpretation.into()),
),
])
}
}
8 changes: 4 additions & 4 deletions transfer-syntax-registry/src/entries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,18 +82,18 @@ pub const RLE_LOSSLESS: Ts = create_ts_stub("1.2.840.10008.1.2.5", "RLE Lossless

// JPEG encoded pixel data

/// An alias for a transfer syntax specifier with `JpegPixelAdapter`
/// (note that only decoding is supported at the moment).
/// An alias for a transfer syntax specifier with [`JpegAdapter`]
/// (supports decoding and encoding).
#[cfg(feature = "jpeg")]
type JpegTs<R = JpegAdapter, W = NeverPixelAdapter> = TransferSyntax<NeverAdapter, R, W>;
type JpegTs<R = JpegAdapter, W = JpegAdapter> = TransferSyntax<NeverAdapter, R, W>;

/// Create a transfer syntax with JPEG encapsulated pixel data
#[cfg(feature = "jpeg")]
const fn create_ts_jpeg(uid: &'static str, name: &'static str) -> JpegTs {
TransferSyntax::new_ele(
uid,
name,
Codec::EncapsulatedPixelData(Some(JpegAdapter), None),
Codec::EncapsulatedPixelData(Some(JpegAdapter), Some(JpegAdapter)),
)
}

Expand Down

0 comments on commit 8b4954b

Please sign in to comment.