wsi-dicom-annotations 0.2.1

DICOM WSI ANN, SEG, SR, and Parametric Map interoperability
Documentation
use crate::{Error, Result};

use super::source::{NormalizedRaster, PixelTile, RasterDescriptor};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum PixelPrecision {
    Float32,
    Float64,
}

impl PixelPrecision {
    pub(super) const fn bytes_per_sample(self) -> u64 {
        match self {
            Self::Float32 => 4,
            Self::Float64 => 8,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct FrameKey {
    pub(super) channel: usize,
    pub(super) channel_ordinal: u32,
    pub(super) tile_row: u32,
    pub(super) tile_column: u32,
}

impl FrameKey {
    pub(super) fn column_position(self, descriptor: RasterDescriptor) -> Result<u32> {
        matrix_position(self.tile_column, descriptor.tile_width, "column")
    }

    pub(super) fn row_position(self, descriptor: RasterDescriptor) -> Result<u32> {
        matrix_position(self.tile_row, descriptor.tile_height, "row")
    }
}

fn matrix_position(tile_index: u32, tile_size: u32, axis: &str) -> Result<u32> {
    tile_index
        .checked_mul(tile_size)
        .and_then(|value| value.checked_add(1))
        .ok_or_else(|| Error::InvalidInput(format!("raster {axis} position overflows UL")))
}

pub(super) struct EncodedFrame {
    pub(super) key: FrameKey,
    pub(super) bytes: Vec<u8>,
    pub(super) all_missing: bool,
    pub(super) finite_minimum: Option<f64>,
    pub(super) finite_maximum: Option<f64>,
    pub(super) missing_sample_count: u64,
    pub(super) padded_sample_count: u64,
}

pub(super) fn encode_frame(
    raster: &NormalizedRaster,
    descriptor: RasterDescriptor,
    key: FrameKey,
) -> Result<EncodedFrame> {
    let origin_y = key
        .tile_row
        .checked_mul(descriptor.tile_height)
        .ok_or_else(|| Error::InvalidInput("raster tile row origin overflows UL".into()))?;
    let origin_x = key
        .tile_column
        .checked_mul(descriptor.tile_width)
        .ok_or_else(|| Error::InvalidInput("raster tile column origin overflows UL".into()))?;
    let height = descriptor.tile_height.min(descriptor.height - origin_y);
    let width = descriptor.tile_width.min(descriptor.width - origin_x);
    let tile = raster.read_tile(key.channel, origin_y, origin_x, height, width)?;
    match tile {
        PixelTile::F32(values) => encode_f32(values, descriptor, key, height, width),
        PixelTile::F64(values) => encode_f64(values, descriptor, key, height, width),
    }
}

fn encode_f32(
    values: Vec<f32>,
    descriptor: RasterDescriptor,
    key: FrameKey,
    height: u32,
    width: u32,
) -> Result<EncodedFrame> {
    validate_sample_count(values.len(), height, width)?;
    let padded_sample_count = padded_sample_count(descriptor, values.len())?;
    let mut bytes = Vec::with_capacity(frame_byte_length(descriptor, PixelPrecision::Float32)?);
    let mut range = FiniteRange::default();
    let width = width as usize;
    for row in values.chunks_exact(width) {
        for value in row {
            range.observe(f64::from(*value));
            bytes.extend_from_slice(&value.to_le_bytes());
        }
        for _ in width as u32..descriptor.tile_width {
            bytes.extend_from_slice(&super::source::CANONICAL_NAN_F32.to_le_bytes());
        }
    }
    for _ in height..descriptor.tile_height {
        for _ in 0..descriptor.tile_width {
            bytes.extend_from_slice(&super::source::CANONICAL_NAN_F32.to_le_bytes());
        }
    }
    Ok(range.finish(key, bytes, padded_sample_count))
}

fn encode_f64(
    values: Vec<f64>,
    descriptor: RasterDescriptor,
    key: FrameKey,
    height: u32,
    width: u32,
) -> Result<EncodedFrame> {
    validate_sample_count(values.len(), height, width)?;
    let padded_sample_count = padded_sample_count(descriptor, values.len())?;
    let mut bytes = Vec::with_capacity(frame_byte_length(descriptor, PixelPrecision::Float64)?);
    let mut range = FiniteRange::default();
    let width = width as usize;
    for row in values.chunks_exact(width) {
        for value in row {
            range.observe(*value);
            bytes.extend_from_slice(&value.to_le_bytes());
        }
        for _ in width as u32..descriptor.tile_width {
            bytes.extend_from_slice(&super::source::CANONICAL_NAN_F64.to_le_bytes());
        }
    }
    for _ in height..descriptor.tile_height {
        for _ in 0..descriptor.tile_width {
            bytes.extend_from_slice(&super::source::CANONICAL_NAN_F64.to_le_bytes());
        }
    }
    Ok(range.finish(key, bytes, padded_sample_count))
}

fn padded_sample_count(descriptor: RasterDescriptor, actual: usize) -> Result<u64> {
    let actual = u64::try_from(actual)
        .map_err(|_| Error::InvalidInput("PM frame sample count does not fit u64".into()))?;
    u64::from(descriptor.tile_height)
        .checked_mul(u64::from(descriptor.tile_width))
        .and_then(|total| total.checked_sub(actual))
        .ok_or_else(|| Error::InvalidInput("PM frame padding count is invalid".into()))
}

fn validate_sample_count(actual: usize, height: u32, width: u32) -> Result<()> {
    let expected = usize::try_from(u64::from(height) * u64::from(width)).map_err(|_| {
        Error::InvalidInput("raster tile sample count exceeds addressable memory".into())
    })?;
    if actual != expected {
        return Err(Error::InvalidInput(format!(
            "raster adapter returned {actual} samples for a {height}x{width} region"
        )));
    }
    Ok(())
}

pub(super) fn frame_byte_length(
    descriptor: RasterDescriptor,
    precision: PixelPrecision,
) -> Result<usize> {
    usize::try_from(
        u64::from(descriptor.tile_height)
            .checked_mul(u64::from(descriptor.tile_width))
            .and_then(|samples| samples.checked_mul(precision.bytes_per_sample()))
            .ok_or_else(|| Error::InvalidInput("PM frame byte length overflows".into()))?,
    )
    .map_err(|_| Error::InvalidInput("PM frame exceeds addressable memory".into()))
}

#[derive(Default)]
struct FiniteRange {
    minimum: Option<f64>,
    maximum: Option<f64>,
    missing: u64,
}

impl FiniteRange {
    fn observe(&mut self, value: f64) {
        if value.is_finite() {
            self.minimum = Some(self.minimum.map_or(value, |minimum| minimum.min(value)));
            self.maximum = Some(self.maximum.map_or(value, |maximum| maximum.max(value)));
        } else {
            self.missing += 1;
        }
    }

    fn finish(self, key: FrameKey, bytes: Vec<u8>, padded_sample_count: u64) -> EncodedFrame {
        EncodedFrame {
            key,
            bytes,
            all_missing: self.minimum.is_none(),
            finite_minimum: self.minimum,
            finite_maximum: self.maximum,
            missing_sample_count: self.missing,
            padded_sample_count,
        }
    }
}