wsi-dicom-annotations 0.2.1

DICOM WSI ANN, SEG, SR, and Parametric Map interoperability
Documentation
use std::hint::black_box;
use std::time::{Duration, Instant};

use crate::test_support::write_source_wsi;

use super::*;

const SEGMENTS: usize = 16;
const FRAMES_PER_SEGMENT: usize = 16;
const TILE: usize = 64;

fn code(value: &str, meaning: &str) -> DicomCode {
    DicomCode::new(value, "99FRAMES", meaning).unwrap()
}

pub(super) fn benchmark_documents() -> (SegmentationDocument, SegmentationDocument) {
    let directory = tempfile::tempdir().unwrap();
    let source_path = directory.path().join("source.dcm");
    write_source_wsi(&source_path, 1024, 1024, TILE as u16, TILE as u16);
    let source = DicomAnnotationContext::from_source(&source_path).unwrap();
    let mut segments = (0..SEGMENTS)
        .map(|index| {
            let offset = (index * 8) as f64;
            SegmentationSegment::new(
                format!("Segment {index}"),
                code("MORPH", "Morphology"),
                code("TUMOR", "Tumor"),
                [1, 2, 3],
                vec![vec![
                    Point2::new(offset, offset),
                    Point2::new(offset + 4.0, offset),
                    Point2::new(offset + 4.0, offset + 4.0),
                    Point2::new(offset, offset + 4.0),
                ]],
                Vec::new(),
            )
            .unwrap()
        })
        .collect::<Vec<_>>();
    for (index, segment) in segments.iter_mut().enumerate() {
        segment.source_segment_number = Some((index as u16) * 3 + 1);
    }
    let binary_frames = segments
        .iter()
        .flat_map(|segment| {
            (0..FRAMES_PER_SEGMENT).map(move |frame_index| {
                let dense = frame_index % 4 == 0;
                let mask = (0..TILE * TILE)
                    .map(|pixel| dense || (pixel + frame_index) % 31 == 0)
                    .collect();
                BinarySegmentationFrame {
                    segment_number: segment.source_segment_number.unwrap(),
                    tile_col: (frame_index % 4) as u32,
                    tile_row: (frame_index / 4) as u32,
                    width: TILE as u16,
                    height: TILE as u16,
                    mask,
                }
            })
        })
        .collect::<Vec<_>>();
    let fractional_frames = binary_frames
        .iter()
        .enumerate()
        .map(|(frame_index, frame)| FractionalSegmentationFrame {
            segment_number: frame.segment_number,
            tile_col: frame.tile_col,
            tile_row: frame.tile_row,
            width: frame.width,
            height: frame.height,
            maximum_fractional_value: 255,
            values: frame
                .mask
                .iter()
                .enumerate()
                .map(|(pixel, value)| {
                    if *value {
                        ((pixel + frame_index) % 255 + 1) as u16
                    } else {
                        0
                    }
                })
                .collect(),
        })
        .collect::<Vec<_>>();
    let mut binary = SegmentationDocument::binary(source, segments).unwrap();
    binary.imported_binary_frames = Some(binary_frames);
    let mut fractional = binary.clone();
    fractional.kind = SegmentationKind::Fractional;
    fractional.imported_binary_frames = None;
    fractional.imported_fractional_frames = Some(fractional_frames);
    (binary, fractional)
}

fn measure(iterations: usize, mut operation: impl FnMut() -> usize) -> (Duration, usize) {
    let start = Instant::now();
    let mut checksum = 0_usize;
    for _ in 0..iterations {
        checksum = checksum.wrapping_add(black_box(operation()));
    }
    (start.elapsed(), checksum)
}

#[test]
#[ignore = "repeatable local performance measurement; not a wall-time CI gate"]
fn seg_normalization_benchmark() {
    let (binary, fractional) = benchmark_documents();
    let (binary_elapsed, binary_checksum) = measure(20, || binary.binary_runs().unwrap().len());
    let (fractional_elapsed, fractional_checksum) =
        measure(20, || fractional.fractional_runs().unwrap().len());
    let (vector_elapsed, vector_checksum) = measure(5, || {
        binary
            .vectorized_annotations(SegToAnnConversionPolicy::AllowLoss)
            .unwrap()
            .groups()
            .iter()
            .map(AnnotationGroup::annotation_count)
            .sum()
    });

    assert!(binary_checksum > 0);
    assert!(fractional_checksum > 0);
    assert!(vector_checksum > 0);
    println!(
        "frames={} segments={} binary_20={binary_elapsed:?} binary_checksum={binary_checksum} fractional_20={fractional_elapsed:?} fractional_checksum={fractional_checksum} vector_5={vector_elapsed:?} vector_checksum={vector_checksum}",
        SEGMENTS * FRAMES_PER_SEGMENT,
        SEGMENTS,
    );
}