Spaces:
Sleeping
Sleeping
| from io import BytesIO | |
| from typing import Tuple | |
| import numpy as np | |
| from PIL import Image | |
| import openslide | |
| from skimage import color | |
| def _level_downsample(slide: openslide.OpenSlide, level: int) -> float: | |
| return float(slide.level_downsamples[level]) | |
| def _level_dims(slide: openslide.OpenSlide, level: int) -> Tuple[int, int]: | |
| return slide.level_dimensions[level] | |
| def _blank_tile(tile_size: int) -> bytes: | |
| img = Image.new("RGB", (tile_size, tile_size), (255, 255, 255)) | |
| buf = BytesIO() | |
| img.save(buf, format="JPEG", quality=85) | |
| return buf.getvalue() | |
| def _channel_from_rgb(rgb: Image.Image, channel: str) -> Image.Image: | |
| if channel == "original": | |
| return rgb | |
| arr = np.asarray(rgb).astype(np.float32) / 255.0 | |
| hed = color.rgb2hed(arr) | |
| if channel == "hematoxylin": | |
| hed_only = np.zeros_like(hed) | |
| hed_only[..., 0] = hed[..., 0] | |
| elif channel == "eosin": | |
| hed_only = np.zeros_like(hed) | |
| hed_only[..., 1] = hed[..., 1] | |
| else: | |
| return rgb | |
| rgb_stain = color.hed2rgb(hed_only) | |
| rgb_stain = np.clip(rgb_stain, 0.0, 1.0) | |
| # Normalize for better contrast | |
| min_val = rgb_stain.min(axis=(0, 1), keepdims=True) | |
| max_val = rgb_stain.max(axis=(0, 1), keepdims=True) | |
| denom = np.maximum(max_val - min_val, 1e-6) | |
| rgb_stain = (rgb_stain - min_val) / denom | |
| out = (rgb_stain * 255.0).clip(0, 255).astype(np.uint8) | |
| return Image.fromarray(out, mode="RGB") | |
| def get_tile_jpeg( | |
| slide: openslide.OpenSlide, | |
| level: int, | |
| x: int, | |
| y: int, | |
| tile_size: int, | |
| channel: str = "original", | |
| ) -> bytes: | |
| if level < 0 or level >= slide.level_count: | |
| raise ValueError("Invalid level") | |
| level_w, level_h = _level_dims(slide, level) | |
| px = x * tile_size | |
| py = y * tile_size | |
| if px >= level_w or py >= level_h: | |
| return _blank_tile(tile_size) | |
| downsample = _level_downsample(slide, level) | |
| x0 = int(px * downsample) | |
| y0 = int(py * downsample) | |
| region = slide.read_region((x0, y0), level, (tile_size, tile_size)) | |
| rgb = region.convert("RGB") | |
| rgb = _channel_from_rgb(rgb, channel) | |
| buf = BytesIO() | |
| rgb.save(buf, format="JPEG", quality=85) | |
| return buf.getvalue() | |
| def get_thumbnail_jpeg( | |
| slide: openslide.OpenSlide, | |
| size: int = 256, | |
| channel: str = "original", | |
| ) -> bytes: | |
| level = max(slide.level_count - 1, 0) | |
| level_w, level_h = _level_dims(slide, level) | |
| # Read the full lowest-resolution level, then downscale to a fixed thumbnail. | |
| region = slide.read_region((0, 0), level, (level_w, level_h)) | |
| rgb = region.convert("RGB") | |
| rgb = _channel_from_rgb(rgb, channel) | |
| # Preserve aspect ratio and pad to square so the whole slide is visible. | |
| scale = min(size / max(level_w, 1), size / max(level_h, 1)) | |
| new_w = max(int(level_w * scale), 1) | |
| new_h = max(int(level_h * scale), 1) | |
| rgb = rgb.resize((new_w, new_h), resample=Image.BILINEAR) | |
| canvas = Image.new("RGB", (size, size), (255, 255, 255)) | |
| offset_x = (size - new_w) // 2 | |
| offset_y = (size - new_h) // 2 | |
| canvas.paste(rgb, (offset_x, offset_y)) | |
| rgb = canvas | |
| buf = BytesIO() | |
| rgb.save(buf, format="JPEG", quality=85) | |
| return buf.getvalue() | |