def _data_from_large_image(path, outputPath, **kwargs): """ Check if the input file can be read by installed large_image tile sources. If so, return the metadata, internal metadata, and extract each associated image. :param path: path of the file. :param outputPath: the name of a temporary output file. :returns: a dictionary of metadata, internal_metadata, and images. images is a dictionary of keys and paths. Returns None if the path is not readable by large_image. """ _import_pyvips() if not path.startswith('large_image://test'): try: ts = large_image.getTileSource(path) except Exception: return else: import urllib.parse tsparams = { k: int(v[0]) if v[0].isdigit() else v[0] for k, v in urllib.parse.parse_qs( path.split('?', 1)[1] if '?' in path else '').items() } ts = large_image.getTileSource('large_image://test', **tsparams) results = { 'metadata': ts.getMetadata(), 'internal_metadata': ts.getInternalMetadata(), 'images': {}, 'tilesource': ts, } tasks = [] pool = _get_thread_pool(**kwargs) for key in ts.getAssociatedImagesList(): if not _use_associated_image(key, **kwargs): continue try: img, mime = ts.getAssociatedImage(key) except Exception: continue savePath = outputPath + '-%s-%s.tiff' % ( key, time.strftime('%Y%m%d-%H%M%S')) # TODO: allow specifying quality separately from main image quality _pool_add(tasks, (pool.submit(_convert_via_vips, img, savePath, outputPath, mime=mime, forTiled=False), )) results['images'][key] = savePath _drain_pool(pool, tasks) return results
def testGetTileSource(self): from large_image import getTileSource, tilesource try: source = getTileSource( os.path.join(os.path.dirname(__file__), 'test_files', 'yb10kx5k.png')) self.assertTrue(False) except tilesource.TileSourceException: source = None self.assertIsNone(source) source = getTileSource(os.path.join( os.environ['LARGE_IMAGE_DATA'], 'sample_svs_image.TCGA-DU-6399-' '01A-01-TS1.e8eb65de-d63e-42db-af6f-14fefbbdf7bd.svs'), encoding='PNG') tileMetadata = source.getMetadata() params = {'encoding': 'PNG'} self._testTilesZXY(source, tileMetadata, params, PNGHeader) source = getTileSource( os.path.join(os.environ['LARGE_IMAGE_DATA'], 'sample_image.ptif')) tileMetadata = source.getMetadata() tileMetadata['sparse'] = 5 self._testTilesZXY(source, tileMetadata) source = getTileSource('large_image://dummy') self.assertEqual(source.getTile(0, 0, 0), '') tileMetadata = source.getMetadata() self.assertEqual(tileMetadata['tileWidth'], 0) self.assertEqual(tileMetadata['tileHeight'], 0) self.assertEqual(tileMetadata['sizeX'], 0) self.assertEqual(tileMetadata['sizeY'], 0) self.assertEqual(tileMetadata['levels'], 0) params = { 'maxLevel': 6, 'tileWidth': 240, 'tileHeight': 170, 'fractal': False, 'encoding': 'PNG' } source = getTileSource('large_image://test', **params) tileMetadata = source.getMetadata() self.assertEqual(tileMetadata['tileWidth'], 240) self.assertEqual(tileMetadata['tileHeight'], 170) self.assertEqual(tileMetadata['sizeX'], 15360) self.assertEqual(tileMetadata['sizeY'], 10880) self.assertEqual(tileMetadata['levels'], 7) self._testTilesZXY(source, tileMetadata, params, PNGHeader)
def testGetTileSource(self): from large_image import getTileSource, tilesource try: source = getTileSource(os.path.join( os.path.dirname(__file__), 'test_files', 'yb10kx5k.png')) self.assertTrue(False) except tilesource.TileSourceException: source = None self.assertIsNone(source) source = getTileSource(os.path.join( os.environ['LARGE_IMAGE_DATA'], 'sample_svs_image.TCGA-DU-6399-' '01A-01-TS1.e8eb65de-d63e-42db-af6f-14fefbbdf7bd.svs'), encoding='PNG') tileMetadata = source.getMetadata() params = {'encoding': 'PNG'} self._testTilesZXY(source, tileMetadata, params, PNGHeader) source = getTileSource(os.path.join( os.environ['LARGE_IMAGE_DATA'], 'sample_image.ptif')) tileMetadata = source.getMetadata() tileMetadata['sparse'] = 5 self._testTilesZXY(source, tileMetadata) source = getTileSource('large_image://dummy') self.assertEqual(source.getTile(0, 0, 0), '') tileMetadata = source.getMetadata() self.assertEqual(tileMetadata['tileWidth'], 0) self.assertEqual(tileMetadata['tileHeight'], 0) self.assertEqual(tileMetadata['sizeX'], 0) self.assertEqual(tileMetadata['sizeY'], 0) self.assertEqual(tileMetadata['levels'], 0) params = { 'maxLevel': 6, 'tileWidth': 240, 'tileHeight': 170, 'fractal': False, 'encoding': 'PNG' } source = getTileSource('large_image://test', **params) tileMetadata = source.getMetadata() self.assertEqual(tileMetadata['tileWidth'], 240) self.assertEqual(tileMetadata['tileHeight'], 170) self.assertEqual(tileMetadata['sizeX'], 15360) self.assertEqual(tileMetadata['sizeY'], 10880) self.assertEqual(tileMetadata['levels'], 7) self._testTilesZXY(source, tileMetadata, params, PNGHeader)
def get_image_from_slide(file, mag=1.25): """ Extract an image at the desired magnification from a slide file Parameters ========== file: path to the slide (str) or the slide (large_image) mag : magnification to extract the image (float) Return ====== image: array """ # ----- Check if slide is already the slide or the input name ----- if type(file) is str: # Read the slide slide = large_image.getTileSource(file) else: slide = file # ----- Get slide at given magnification ----- if mag == 'base': mag = slide.getNativeMagnification()['magnification'] image, _ = slide.getRegion(scale=dict(magnification=mag), format=large_image.tilesource.TILE_FORMAT_NUMPY) return image[:, :, :3]
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 main(args): utils.create_dask_client(args) ts = large_image.getTileSource(args.inputImageFile) make_label_image = args.outputLabelImage is not None region = utils.get_region_dict( args.region, *(args.maxRegionSize, ts) if make_label_image else ()).get('region') ppc_params = ppc.Parameters( **{k: getattr(args, k) for k in ppc.Parameters._fields}) results = ppc.count_slide( args.inputImageFile, ppc_params, region, args.tile_grouping, make_label_image, ) if make_label_image: stats, label_image = results # Colorize label image. Colors from the "coolwarm" color map color_map = np.empty((4, 3), dtype=np.uint8) color_map[ppc.Labels.NEGATIVE] = 255 color_map[ppc.Labels.WEAK] = 60, 78, 194 color_map[ppc.Labels.PLAIN] = 221, 220, 220 color_map[ppc.Labels.STRONG] = 180, 4, 38 # Cleverly index color_map label_image = color_map[label_image] skimage.io.imsave(args.outputLabelImage, label_image) else: stats, = results with open(args.returnParameterFile, 'w') as f: for k, v in zip(stats._fields, stats): f.write('{} = {}\n'.format(k, v))
def main(args): # Read Input Image print('>> Reading input image') print(args.inputImageFile) ts = large_image.getTileSource(args.inputImageFile) im_input = ts.getRegion(format=large_image.tilesource.TILE_FORMAT_NUMPY, **utils.get_region_dict(args.region, args.maxRegionSize, ts))[0] # Create stain matrix print('>> Creating stain matrix') w = utils.get_stain_matrix(args) print(w) # Perform color deconvolution print('>> Performing color deconvolution') im_stains = htk_cd.color_deconvolution(im_input, w).Stains # write stain images to output print('>> Outputting individual stain images') print(args.outputStainImageFile_1) skimage.io.imsave(args.outputStainImageFile_1, im_stains[:, :, 0]) print(args.outputStainImageFile_2) skimage.io.imsave(args.outputStainImageFile_2, im_stains[:, :, 1]) print(args.outputStainImageFile_3) skimage.io.imsave(args.outputStainImageFile_3, im_stains[:, :, 2])
def average_color(imagePath, magnification=None): """ Print the average color for a tiled image file. :param imagePath: path of the file to analyze. :param magnification: optional magnification to use for the analysis. """ source = large_image.getTileSource(imagePath) # get a thumbnail no larger than 1024x1024 pixels thumbnail, mimeType = source.getThumbnail( width=1024, height=1024, encoding='JPEG') print('Made a thumbnail of type %s taking %d bytes' % ( mimeType, len(thumbnail))) # We could save it, if we want to. # open('/tmp/thumbnail.jpg', 'wb').write(thumbnail) tileMeans = [] tileWeights = [] # iterate through the tiles at a particular magnification: for tile in source.tileIterator( format=large_image.tilesource.TILE_FORMAT_NUMPY, scale={'magnification': magnification}, resample=True): # The tile image data is in tile['tile'] and is a numpy # multi-dimensional array mean = numpy.mean(tile['tile'], axis=(0, 1)) tileMeans.append(mean) tileWeights.append(tile['width'] * tile['height']) print('x: %d y: %d w: %d h: %d mag: %g color: %g %g %g' % ( tile['x'], tile['y'], tile['width'], tile['height'], tile['magnification'], mean[0], mean[1], mean[2])) mean = numpy.average(tileMeans, axis=0, weights=tileWeights) print('Average color: %g %g %g' % (mean[0], mean[1], mean[2]))
def read_tf_largeImage(tif_file_path, mag): wsi_image = large_image.getTileSource(tif_file_path) rgba_image, _ = wsi_image.getRegion( scale=dict(magnification=mag), format=large_image.tilesource.TILE_FORMAT_NUMPY) slide_w, slide_h = rgba_image.shape[1], rgba_image.shape[0] return rgba_image, (slide_w, slide_h)
def _sample_pixels_tile(slide_path, iter_args, positions, sample_fraction, tissue_seg_mag, min_coverage, im_fgnd_mask_lres): start_position, position_count = positions sample_pixels = [np.empty((0, 3))] ts = large_image.getTileSource(slide_path) for position in range(start_position, start_position + position_count): tile = ts.getSingleTile(tile_position=position, **iter_args) # get current region in base_pixels rgn_hres = {'left': tile['gx'], 'top': tile['gy'], 'right': tile['gx'] + tile['gwidth'], 'bottom': tile['gy'] + tile['gheight'], 'units': 'base_pixels'} # get foreground mask for current tile at low resolution rgn_lres = ts.convertRegionScale(rgn_hres, targetScale={'magnification': tissue_seg_mag}, targetUnits='mag_pixels') top = np.int(rgn_lres['top']) bottom = np.int(rgn_lres['bottom']) left = np.int(rgn_lres['left']) right = np.int(rgn_lres['right']) tile_fgnd_mask_lres = im_fgnd_mask_lres[top:bottom, left:right] # skip tile if there is not enough foreground in the slide cur_fgnd_frac = tile_fgnd_mask_lres.mean() if np.isnan(cur_fgnd_frac) or cur_fgnd_frac <= min_coverage: continue # get current tile image im_tile = tile['tile'][:, :, :3] # get tile foreground mask at resolution of current tile tile_fgnd_mask = scipy.misc.imresize( tile_fgnd_mask_lres, im_tile.shape, interp='nearest' ) # generate linear indices of sample pixels in fgnd mask nz_ind = np.nonzero(tile_fgnd_mask.flatten())[0] # Handle fractions in the desired sample size by rounding up # or down, weighted by the fractional amount. float_samples = sample_fraction * nz_ind.size num_samples = int(np.floor(float_samples)) num_samples += np.random.binomial(1, float_samples - num_samples) sample_ind = np.random.choice(nz_ind, num_samples) # convert rgb tile image to Nx3 array tile_pix_rgb = np.reshape(im_tile, (-1, 3)) # add rgb triplet of sample pixels sample_pixels.append(tile_pix_rgb[sample_ind, :]) return np.concatenate(sample_pixels, 0)
def sum_squares(imagePath, magnification=None, **kwargs): """ Print the sum-of-squares of each color channel for a tiled image file. :param imagePath: path of the file to analyze. :param magnification: optional magnification to use for the analysis. """ source = large_image.getTileSource(imagePath) tileSumSquares = [] # iterate through the tiles at a particular magnification: for tile in source.tileIterator( format=large_image.tilesource.TILE_FORMAT_NUMPY, scale={'magnification': magnification}, resample=True, **kwargs): # The tile image data is in tile['tile'] and is a numpy # multi-dimensional array data = tile['tile'] # trim off any overlap so we don't include it in our calculations. data = data[ tile['tile_overlap']['top']: data.shape[0]-tile['tile_overlap']['bottom'], tile['tile_overlap']['left']: data.shape[1]-tile['tile_overlap']['right'], :] sumsq = numpy.sum(data**2, axis=(0, 1)) tileSumSquares.append(sumsq) print('x: %d y: %d w: %d h: %d mag: %g sums: %d %d %d' % ( tile['x'], tile['y'], tile['width'], tile['height'], tile['magnification'], sumsq[0], sumsq[1], sumsq[2])) sumsq = numpy.sum(tileSumSquares, axis=0) print('Sum of squares: %d %d %d' % (sumsq[0], sumsq[1], sumsq[2]))
def average_color(imagePath, magnification=None): """ Print the average color for a tiled image file. :param imagePath: path of the file to analyze. :param magnification: optional magnification to use for the analysis. """ source = large_image.getTileSource(imagePath) # get a thumbnail no larger than 1024x1024 pixels thumbnail, mimeType = source.getThumbnail(width=1024, height=1024, encoding='JPEG') print('Made a thumbnail of type %s taking %d bytes' % (mimeType, len(thumbnail))) # We could save it, if we want to. # open('/tmp/thumbnail.jpg', 'wb').write(thumbnail) tileMeans = [] tileWeights = [] # iterate through the tiles at a particular magnification: for tile in source.tileIterator( format=large_image.tilesource.TILE_FORMAT_NUMPY, scale={'magnification': magnification}, resample=True): # The tile image data is in tile['tile'] and is a numpy # multi-dimensional array mean = numpy.mean(tile['tile'], axis=(0, 1)) tileMeans.append(mean) tileWeights.append(tile['width'] * tile['height']) print('x: %d y: %d w: %d h: %d mag: %g color: %g %g %g' % (tile['x'], tile['y'], tile['width'], tile['height'], tile['magnification'], mean[0], mean[1], mean[2])) mean = numpy.average(tileMeans, axis=0, weights=tileWeights) print('Average color: %g %g %g' % (mean[0], mean[1], mean[2])) return mean
def retrive_wsi_low_res(self, file): print("show wsi", file) ts = large_image.getTileSource(os.path.join(cfg.FILE_DIR, file)) im_low_res, _ = ts.getRegion( scale=dict(magnification=1.25), format=large_image.tilesource.TILE_FORMAT_NUMPY) plt.imshow(im_low_res) plt.savefig(file + '.png')
def get_slide_metadata(self, file): ts = large_image.getTileSource(os.path.join(cfg.FILE_DIR, file)) print(ts.getMetadata()) # Get the magnification associated with all levels of the image pyramid for i in range(ts.levels): print('Level-{} : {}'.format(i, ts.getMagnificationForLevel(level=i)))
def detect_tile_nuclei(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 nuclear foreground im_nuclei_fgnd_mask = im_nuclei_stain < args.foreground_threshold # segment nuclei im_nuclei_seg_mask = htk_nuclear.detect_nuclei_kofahi( im_nuclei_stain, im_nuclei_fgnd_mask, args.min_radius, args.max_radius, args.min_nucleus_area, args.local_max_search_radius ) # Delete border nuclei if args.ignore_border_nuclei is True: im_nuclei_seg_mask = htk_seg_label.delete_border(im_nuclei_seg_mask) # generate nuclei annotations nuclei_annot_list = [] flag_nuclei_found = np.any(im_nuclei_seg_mask) if flag_nuclei_found: nuclei_annot_list = cli_utils.create_tile_nuclei_annotations( im_nuclei_seg_mask, tile_info, args.nuclei_annotation_format) return nuclei_annot_list
def positive_pixel_count_tiles(args, kwargs, position, count): ts = large_image.getTileSource(args.inputImageFile) total = dict((k, 0) for k in results_keys) for pos in range(position, position + count): tile = ts.getSingleTile(tile_position=pos, **kwargs)['tile'] subtotal = positive_pixel_count_single_tile(args, tile, makeLabelImage=False) for k in results_keys: total[k] += subtotal[k] return total
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_tiff_tile_source(file_, output): """Check whether large_image can return a tile with svs sources.""" test_url = 'https://data.kitware.com/api/v1/file/{}/download'.format(file_) urllib.urlretrieve(test_url, output) image = large_image.getTileSource(output) # Make sure it is the svs tile source assert isinstance(image, large_image.tilesource.SVSFileTileSource) # Make sure we can get a tile without an exception assert type(image.getTile(0, 0, 0)) == str
def _count_tiles(slide_path, params, kwargs, position, count): ts = large_image.getTileSource(slide_path) subtotal = np.array((0, 0)) for pos in range(position, position + count): tile = ts.getSingleTile(tile_position=pos, **kwargs)['tile'] subtotal = subtotal + np.array(tile.shape[0:2]) return subtotal
def test_pil_tile_source(): """Check whether large_image can return a tile with pil source.""" test_url = 'https://data.kitware.com/api/v1/item/590346fe8d777f16d01e0546/download' # noqa: E501 test_png = '/tmp/Easy1.png' urllib.urlretrieve(test_url, test_png) image = large_image.getTileSource(test_png) # Make sure it is the pil tile source assert isinstance(image, large_image.tilesource.PILFileTileSource) # Make sure we can get a tile without an exception assert type(image.getTile(0, 0, 0)) == str
def _count_tiles(slide_path, params, kwargs, position, count): ts = large_image.getTileSource(slide_path) lpotf = len(OutputTotals._fields) total = [0] * lpotf for pos in range(position, position + count): tile = ts.getSingleTile(tile_position=pos, **kwargs)['tile'] subtotal = _count_image(tile, params)[0] for k in range(lpotf): total[k] += subtotal[k] return OutputTotals._make(total)
def tile_wsi(self, file, magnification, tile_width, tile_hight, tile_overlap_x, tile_overlap_y): ts = large_image.getTileSource(os.path.join(cfg.FILE_DIR, file)) num_tiles = 0 num_empty_tiles = 0 num_nuclei = 0 tile_means = [] tile_areas = [] for tile_info in tqdm( ts.tileIterator( #region=dict(left=5000, top=5000, width=20000, height=20000, units='base_pixels'), scale=dict(magnification=magnification), tile_size=dict(width=tile_width, height=tile_hight), tile_overlap=dict(x=tile_overlap_x, y=tile_overlap_y), format=large_image.tilesource.TILE_FORMAT_PIL)): #print(tile_info) if (num_tiles > 0 and num_tiles % 100 == 0): print('Tile - {} '.format(num_tiles)) print('Empty Tile - {} '.format(num_empty_tiles)) #display(tile_info) #img im_tile = np.array(tile_info['tile']) # To check the content of the image if (self.is_good_tile(im_tile)): if (cfg.SAVE_TILES): self.save_tile_to_disk(im_tile, (tile_info['x'], tile_info['y']), file, magnification) #mean rgb tile_mean_rgb = np.mean(im_tile[:, :, :3], axis=(0, 1)) tile_means.append(tile_mean_rgb) tile_areas.append(tile_info['width'] * tile_info['height']) num_tiles += 1 """ Here is the call for computing morphometry features for each good tile""" num_nuclei += self.nuclei_seg.compute_nuclei_feat( im_tile, (tile_info['x'], tile_info['y']), file, magnification) else: """ This image is one solid color so no need to save it""" num_empty_tiles += 1 slide_mean_rgb = np.average(tile_means, axis=0, weights=tile_areas) print('Slide: ', file) print('Total num of Nuclei = {}'.format(num_nuclei)) print('Number of tiles = {}'.format(num_tiles)) print('Number of empty tiles = {}'.format(num_empty_tiles)) print('Slide mean color = {}'.format(slide_mean_rgb))
def test_mapnik_tile_source(test_url, output): """Check whether large_image can return a tile with mapnik sources.""" urllib.urlretrieve(test_url, output) image = large_image.getTileSource(output) metadata = image.getMetadata() # Make sure it is the mapnik tile source assert metadata['geospatial'] assert metadata['bounds'] assert isinstance(image, large_image.tilesource.MapnikTileSource) # Make sure we can get a tile without an exception assert type(image.getTile(0, 0, 0)) == str
def count_slide(slide_path, params, region=None, tile_grouping=256, make_label_image=False): """Compute a count of positive pixels in the slide at slide_path. This routine can also create a label image. Parameters --------- slide_path : string (path) Path to the slide to analyze. params : Parameters An instance of Parameters, which see for further documentation region : dict, optional A valid region dict (per a large_image TileSource.tileIterator's region argument) tile_grouping : int The number of tiles to process as part of a single task make_label_image : bool, default=False Whether to make a label image. See also "Notes" Returns ------- stats : Output Various statistics on the input image. See Output. label_image : array-like, only if make_label_image is set Notes ----- The return value is either a single or a pair -- it is in either case a tuple. Dask is used as configured to compute the statistics, but only if make_label_image is reset. If make_label_image is set, everything is computed in a single-threaded manner. """ ts = large_image.getTileSource(slide_path) kwargs = dict(format=large_image.tilesource.TILE_FORMAT_NUMPY) if region is not None: kwargs['region'] = region if make_label_image: tile = ts.getRegion(**kwargs)[0] return count_image(tile, params) else: results = [] total_tiles = ts.getSingleTile(**kwargs)['iterator_range']['position'] for position in range(0, total_tiles, tile_grouping): results.append( delayed(_count_tiles)(slide_path, params, kwargs, position, min(tile_grouping, total_tiles - position))) results = delayed(_combine)(results).compute() return _totals_to_stats(results),
def main(args): # # read input image # print('>> Reading input image') print(args.inputImageFile) slide_name = os.path.basename(args.inputImageFile) slide_name = os.path.splitext(slide_name)[0] ts = large_image.getTileSource(args.inputImageFile) # # read annotation file # print('\n>> Reading annotation file ...\n') with open(args.inputAnnotationFile) as json_file: annotation_data = json.load(json_file) data = [] if isinstance(annotation_data, dict): data.append(annotation_data) elif isinstance(annotation_data, list): data = list(annotation_data) # # read GTCode file # print('\n>> Reading Ground Truth file ...\n') GTCodes = args.inputGTCodeFile # # convert annotation to mask # print('\n>> Performing conversion from annotation to mask ...\n') MASK_SAVEPATH = args.outputDirectory # create folders if necessary for folder in [MASK_SAVEPATH, ]: try: os.mkdir(folder) except: pass get_all_roi_masks_for_slide(ts, data, GTCodes, MASK_SAVEPATH=MASK_SAVEPATH, slide_name=slide_name, verbose=True, get_roi_mask_kwargs={ 'iou_thresh': 0.0, 'crop_to_roi': True, 'use_shapely': True, 'verbose': True}, )
def compute_superpixel_data(img_path, tile_position, wsi_mean, wsi_stddev): # get slide tile source ts = large_image.getTileSource(img_path) # get requested tile information tile_info = ts.getSingleTile( tile_position=tile_position, resample=True, format=large_image.tilesource.TILE_FORMAT_NUMPY) im_tile = tile_info['tile'][:, :, :3] reference_mu_lab = [8.63234435, -0.11501964, 0.03868433] reference_std_lab = [0.57506023, 0.10403329, 0.01364062] # perform color normalization im_nmzd = htk_cnorm.reinhard(im_tile, reference_mu_lab, reference_std_lab, wsi_mean, wsi_stddev) patchSize = 32 # compute the number of super-pixels im_width, im_height = im_nmzd.shape[:2] n_superpixels = (im_width / patchSize) * (im_height / patchSize) # # Generate labels using a superpixel algorithm (SLIC) # In SLIC, compactness controls image space proximity. # Higher compactness will make the shape of superpixels more square. # compactness = 50 im_label = slic(im_nmzd, n_segments=n_superpixels, compactness=compactness) + 1 region_props = regionprops(im_label) # set superpixel data list s_data = [] for i in range(len(region_props)): # get x, y centroids for superpixel cen_x, cen_y = region_props[i].centroid # get bounds of superpixel region min_row, max_row, min_col, max_col = \ get_patch_bounds(cen_x, cen_y, patchSize, im_width, im_height) rgb_data = im_nmzd[min_row:max_row, min_col:max_col] s_data.append(rgb_data) return s_data
def tiling(path, folder, counter): # if counter == 0: ts = large_image.getTileSource(path) num_tiles = 0 # print(wsi_path) tile_means = [] tile_areas = [] it = 0 for tile_info in ts.tileIterator( region=dict(left=5000, top=5000, width=20000, height=20000, units='base_pixels'), scale=dict(magnification=20), tile_size=dict(width=400, height=400), tile_overlap=dict(x=0, y=0), format=large_image.tilesource.TILE_FORMAT_PIL): print("------------------------" + str(nm_im) + "----" + str(counter) + "----" + str(it) + "------------------------") im_tile = (tile_info['tile']) im_tile = np.array(im_tile) flag = check_image(im_tile) if flag: plt.imshow(im_tile) plt.show() print(im_tile.shape) cv2.imwrite('test_images/lgg/' + str(counter) + '.png', im_tile) counter += 1 it += 1 # print(it, tile_info['gheight'],tile_info['gheight']) # plt.imshow(im_tile) # plt.show() # if tile_info['gwidth']==2000 and tile_info['gheight']==2000: # im_tile.convert('LA').save(str("im1/grayscale/")+str(it)+'.png') # # plt.savefig(str("im1/color/")+str(it)+'.png') # # tile_mean_rgb = np.mean(im_tile[:, :, :3], axis=(0, 1)) # # tile_means.append( tile_mean_rgb ) # tile_areas.append( tile_info['width'] * tile_info['height'] ) num_tiles += 1 # slide_mean_rgb = np.average(tile_means, axis=0, weights=tile_areas) # print(it) print('Number of tiles = {}'.format(num_tiles)) # print('Slide mean color = {}'.format(slide_mean_rgb)) ts.getNativeMagnification() ts.getMagnificationForLevel(level=0) return counter
def testTiffClosed(self): # test the Tiff files are properly closed. orig_del = TiledTiffDirectory.__del__ orig_init = TiledTiffDirectory.__init__ self.delCount = 0 self.initCount = 0 def countDelete(*args, **kwargs): self.delCount += 1 orig_del(*args, **kwargs) def countInit(*args, **kwargs): self.initCount += 1 orig_init(*args, **kwargs) imagePath = utilities.externaldata('data/sample_image.ptif.sha512') cachesClear() gc.collect(2) TiledTiffDirectory.__del__ = countDelete TiledTiffDirectory.__init__ = countInit self.initCount = 0 self.delCount = 0 source = large_image.getTileSource(imagePath) assert source is not None assert self.initCount == 12 assert self.delCount < 12 # Create another source; we shouldn't init it again, as it should be # cached. source = large_image.getTileSource(imagePath) assert source is not None assert self.initCount == 12 assert self.delCount < 12 source = None # Clear the cache to free references and force garbage collection cachesClear() gc.collect(2) cachesClear() assert self.delCount == 12
def test_get_region_dict(self): ts = large_image.getTileSource( os.path.join(utilities.externaldata('data/Easy1.png.sha512'))) result = cli_utils.get_region_dict([-1, -1, -1, -1], 2000, ts) expected = {} assert result == expected, "Expected {}, got {}".format( expected, result) result = cli_utils.get_region_dict([100, 110, 250, 240], 500, ts) expected = dict(region=dict(left=100, top=110, width=250, height=240)) assert result == expected, "Expected {}, got {}".format( expected, result)
def test_get_region_dict(self): ts = large_image.getTileSource(os.path.join(TEST_DATA_DIR, 'Easy1.png')) result = cli_utils.get_region_dict([-1, -1, -1, -1], 2000, ts) expected = {} assert result == expected, "Expected {}, got {}".format( expected, result) result = cli_utils.get_region_dict([100, 110, 250, 240], 500, ts) expected = dict(region=dict(left=100, top=110, width=250, height=240)) assert result == expected, "Expected {}, got {}".format( expected, result)
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, **it_kwargs) # get tile image im_tile = tile_info['tile'][:, :, :3] # segment nuclei im_nuclei_seg_mask = detect_nuclei_kofahi(im_tile, args) # generate nuclei bounding boxes annotations obj_props = skimage.measure.regionprops(im_nuclei_seg_mask) nuclei_bbox_list = [] gx = tile_info['gx'] gy = tile_info['gy'] wfrac = tile_info['gwidth'] / np.double(tile_info['width']) hfrac = tile_info['gheight'] / np.double(tile_info['height']) for i in range(len(obj_props)): cx = obj_props[i].centroid[1] cy = obj_props[i].centroid[0] width = obj_props[i].bbox[3] - obj_props[i].bbox[1] + 1 height = obj_props[i].bbox[2] - obj_props[i].bbox[0] + 1 # convert to base pixel coords cx = gx + cx * wfrac cy = gy + cy * hfrac width *= wfrac height *= hfrac # create annotation json cur_bbox = { "type": "rectangle", "center": [cx, cy, 0], "width": width, "height": height, "rotation": 0 } nuclei_bbox_list.append(cur_bbox) return nuclei_bbox_list
def load_ROI(WSI_path, xml_file_path, magnification=20): ROI_width, ROI_height = ROI_width_and_height_on_WSI(xml_file_path) ROI_coords = ROI_coordinates(xml_file_path) left = ROI_coords[0][0] top = ROI_coords[0][1] ts = large_image.getTileSource(WSI_path) ROI, _ = ts.getRegionAtAnotherScale( sourceRegion=dict(left=left, top=top, width=ROI_width, height=ROI_height, units='base_pixels'), targetScale=dict(magnification=magnification), format=large_image.tilesource.TILE_FORMAT_NUMPY) return ROI
def sample_pixels(slide_path, magnification, sample_percent, tissue_seg_mag=1.25, min_coverage=0.1): """Generates a sampling of pixels from a whole-slide image. Useful for generating statistics or Reinhard color-normalization or adaptive deconvolution. Uses mixture modeling approach to focus sampling in tissue regions. Parameters ---------- slide_path : str path and filename of slide. magnification : double Desired magnification for sampling (defaults to native scan magnification). sample_percent : double Percentage of pixels to sample. Must be in the range [0, 1]. tissue_seg_mag: double, optional low resolution magnification at which foreground will be segmented. Default value = 1.25. min_coverage: double, optional minimum sample_percent of tile covered by tissue to be included in sampling. Ranges between [0,1). Default value = 0.1. Returns ------- Pixels : array_like A Nx3 matrix of RGB pixel values sampled from the whole-slide. See Also -------- histomicstk.preprocessing.color_normalization.reinhard, histomicstk.preprocessing.color_deconvolution.SparseColorDeconvolution """ ts = large_image.getTileSource(slide_path) # get enitre whole-silde image at low resolution scale_lres = {'magnification': tissue_seg_mag} im_lres, _ = ts.getRegion( format=large_image.tilesource.TILE_FORMAT_NUMPY, scale=scale_lres ) im_lres = im_lres[:, :, :3] # compute foreground mask of whole-slide image at low-res im_fgnd_mask_lres = simple_mask(im_lres) # generate sample pixels sample_pixels = [] scale_hres = {'magnfication': magnification} for tile in ts.tileIterator( scale=scale_hres, format=large_image.tilesource.TILE_FORMAT_NUMPY): # get current region in base_pixels rgn_hres = {'left': tile['gx'], 'top': tile['gy'], 'right': tile['gx'] + tile['gwidth'], 'bottom': tile['gy'] + tile['gheight'], 'units': 'base_pixels'} # get foreground mask for current tile at low resolution rgn_lres = ts.convertRegionScale(rgn_hres, targetScale=scale_lres, targetUnits='mag_pixels') tile_fgnd_mask_lres = \ im_fgnd_mask_lres[rgn_lres['top']:rgn_lres['bottom'], rgn_lres['left']:rgn_lres['right']] # skip tile if there is not enough foreground in the slide cur_fgnd_frac = tile_fgnd_mask_lres.mean() if np.isnan(cur_fgnd_frac) or cur_fgnd_frac <= min_coverage: continue # get current tile image im_tile = tile['tile'][:, :, :3] # get tile foreground mask at resolution of current tile tile_fgnd_mask = scipy.misc.imresize( tile_fgnd_mask_lres, im_tile.shape, interp='nearest' ) # generate linear indices of sample pixels in fgnd mask nz_ind = np.nonzero(tile_fgnd_mask.flatten())[0] sample_ind = np.random.choice(nz_ind, np.ceil(sample_percent * nz_ind.size)) # convert rgb tile image to Nx3 array tile_pix_rgb = np.reshape(im_tile, (im_tile.shape[0] * im_tile.shape[1], 3)) # add rgb triplet of sample pixels sample_pixels.append(tile_pix_rgb[sample_ind, :]) # concatenate pixel values in list try: sample_pixels = np.concatenate(sample_pixels, 0) except ValueError: print "Sampling could not identify any foreground regions." return sample_pixels