wsi-rs 0.4.0

wsi-rs whole-slide image reader
Documentation
use super::*;

pub(super) fn is_encapsulated_transfer_syntax(uid: &str) -> bool {
    uid == JPEG_TRANSFER_SYNTAX
        || uid == RLE_TRANSFER_SYNTAX
        || JP2K_TRANSFER_SYNTAXES.contains(&uid)
}

#[cfg(any(feature = "metal", feature = "cuda"))]
pub(super) fn dicom_jp2k_device_decode_enabled() -> bool {
    std::env::var(DICOM_JP2K_DEVICE_DECODE_ENV).is_ok_and(|value| {
        value.eq_ignore_ascii_case("1")
            || value.eq_ignore_ascii_case("true")
            || value.eq_ignore_ascii_case("yes")
            || value.eq_ignore_ascii_case("on")
    })
}

#[cfg(any(feature = "metal", feature = "cuda"))]
pub(super) const DICOM_CLASSIC_JP2K_PREFER_DEVICE_BATCH_MIN: usize = 8;

#[cfg(any(feature = "metal", feature = "cuda"))]
pub(super) fn dicom_htj2k_transfer_syntax(transfer_syntax_uid: &str) -> bool {
    matches!(
        transfer_syntax_uid,
        HTJ2K_TRANSFER_SYNTAX
            | HTJ2K_LOSSLESS_TRANSFER_SYNTAX
            | HTJ2K_LOSSLESS_RPCL_TRANSFER_SYNTAX
    )
}

#[cfg(any(feature = "metal", feature = "cuda"))]
pub(super) fn dicom_jp2k_device_batch_allowed_for_output(
    transfer_syntax_uid: &str,
    output: &TileOutputPreference,
    classic_jp2k_override: bool,
    batch_len: usize,
) -> bool {
    if !JP2K_TRANSFER_SYNTAXES.contains(&transfer_syntax_uid) {
        return false;
    }
    if dicom_htj2k_transfer_syntax(transfer_syntax_uid) {
        return output.compressed_device_decode_enabled();
    }

    output.compressed_device_decode_enabled()
        && (classic_jp2k_override
            || output.requires_device()
            || !output.adaptive_decode_route_enabled()
            || batch_len >= DICOM_CLASSIC_JP2K_PREFER_DEVICE_BATCH_MIN)
}

#[cfg(any(feature = "metal", feature = "cuda"))]
pub(super) fn dicom_jp2k_device_batch_allowed(
    transfer_syntax_uid: &str,
    output: &TileOutputPreference,
    batch_len: usize,
) -> bool {
    dicom_jp2k_device_batch_allowed_for_output(
        transfer_syntax_uid,
        output,
        dicom_jp2k_device_decode_enabled(),
        batch_len,
    )
}

pub(crate) struct DicomBackend {
    pub(super) probe_cache: Mutex<LruCache<PathBuf, Arc<DicomSlide>>>,
}

impl DicomBackend {
    pub(crate) fn new() -> Self {
        Self {
            probe_cache: Mutex::new(LruCache::new(NonZeroUsize::new(4).unwrap())),
        }
    }

    pub(super) fn cache_key(path: &Path) -> PathBuf {
        std::fs::canonicalize(path).unwrap_or_else(|_| path.to_path_buf())
    }

    pub(super) fn parse(&self, path: &Path) -> Result<Arc<DicomSlide>, WsiError> {
        Ok(Arc::new(DicomSlide::parse(path)?))
    }
}

impl Default for DicomBackend {
    fn default() -> Self {
        Self::new()
    }
}

impl FormatProbe for DicomBackend {
    fn probe(&self, path: &Path) -> Result<ProbeResult, WsiError> {
        let key = Self::cache_key(path);
        if self
            .probe_cache
            .lock()
            .unwrap_or_else(|e| e.into_inner())
            .get(&key)
            .is_some()
        {
            return Ok(ProbeResult {
                detected: true,
                vendor: "dicom".into(),
                confidence: ProbeConfidence::Definite,
            });
        }
        if path.is_dir() {
            return match self.parse(path) {
                Ok(slide) => {
                    self.probe_cache
                        .lock()
                        .unwrap_or_else(|e| e.into_inner())
                        .put(key, slide);
                    Ok(ProbeResult {
                        detected: true,
                        vendor: "dicom".into(),
                        confidence: ProbeConfidence::Definite,
                    })
                }
                Err(WsiError::UnsupportedFormat(_)) => Ok(ProbeResult {
                    detected: false,
                    vendor: String::new(),
                    confidence: ProbeConfidence::Likely,
                }),
                Err(err) => Err(err),
            };
        }
        match parse_metadata_object(path) {
            Ok(meta) if is_vl_wsi(meta.obj.meta().media_storage_sop_class_uid()) => {
                let slide = self.parse(path)?;
                self.probe_cache
                    .lock()
                    .unwrap_or_else(|e| e.into_inner())
                    .put(key, slide);
                Ok(ProbeResult {
                    detected: true,
                    vendor: "dicom".into(),
                    confidence: ProbeConfidence::Definite,
                })
            }
            Ok(_) => Ok(ProbeResult {
                detected: false,
                vendor: String::new(),
                confidence: ProbeConfidence::Likely,
            }),
            Err(_) => Ok(ProbeResult {
                detected: false,
                vendor: String::new(),
                confidence: ProbeConfidence::Likely,
            }),
        }
    }
}

impl DatasetReader for DicomBackend {
    fn open(&self, path: &Path) -> Result<Box<dyn SlideReader>, WsiError> {
        let key = Self::cache_key(path);
        let cached = self
            .probe_cache
            .lock()
            .unwrap_or_else(|e| e.into_inner())
            .get(&key)
            .cloned();
        let slide = match cached {
            Some(slide) => slide,
            None => self.parse(path)?,
        };
        Ok(Box::new(DicomReader { slide }))
    }
}