# -- encoding: utf-8 -- """ Description: 医疗图片按坐标切片 Create: 2025/02/08 11:00 """ import copy import time from typing import List, Tuple, Dict, Any import itertools from loguru import logger from openslide import OpenSlide import numpy as np from datamate.core.base_op import Slicer from datamate.common.utils import bytes_transform class SimpleSlicer(Slicer): def __init__(self, *args, **kwargs): super(SimpleSlicer, self).__init__(*args, **kwargs) self._target_size = kwargs.get("sliceSize", [128, 128]) self._overlap = kwargs.get("overlap", 0) self.last_ops = True if not isinstance(self._target_size, List): raise TypeError(f" received as {type(self._target_size)}, but expected list.") if len(self._target_size) != 2: raise ValueError(f" has {len(self._target_size)} elements, but expected 2.") if not all(isinstance(dim, int) for dim in self._target_size): raise TypeError(f"Elements in must be integers, but got {self._target_size}.") if not isinstance(self._overlap, (int, float)): raise TypeError(f" received as {type(self._overlap)}, but expected int.") if self._overlap < 0 or self._overlap > 1: raise ValueError( f" received an out of range value: {self._overlap}, " f"but (0 <= overlap <= 1) is expected." ) def execute(self, sample: Dict[str, Any]) -> List[Dict]: start = time.time() slide: OpenSlide = OpenSlide(sample["filePath"]) if not isinstance(slide, OpenSlide): logger.error("Not desired object.") dimensions: tuple[int, int] = slide.dimensions target_size = self._target_size overlap = self._overlap patch_num = self.auto_simple_slicer(sample, slide, dimensions, target_size, overlap) sample["slice_num"] = patch_num file_name = sample[self.filename_key] logger.info(f"fileName: {file_name}, method: CoordinateSlider costs {(time.time() - start):6f} s") return [sample] def auto_simple_slicer( self, original_sample: Dict[str, Any], slide: OpenSlide, dimensions: Tuple[int, int], target_size: Tuple[int, int], overlap: float ) -> int: """ 自动根据给定规格切片原图像 Return: List[Content] 每个 content 都是一个 data 为 patch 的 content """ stride_x, stride_y = map(lambda x: int(x * (1 - overlap)), target_size) w, h = target_size patch_no = 0 for x, y in itertools.product( range(0, dimensions[0] - w + 1, stride_x), range(0, dimensions[1] - h + 1, stride_y) ): # 切片 region = slide.read_region((x, y), 0, target_size) region_np = np.array(region.convert("RGB")) patch_sample = copy.deepcopy(original_sample) patch_sample[self.data_key] = bytes_transform.numpy_to_bytes(region_np, ".png") patch_no += 1 self.save_patch_sample(patch_sample, patch_no, save_format="image") logger.info(f"One image sliced into pieces: {patch_no}") return patch_no