def detect_tile_nuclei(slide_path, tile_position, args, **it_kwargs): # get slide tile source ts = large_image.getTileSource(slide_path) # get requested tile tile_info = ts.getSingleTile( tile_position=tile_position, format=large_image.tilesource.TILE_FORMAT_NUMPY, **it_kwargs) # get tile image im_tile = tile_info['tile'][:, :, :3] # perform color normalization im_nmzd = htk_cnorm.reinhard(im_tile, args.reference_mu_lab, args.reference_std_lab) # perform color decovolution w = cli_utils.get_stain_matrix(args) im_stains = htk_cdeconv.color_deconvolution(im_nmzd, w).Stains im_nuclei_stain = im_stains[:, :, 0].astype(np.float) # segment nuclei im_nuclei_seg_mask = cli_utils.detect_nuclei_kofahi(im_nuclei_stain, args) # generate nuclei annotations nuclei_annot_list = cli_utils.create_tile_nuclei_annotations( im_nuclei_seg_mask, tile_info, args.nuclei_annotation_format) return nuclei_annot_list
def compute_tile_nuclei_features(slide_path, tile_position, args, it_kwargs, src_mu_lab=None, src_sigma_lab=None): # get slide tile source ts = large_image.getTileSource(slide_path) # get requested tile tile_info = ts.getSingleTile( tile_position=tile_position, format=large_image.tilesource.TILE_FORMAT_NUMPY, **it_kwargs) # get tile image im_tile = tile_info['tile'][:, :, :3] # perform color normalization im_nmzd = htk_cnorm.reinhard(im_tile, args.reference_mu_lab, args.reference_std_lab, src_mu=src_mu_lab, src_sigma=src_sigma_lab) # perform color decovolution w = cli_utils.get_stain_matrix(args) im_stains = htk_cdeconv.color_deconvolution(im_nmzd, w).Stains im_nuclei_stain = im_stains[:, :, 0].astype(np.float) # segment nuclei im_nuclei_seg_mask = cli_utils.detect_nuclei_kofahi(im_nuclei_stain, args) # generate nuclei annotations nuclei_annot_list = cli_utils.create_tile_nuclei_annotations( im_nuclei_seg_mask, tile_info, args.nuclei_annotation_format) # compute nuclei features if args.cytoplasm_features: im_cytoplasm_stain = im_stains[:, :, 1].astype(np.float) else: im_cytoplasm_stain = None fdata = htk_features.compute_nuclei_features( im_nuclei_seg_mask, im_nuclei_stain, im_cytoplasm_stain, fsd_bnd_pts=args.fsd_bnd_pts, fsd_freq_bins=args.fsd_freq_bins, cyto_width=args.cyto_width, num_glcm_levels=args.num_glcm_levels, morphometry_features_flag=args.morphometry_features, fsd_features_flag=args.fsd_features, intensity_features_flag=args.intensity_features, gradient_features_flag=args.gradient_features, ) fdata.columns = ['Feature.' + col for col in fdata.columns] return nuclei_annot_list, fdata
def test_create_tile_nuclei_annotations(self): wsi_path = os.path.join( TEST_DATA_DIR, 'TCGA-06-0129-01Z-00-DX3.bae772ea-dd36-47ec-8185-761989be3cc8.svs') # define parameters args = { 'reference_mu_lab': [8.63234435, -0.11501964, 0.03868433], 'reference_std_lab': [0.57506023, 0.10403329, 0.01364062], 'stain_1': 'hematoxylin', 'stain_2': 'eosin', 'stain_3': 'null', 'stain_1_vector': [-1, -1, -1], 'stain_2_vector': [-1, -1, -1], 'stain_3_vector': [-1, -1, -1], 'min_fgnd_frac': 0.50, 'analysis_mag': 20, 'analysis_tile_size': 1200, 'min_radius': 12, 'max_radius': 30, 'foreground_threshold': 60, 'min_nucleus_area': 80, 'local_max_search_radius': 10, 'scheduler_address': None } args = collections.namedtuple('Parameters', args.keys())(**args) # read WSI ts = large_image.getTileSource(wsi_path) ts_metadata = ts.getMetadata() analysis_tile_size = { 'width': int(ts_metadata['tileWidth'] * np.floor( 1.0 * args.analysis_tile_size / ts_metadata['tileWidth'])), 'height': int(ts_metadata['tileHeight'] * np.floor( 1.0 * args.analysis_tile_size / ts_metadata['tileHeight'])) } # define ROI roi = { 'left': ts_metadata['sizeX'] / 2, 'top': ts_metadata['sizeY'] * 3 / 4, 'width': analysis_tile_size['width'], 'height': analysis_tile_size['height'], 'units': 'base_pixels' } # define tile iterator parameters it_kwargs = { 'tile_size': { 'width': args.analysis_tile_size }, 'scale': { 'magnification': args.analysis_mag }, 'region': roi } # create dask client cli_utils.create_dask_client(args) # get tile foregreoung at low res im_fgnd_mask_lres, fgnd_seg_scale = \ cli_utils.segment_wsi_foreground_at_low_res(ts) # compute tile foreground fraction tile_fgnd_frac_list = htk_utils.compute_tile_foreground_fraction( wsi_path, im_fgnd_mask_lres, fgnd_seg_scale, **it_kwargs) num_fgnd_tiles = np.count_nonzero( tile_fgnd_frac_list >= args.min_fgnd_frac) np.testing.assert_equal(num_fgnd_tiles, 2) # create nuclei annotations nuclei_bbox_annot_list = [] nuclei_bndry_annot_list = [] for tile_info in ts.tileIterator( format=large_image.tilesource.TILE_FORMAT_NUMPY, **it_kwargs): im_tile = tile_info['tile'][:, :, :3] # perform color normalization im_nmzd = htk_cnorm.reinhard(im_tile, args.reference_mu_lab, args.reference_std_lab) # perform color deconvolution w = cli_utils.get_stain_matrix(args) im_stains = htk_cdeconv.color_deconvolution(im_nmzd, w).Stains im_nuclei_stain = im_stains[:, :, 0].astype(np.float) # segment nuclei im_nuclei_seg_mask = cli_utils.detect_nuclei_kofahi( im_nuclei_stain, args) # generate nuclei annotations as bboxes cur_bbox_annot_list = cli_utils.create_tile_nuclei_annotations( im_nuclei_seg_mask, tile_info, 'bbox') nuclei_bbox_annot_list.extend(cur_bbox_annot_list) # generate nuclei annotations as boundaries cur_bndry_annot_list = cli_utils.create_tile_nuclei_annotations( im_nuclei_seg_mask, tile_info, 'boundary') nuclei_bndry_annot_list.extend(cur_bndry_annot_list) # compare nuclei bbox annotations with gtruth nuclei_bbox_annot_gtruth_file = os.path.join( TEST_DATA_DIR, 'TCGA-06-0129-01Z-00-DX3_roi_nuclei_bbox.anot' # noqa ) with open(nuclei_bbox_annot_gtruth_file, 'r') as fbbox_annot: nuclei_bbox_annot_list_gtruth = json.load(fbbox_annot)['elements'] # Check that nuclei_bbox_annot_list is nearly equal to # nuclei_bbox_annot_list_gtruth self.assertEqual(len(nuclei_bbox_annot_list), len(nuclei_bbox_annot_list_gtruth)) for pos in range(len(nuclei_bbox_annot_list)): np.testing.assert_array_almost_equal( nuclei_bbox_annot_list[pos]['center'], nuclei_bbox_annot_list_gtruth[pos]['center'], 0) np.testing.assert_almost_equal( nuclei_bbox_annot_list[pos]['width'], nuclei_bbox_annot_list_gtruth[pos]['width'], 1) np.testing.assert_almost_equal( nuclei_bbox_annot_list[pos]['height'], nuclei_bbox_annot_list_gtruth[pos]['height'], 1) # compare nuclei boundary annotations with gtruth nuclei_bndry_annot_gtruth_file = os.path.join( TEST_DATA_DIR, 'TCGA-06-0129-01Z-00-DX3_roi_nuclei_boundary.anot' # noqa ) with open(nuclei_bndry_annot_gtruth_file, 'r') as fbndry_annot: nuclei_bndry_annot_list_gtruth = json.load( fbndry_annot)['elements'] self.assertEqual(len(nuclei_bndry_annot_list), len(nuclei_bndry_annot_list_gtruth)) for pos in range(len(nuclei_bndry_annot_list)): np.testing.assert_array_almost_equal( nuclei_bndry_annot_list[pos]['points'], nuclei_bndry_annot_list_gtruth[pos]['points'], 0)