(wsi, tissue_mask_scaled, tile_size_microns, offsets_micron=None)
| 367 | return tissue_mask_scaled |
| 368 | |
| 369 | def create_tissue_tiles(wsi, tissue_mask_scaled, tile_size_microns, offsets_micron=None): |
| 370 | |
| 371 | print(f"tile size is {tile_size_microns} um") |
| 372 | |
| 373 | # Compute the tile size in pixels from the desired tile size in microns and the image resolution |
| 374 | assert ( |
| 375 | openslide.PROPERTY_NAME_MPP_X in wsi.properties |
| 376 | ), "microns per pixel along X-dimension not available" |
| 377 | assert ( |
| 378 | openslide.PROPERTY_NAME_MPP_Y in wsi.properties |
| 379 | ), "microns per pixel along Y-dimension not available" |
| 380 | |
| 381 | mpp_x = float(wsi.properties[openslide.PROPERTY_NAME_MPP_X]) |
| 382 | mpp_y = float(wsi.properties[openslide.PROPERTY_NAME_MPP_Y]) |
| 383 | |
| 384 | # For larger tiles in micron, NKI scanner outputs mppx slight different than mppy. |
| 385 | # Force tiles to be squared. |
| 386 | mpp_scale_factor = min(mpp_x, mpp_y) |
| 387 | if mpp_x != mpp_y: |
| 388 | print( |
| 389 | f"mpp_x of {mpp_x} and mpp_y of {mpp_y} are not the same. Using smallest value: {mpp_scale_factor}" |
| 390 | ) |
| 391 | |
| 392 | tile_size_pix = round(tile_size_microns / mpp_scale_factor) |
| 393 | |
| 394 | # Use the tissue mask bounds as base offsets (+ a margin of a few tiles) to avoid wasting CPU power creating tiles that are never going |
| 395 | # to be inside the tissue mask. |
| 396 | tissue_margin_pix = tile_size_pix * 2 |
| 397 | minx, miny, maxx, maxy = tissue_mask_scaled.bounds |
| 398 | min_offset_x = minx - tissue_margin_pix |
| 399 | min_offset_y = miny - tissue_margin_pix |
| 400 | offsets = [(min_offset_x, min_offset_y)] |
| 401 | |
| 402 | if offsets_micron is not None: |
| 403 | assert ( |
| 404 | len(offsets_micron) > 0 |
| 405 | ), "offsets_micron needs to contain at least one value" |
| 406 | # Compute the offsets in micron scale |
| 407 | offset_pix = [round(o / mpp_scale_factor) for o in offsets_micron] |
| 408 | offsets = [(o + min_offset_x, o + min_offset_y) for o in offset_pix] |
| 409 | |
| 410 | # Generate tiles covering the entire WSI |
| 411 | all_tiles = generate_tiles( |
| 412 | tile_size_pix, |
| 413 | tile_size_pix, |
| 414 | maxx + tissue_margin_pix, |
| 415 | maxy + tissue_margin_pix, |
| 416 | offsets=offsets, |
| 417 | ) |
| 418 | |
| 419 | # Retain only the tiles that sit within the tissue mask polygon |
| 420 | filtered_tiles = [rect for rect in all_tiles if tissue_mask_scaled.intersects(rect)] |
| 421 | |
| 422 | return filtered_tiles |
| 423 | |
| 424 | def tile_is_not_empty(tile, threshold_white=20): |
| 425 | histogram = tile.histogram() |
no test coverage detected