Split the input tensor into patches and return patches and locations. Args: inputs: the file path to a whole slide image. Yields: tuple[torch.Tensor, Sequence[int]]: yields tuple of patch and location
(self, inputs: PathLike | Sequence[PathLike])
| 396 | return self.reader.get_size(wsi, level) |
| 397 | |
| 398 | def __call__(self, inputs: PathLike | Sequence[PathLike]) -> Iterable[tuple[torch.Tensor, Sequence[int]]]: |
| 399 | """Split the input tensor into patches and return patches and locations. |
| 400 | |
| 401 | Args: |
| 402 | inputs: the file path to a whole slide image. |
| 403 | |
| 404 | Yields: |
| 405 | tuple[torch.Tensor, Sequence[int]]: yields tuple of patch and location |
| 406 | """ |
| 407 | # Handle if the input file paths are batched |
| 408 | if not isinstance(inputs, str) and isinstance(inputs, Sequence): |
| 409 | if len(inputs) > 1: |
| 410 | raise ValueError("Only batch size of one would work for wsi image. Please provide one path at a time.") |
| 411 | inputs = inputs[0] |
| 412 | |
| 413 | # Check if the input is a sting or path like |
| 414 | if not isinstance(inputs, (str, os.PathLike)): |
| 415 | raise ValueError(f"The input should be the path to the whole slide image. {type(inputs)} is given.") |
| 416 | |
| 417 | wsi = self.reader.read(inputs) |
| 418 | level = self.reader_kwargs.get("level", 0) |
| 419 | downsample_ratio = self.reader.get_downsample_ratio(wsi, level) |
| 420 | spatial_shape: tuple = self.reader.get_size(wsi, level) |
| 421 | spatial_ndim = len(spatial_shape) |
| 422 | if spatial_ndim != 2: |
| 423 | raise ValueError(f"WSIReader only support 2D images. {spatial_ndim} spatial dimension is provided.") |
| 424 | patch_size, overlap, offset = self._get_valid_shape_parameters(spatial_shape) |
| 425 | pad_size, is_start_padded = self._calculate_pad_size(spatial_shape, spatial_ndim, patch_size, offset, overlap) |
| 426 | |
| 427 | # Padding (extend the spatial shape) |
| 428 | if any(pad_size): |
| 429 | spatial_shape = tuple(ss + ps + pe for ss, ps, pe in zip(spatial_shape, pad_size[1::2], pad_size[::2])) |
| 430 | # correct the offset with respect to the padded image |
| 431 | if is_start_padded: |
| 432 | offset = tuple(off + p for off, p in zip(offset, pad_size[1::2])) |
| 433 | |
| 434 | # Splitting (extracting patches) |
| 435 | for location in iter_patch_position(spatial_shape, patch_size, offset, overlap, False): |
| 436 | location_ = tuple(round(loc * downsample_ratio) for loc in location) |
| 437 | patch = self._get_patch(wsi, location_, patch_size) |
| 438 | patch = convert_to_tensor(patch, device=self.device) |
| 439 | # correct the location with respect to original inputs (remove starting pads) |
| 440 | if is_start_padded: |
| 441 | location = tuple(loc - p for loc, p in zip(location, pad_size[1::2])) |
| 442 | # filter patch and yield |
| 443 | if self.filter_fn is None or self.filter_fn(patch, location): |
| 444 | yield patch, location |
nothing calls this directly
no test coverage detected