| 447 | return slide.read_region(top_left_coords, 0, (int(maxx - minx), int(maxy - miny))) |
| 448 | |
| 449 | class BagOfTiles(Dataset): |
| 450 | def __init__(self, wsi, tiles, resize_to=224): |
| 451 | self.wsi = wsi |
| 452 | self.tiles = tiles |
| 453 | |
| 454 | self.roi_transforms = transforms.Compose( |
| 455 | [ |
| 456 | # As we can't be sure that the input tile dimensions are all consistent, we resize |
| 457 | # them to a commonly used size before feeding them to the model. |
| 458 | # Note: assumes a square image. |
| 459 | transforms.Resize(resize_to), |
| 460 | # Turn the PIL image into a (C x H x W) float tensor in the range [0.0, 1.0] |
| 461 | transforms.ToTensor(), |
| 462 | ] |
| 463 | ) |
| 464 | |
| 465 | def __len__(self): |
| 466 | return len(self.tiles) |
| 467 | |
| 468 | def __getitem__(self, idx): |
| 469 | tile = self.tiles[idx] |
| 470 | img = crop_rect_from_slide(self.wsi, tile) |
| 471 | |
| 472 | # RGB filtering - calling here speeds up computation since it requires crop_rect_from_slide function. |
| 473 | #is_tile_kept = tile_is_not_empty(img, threshold_white=20) |
| 474 | is_tile_kept = True |
| 475 | |
| 476 | # Ensure the img is RGB, as expected by the pretrained model. |
| 477 | # See https://pytorch.org/docs/stable/torchvision/models.html |
| 478 | img = img.convert("RGB") |
| 479 | |
| 480 | # Ensure we have a square tile in our hands. |
| 481 | # We can't handle non-squares currently, as this would requiring changes to |
| 482 | # the aspect ratio when resizing. |
| 483 | width, height = img.size |
| 484 | assert width == height, "input image is not a square" |
| 485 | |
| 486 | img = self.roi_transforms(img).unsqueeze(0) |
| 487 | coord = tile.bounds |
| 488 | return img, coord, is_tile_kept |
| 489 | |
| 490 | def collate_features(batch): |
| 491 | # Item 2 is the boolean value from tile filtering. |