(args)
| 574 | return np.asarray(features_), np.asarray(coords_) |
| 575 | |
| 576 | def extract_save_features(args): |
| 577 | # Derive the slide ID from its name. |
| 578 | slide_id, _ = os.path.splitext(os.path.basename(args.input_slide)) |
| 579 | wip_file_path = os.path.join(args.output_dir, slide_id + "_wip.h5") |
| 580 | output_file_path = os.path.join(args.output_dir, slide_id + "_features.h5") |
| 581 | |
| 582 | os.makedirs(args.output_dir, exist_ok=True) |
| 583 | |
| 584 | # Check if the _features output file already exist. If so, we terminate to avoid |
| 585 | # overwriting it by accident. This also simplifies resuming bulk batch jobs. |
| 586 | if os.path.exists(output_file_path): |
| 587 | raise Exception(f"{output_file_path} already exists") |
| 588 | |
| 589 | # Open the slide for reading. |
| 590 | wsi = openslide.open_slide(args.input_slide) |
| 591 | |
| 592 | # Decide on which slide level we want to base the segmentation. |
| 593 | seg_level = wsi.get_best_level_for_downsample(64) |
| 594 | |
| 595 | # Run the segmentation and tiling procedure. |
| 596 | start_time = time.time() |
| 597 | tissue_mask_scaled = create_tissue_mask(wsi, seg_level, method=args.method) |
| 598 | filtered_tiles = create_tissue_tiles(wsi, tissue_mask_scaled, args.tile_size) |
| 599 | |
| 600 | # Build a figure for quality control purposes, to check if the tiles are where we expect them. |
| 601 | qc_img = make_tile_QC_fig(filtered_tiles, wsi, seg_level, 2) |
| 602 | qc_img_target_width = 1920 |
| 603 | qc_img = qc_img.resize((qc_img_target_width, int(qc_img.height / (qc_img.width / qc_img_target_width)))) |
| 604 | qc_img_file_path = os.path.join(args.output_dir, f"{slide_id}_features_QC.png") |
| 605 | qc_img.save(qc_img_file_path) |
| 606 | print(f"Finished creating {len(filtered_tiles)} tissue tiles in {time.time() - start_time}s") |
| 607 | |
| 608 | # Save QC figure. |
| 609 | qc_img_file_path = os.path.join( |
| 610 | args.output_dir, f"{slide_id}_N{len(mergedpatches)}mergedpatches_distThreshold{args.dist_threshold}_corrThreshold{args.corr_threshold}.png" |
| 611 | ) |
| 612 | |
| 613 | # Extract the rectangles, and compute the feature vectors. Example using EsVIT. |
| 614 | device = torch.device("cuda") |
| 615 | model, _, depths = load_encoder_esVIT(args, device) |
| 616 | |
| 617 | features, coords = extract_features( |
| 618 | model, |
| 619 | device, |
| 620 | wsi, |
| 621 | filtered_tiles, |
| 622 | args.workers, |
| 623 | args.out_size, |
| 624 | args.batch_size, |
| 625 | n_last_blocks = args.n_last_blocks, |
| 626 | avgpool_patchtokens = args.avgpool_patchtokens, |
| 627 | depths = depths, |
| 628 | ) |
| 629 | |
| 630 | print(f'Number of features N={len(features)}') |
| 631 | # Merging nearby patches with similar semantic. |
| 632 | mergedpatches = mergedpatch_gen(features, coords, dist_threshold=args.dist_threshold, corr_threshold=args.corr_threshold) |
| 633 | print(f'Merging step => N={len(mergedpatches)}') |
no test coverage detected