def test_operations_on_closed_handle(self): osr = OpenSlide(file_path('boxes.tiff')) props = osr.properties associated = osr.associated_images osr.close() self.assertRaises(ArgumentError, lambda: osr.read_region((0, 0), 0, (100, 100))) self.assertRaises(ArgumentError, lambda: osr.close()) self.assertRaises(ArgumentError, lambda: props['openslide.vendor']) self.assertRaises(ArgumentError, lambda: associated['label'])
def get_image_open(wsi_path): try: wsi_image = OpenSlide(wsi_path) level_used = 6 rgb_image = np.array( wsi_image.read_region((0, 0), level_used, wsi_image.level_dimensions[level_used])) wsi_image.close() except OpenSlideUnsupportedFormatError: raise ValueError( 'Exception: OpenSlideUnsupportedFormatError for %s' % wsi_path) # hsv -> 3 channel hsv = cv2.cvtColor(rgb_image, cv2.COLOR_BGR2HSV) lower_red = np.array([20, 20, 20]) upper_red = np.array([200, 200, 200]) # mask -> 1 channel mask = cv2.inRange(hsv, lower_red, upper_red) close_kernel = np.ones((20, 20), dtype=np.uint8) image_close = cv2.morphologyEx(np.array(mask), cv2.MORPH_CLOSE, close_kernel) open_kernel = np.ones((5, 5), dtype=np.uint8) image_open = cv2.morphologyEx(np.array(image_close), cv2.MORPH_OPEN, open_kernel) return image_open
def v_slide(svs_path, bound_y, queue, tile_size, start_point): """ """ logger = logging.getLogger(__name__) AVG_THRESHOLD = 170 LAYER = 0 GREEN_CHANNEL_INDEX = 1 try: svs_file = OpenSlide(svs_path) filename = os.path.basename(svs_path) x0 = start_point[0] y0 = start_point[1] pid = os.getpid() logger.debug(f'{pid}: start working...') logger.debug(f'worker {pid} working on {filename}...') while y0 < bound_y: img = svs_file.read_region((x0, y0), LAYER, (tile_size, tile_size)) green_c_avg = np.average(np.array(img)[:, :, GREEN_CHANNEL_INDEX]) if green_c_avg < AVG_THRESHOLD: img = np.array(img) img = img[:, :, 0:3] queue.put(img) y0 += tile_size logger.debug(f'{pid}: work finished.') finally: svs_file.close()
def read_wsi_tumor(wsi_path, mask_path): """ # ===================================================================================== # read WSI image and resize # Due to memory constraint, we use down sampled (4th level, 1/32 resolution) image # ====================================================================================== """ try: wsi_image = OpenSlide(wsi_path) wsi_mask = OpenSlide(mask_path) level_used = wsi_image.level_count - 1 rgb_image = np.array(wsi_image.read_region((0, 0), level_used, wsi_image.level_dimensions[level_used])) mask_level = wsi_mask.level_count - 1 mask_image = wsi_mask.read_region((0, 0), mask_level, wsi_image.level_dimensions[mask_level]) resize_factor = float(1.0 / pow(2, level_used - mask_level)) # print('resize_factor: %f' % resize_factor) mask_image = cv2.resize(np.array(mask_image), (0, 0), fx=resize_factor, fy=resize_factor) wsi_mask.close() except OpenSlideUnsupportedFormatError: print('Exception: OpenSlideUnsupportedFormatError') return None, None, None, None return wsi_image, rgb_image, mask_image, level_used
def v_slide(slp, n_y, x, y, tile_size, stepsize, x0, outdir, std): # pid = os.getpid() # print('{}: start working'.format(pid)) slide = OpenSlide(slp) imloc = [] imlist = [] y0 = 0 target_x = x0 * stepsize image_x = target_x + x while y0 < n_y: target_y = y0 * stepsize image_y = target_y + y img = slide.read_region((image_x, image_y), 0, (tile_size, tile_size)) wscore = bgcheck(img, tile_size) if wscore < 0.3: img = img.resize((tile_size, tile_size)) # try: # img = normalization(img, std) # except staintools.miscellaneous.exceptions.TissueMaskException: # print("Empty tissue mask computed: region_x-{}-y-{}".format(image_x, image_y)) # y0 += 1 # continue # except: # print('An error occurred: region_x-{}-y-{}'.format(image_x, image_y)) # y0 += 1 # continue img.save(outdir + "/region_x-{}-y-{}.png".format(image_x, image_y)) strr = outdir + "/region_x-{}-y-{}.png".format(image_x, image_y) imloc.append([x0, y0, image_x, image_y, target_x, target_y, strr]) # imlist.append(np.array(img)[:, :, :3]) y0 += 1 slide.close() return imloc, imlist
def read_wsi_tumor(wsi_path, mask_path): """ # ===================================================================================== # read WSI image and resize # Due to memory constraint, we use down sampled (4th level, 1/32 resolution) image # ====================================================================================== """ try: wsi_image = OpenSlide(wsi_path) wsi_mask = OpenSlide(mask_path) level_used = wsi_image.level_count - 1 rgb_image = np.array( wsi_image.read_region((0, 0), level_used, wsi_image.level_dimensions[level_used])) mask_level = wsi_mask.level_count - 1 tumor_gt_mask = wsi_mask.read_region( (0, 0), mask_level, wsi_image.level_dimensions[mask_level]) resize_factor = float(1.0 / pow(2, level_used - mask_level)) # print('resize_factor: %f' % resize_factor) tumor_gt_mask = cv2.resize(np.array(tumor_gt_mask), (0, 0), fx=resize_factor, fy=resize_factor) wsi_mask.close() except OpenSlideUnsupportedFormatError: print('Exception: OpenSlideUnsupportedFormatError') return None, None, None, None return wsi_image, rgb_image, wsi_mask, tumor_gt_mask, level_used
def __init__(self, filepath, num_of_imgs=0, resume_from=None): super(MaskTestset, self).__init__() self.filepath = filepath self.patch_size = patch_size if os.path.isdir(self.filepath): self.images_grid = [] # list ( n * 2 ) self.imageIDX = [] # list ( n ) self.image_size = [] # list ( ? ) self.files = [ f for f in sorted(os.listdir(self.filepath)) if os.path.isfile(os.path.join(self.filepath, f)) ] if resume_from is not None: self.files[:self.files.index(resume_from)] = [] for i, file in enumerate(tqdm(self.files, desc="loading images")): if num_of_imgs != 0 and i == num_of_imgs: break if file.endswith((".svs", ".tiff")): self.mode = "WSI" slide = OpenSlide(os.path.join(self.filepath, file)) patches_grid = self.sample_patches(slide.dimensions, self.patch_size - 16) self.images_grid.extend(patches_grid) self.imageIDX.extend([i] * len(patches_grid)) self.image_size.append(slide.dimensions) slide.close() elif file.endswith((".jpg", ".png")): self.mode = "ROI" img = io.imread(os.path.join(self.filepath, file)).astype(np.uint8) patches_grid = self.sample_patches(img.shape, self.patch_size - 16) self.images_grid.extend(patches_grid) self.imageIDX.extend([i] * len(patches_grid)) self.image_size.append(img.shape) else: raise FileNotFoundError("Invalid data directory.") elif (os.path.exists(self.filepath) and os.path.isfile(self.filepath) and self.filepath.endswith(("h5", "hdf5"))): self.mode = "patch" self.images = [] f = h5py.File(self.filepath, 'r') for i, img in enumerate(f['x']): if num_of_imgs != 0 and i == num_of_imgs: break self.images.append(img) else: raise FileNotFoundError("Invalid data directory.") self.transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ])
def process_zip(path, project_name): proj_base_dir = os.environ.get('PROJECT_BASE_DIR', '/mnt/WORK/temp/') data = {'project_name': project_name, 'images': []} if proj_base_dir is None or not os.path.exists(path): return False, None project_dir = os.path.join(proj_base_dir, project_name) save_dir = os.path.join(project_dir, 'wsi') thumbnails = os.path.join(project_dir, 'thumbnails') os.makedirs(save_dir) os.makedirs(thumbnails) with zipfile.ZipFile(path, 'r') as zip_ref: zip_ref.extractall(save_dir) for wsi in os.listdir(save_dir): slide = OpenSlide(os.path.join(save_dir, wsi)) fname = _save_thumbnails(slide, thumbnails, wsi) temp = {} temp['tiles'] = _extract_tiles(slide) temp['name'] = wsi temp['project_name'] = project_name temp['meta'] = {} temp['thumbnail'] = fname data['images'].append(temp) slide.close() return True, data
def v_slide(slp, n_y, x, y, tile_size, stepsize, x0, outdir, level, dp, std): # pid = os.getpid() # print('{}: start working'.format(pid)) slide = OpenSlide(slp) imloc = [] y0 = 0 target_x = x0 * stepsize image_x = (target_x + x)*(4**level) while y0 < n_y: target_y = y0 * stepsize image_y = (target_y + y)*(4**level) img = slide.read_region((image_x, image_y), level, (tile_size, tile_size)) wscore = bgcheck(img, tile_size) if wscore < 0.7: img = img.resize((299, 299)) img = normalization(img, std) if dp: img.save(outdir + "/region_x-{}-y-{}_{}.png".format(image_x, image_y, str(dp))) strr = outdir + "/region_x-{}-y-{}_{}.png".format(image_x, image_y, str(dp)) else: img.save(outdir + "/region_x-{}-y-{}.png".format(image_x, image_y)) strr = outdir + "/region_x-{}-y-{}.png".format(image_x, image_y) imloc.append([x0, y0, image_x, image_y, strr]) y0 += 1 slide.close() return imloc
def read_wsi_normal(wsi_normal_path, resolution_level=None): wsi_normal = OpenSlide(wsi_normal_path) if resolution_level is None: resolution_level = wsi_normal.level_count - 1 wsi_normal_sample = wsi_normal.read_region( (0, 0), resolution_level, wsi_normal.level_dimensions[resolution_level]) wsi_normal.close() return wsi_normal_sample
def get_image_metadata(): """ Read slide and process :return: """ p = Path(os.path.join(SLIDE_DIR, (CASE_ID + '.svs'))) osr = OpenSlide(str(p)) # props = osr.properties # props.__getitem__('openslide.level[0].width') # props.__getitem__('openslide.level[0].height') image_width = osr.dimensions[0] image_height = osr.dimensions[1] osr.close() return image_width, image_height
def v_slide(params): """ """ paths = Paths() try: try: scn_file = OpenSlide(paths.slice_80) except OpenSlideUnsupportedFormatError: logging.error("OpenSlideUnsupportedFormatError!") return except OpenSlideError: logging.error("OpenSlideError!") return start_point = params["start_point"] x0 = start_point[0] y0 = start_point[1] bound_y = params["bound_y"] tile_path = params["tile_path"] save_tiles = params["save_tiles"] q = params["queue"] AVG_THRESHOLD = 170 pid = os.getpid() data = {} while y0 < bound_y: img = scn_file.read_region((x0, y0), 0, (299, 299)) green_c_avg = np.average(np.array(img)[:, :, 1]) if green_c_avg < AVG_THRESHOLD: sufix = "_" + str(x0) + "_" + \ str(y0) + ".png" file_name = "scn80" + sufix img = np.array(img) img = img[:, :, 0:3] data['pred'] = img data['xlabel'] = np.array([x0]) data['ylabel'] = np.array([y0]) q.put(dict(data)) if save_tiles: img.save(os.path.join(tile_path, file_name)) y0 += 150 return pid finally: scn_file.close()
def v_slide(slp, s_id, n_y, x, y, tile_size, step_size, out_dir, x0, std=np.asarray([])): # pid = os.getpid() # print('{}: start working'.format(pid)) slide = OpenSlide(slp) imloc = [] imlist = [] y0 = 0 target_x = x0 * step_size image_x = target_x + x while y0 < n_y: target_y = y0 * step_size image_y = target_y + y img = slide.read_region((image_x, image_y), 0, (tile_size, tile_size)) wscore = bgcheck(img) # output png files if an output directory is given if wscore < 0.8: tile_name = s_id + '_tile_x_{}_y_{}.png'.format( str(image_x).zfill(6), str(image_y).zfill(6)) if std.any(): img = normalization(img, std) if out_dir: img.save(out_dir + '/' + tile_name) # append image data and descriptions to list imlist.append(np.asarray(img)[:, :, :3].astype(np.uint8)) imloc.append([ s_id, str(x0).zfill(3), str(y0).zfill(3), str(target_x).zfill(5), str(target_y).zfill(5), tile_name ]) y0 += 1 slide.close() return imloc, imlist
def v_slide(slp, n_y, x, y, tile_size, stepsize, x0): # pid = os.getpid() # print('{}: start working'.format(pid)) slide = OpenSlide(slp) imloc = [] imlist = [] y0 = 0 target_x = x0 * stepsize image_x = target_x + x while y0 < n_y: target_y = y0 * stepsize image_y = target_y + y img = slide.read_region((image_x, image_y), 0, (tile_size, tile_size)) wscore = bgcheck(img) if wscore < 0.5: imloc.append([x0, y0, target_x, target_y]) imlist.append(np.array(img)[:, :, :3]) y0 += 1 slide.close() return imloc, imlist
def read_wsi_tumor(wsi_tumor_path, wsi_mask_path, resolution_level=None): try: wsi_tumor = OpenSlide(wsi_tumor_path) if resolution_level is None: resolution_level = wsi_tumor.level_count - 1 wsi_tumor_sample = wsi_tumor.read_region( (0, 0), resolution_level, wsi_tumor.level_dimensions[resolution_level]) wsi_tumor.close() wsi_tumor_mask = OpenSlide(wsi_mask_path) wsi_tumor_mask_sample = wsi_tumor_mask.read_region( (0, 0), resolution_level, wsi_tumor_mask.level_dimensions[resolution_level]) wsi_tumor_mask.close() except Exception as e: return None, None return wsi_tumor_sample, wsi_tumor_mask_sample
def generate_thumbnail(image_filepath, thumbnail_filepath): ''' Generates a thumbnail from the specified image. ''' # open image with OpenSlide library image_file = OpenSlide(image_filepath) # extract image dimensions image_dims = image_file.dimensions # make thumbnail 100 times smaller thumb_dims = tuple( (x/100 for x in image_dims) ) # create thumbnail thumb_file = image_file.get_thumbnail(thumb_dims) # save file with desired path, format thumb_file.save(thumbnail_filepath, "png") # cleanup image_file.close()
def napari_get_reader(path): """A basic implementation of the napari_get_reader hook specification. Parameters ---------- path : str or list of str Path to file, or list of paths. Returns ------- function or None If the path is a recognized format, return a function that accepts the same path or list of paths, and returns a list of layer data tuples. """ if isinstance(path, list): # Don't handle multiple paths return None if OpenSlide.detect_format(path) is None: return None try: slide = OpenSlide(path) except OpenSlideUnsupportedFormatError: return None description = slide.properties.get(PROPERTY_NAME_COMMENT) # Don't try to handle OME-TIFF # https://github.com/cgohlke/tifffile/blob/b346e3bd7de81de512a6715b01124c8f6d60a707/tifffile/tifffile.py#L5781 if description and description[-4:] == "OME>": return None # Don't try to handle files that aren't multiscale. if slide.level_count == 1: return None slide.close() return reader_function
def glue_to_one_picture_from_coord(url, regions, window=200, K=16, layer=0): block = int(np.sqrt(K)) slide = OpenSlide(url) scale = slide.level_downsamples[2] / slide.level_downsamples[layer] image = np.full( (int(block * window * scale), int(block * window * scale), 3), 255, dtype=np.uint8) for i, itr in enumerate(regions): r, c = divmod(i, block) ws = int(window * scale) patch = np.asarray( slide.read_region((int(itr[0] * slide.level_downsamples[2]), int(itr[1] * slide.level_downsamples[2])), layer, (ws, ws)))[:, :, :3] r0 = int(r * window * scale) r1 = r0 + ws c0 = int(c * window * scale) c1 = c0 + ws image[r0:r1, c0:c1, :] = patch slide.close() return image
def glue_to_one_picture_from_coord(url, regions, window=200, K=16, layer=0): block = int(np.sqrt(K)) slide = OpenSlide(url) scale = slide.level_downsamples[2] / slide.level_downsamples[layer] image = np.full((int(block * window * scale), int(block * window * scale), 3), 255, dtype=np.uint8) for i, itr in enumerate(regions): r, c = divmod(i, block) ws = int(window * scale) xs = int(itr[0] * slide.level_downsamples[2]) ys = int(itr[1] * slide.level_downsamples[2]) # patch = np.asarray(slide.read_region((xs, ys), layer, (ws, ws)))[:, :, :3] # 環境によっては以下の対応が必要 wo = int(window * slide.level_downsamples[2]) patch = np.asarray(slide.read_region((xs, ys), 0, (wo, wo)))[:, :, :3] # layer != 0 の場合はリサイズ if layer > 0: patch = cv2.resize(patch, (ws, ws)) r0 = int(r * window * scale) r1 = r0 + ws c0 = int(c * window * scale) c1 = c0 + ws image[r0:r1, c0:c1, :] = patch slide.close() return image
def get_a_patch(self, idx): if self.mode == "WSI": image_file = os.path.join(self.filepath, self.files[self.imageIDX[idx]]) slide = OpenSlide(image_file) x, y = self.images_grid[idx] patch = np.asarray( slide.read_region((x, y), level=0, size=tuple(self.patch_size)).convert('RGB')) slide.close() return patch, self.imageIDX[idx] elif self.mode == "ROI": image_file = os.path.join(self.filepath, self.files[self.imageIDX[idx]]) image = io.imread(image_file).astype(np.uint8) x, y = self.images_grid[idx] patch = image[x:x + self.patch_size[0], y:y + self.patch_size[1]] return patch, self.imageIDX[idx] else: patch = self.images[idx] return [patch]
def glue_to_one_picture_from_coord_lowlayer(url, regions, window=200, K=16, layer=1): block = int(np.sqrt(K)) slide = OpenSlide(url) scale = slide.level_downsamples[2] / slide.level_downsamples[layer] slide.close() slide = skimage.io.MultiImage(url)[layer] slide = np.array(slide) image = np.full((int(block * window * scale), int(block * window * scale), 3), 255, dtype=np.uint8) # print(coordinates) for i, itr in enumerate(regions): r, c = divmod(i, block) ws = int(window * scale) x0 = int(itr[0] * scale) x1 = x0 + ws y0 = int(itr[1] * scale) y1 = y0 + ws buf = slide[y0:y1, x0:x1, :] h, w = buf.shape[:2] if h < ws or w < ws: dx = max(0, ws - w) dy = max(0, ws - h) buf = np.pad(buf, [[0, dy], [0, dx], [0, 0]], 'constant', constant_values=(255, 255)) image[r * ws:(r+1) * ws, c * ws:(c+1) * ws, :] = buf return image
class SlideReader: handle = None suffix = None attrs = None _path = None def __init__(self): pass @property def path(self): return self._path def open(self, path): if path == self._path or path is None: return else: self.close() try: self.suffix = os.path.splitext(path)[-1] if self.suffix == ".sdpc": self.handle = Sdpc() self.handle.open(path) elif self.suffix == ".srp": self.handle = Srp() self.handle.open(path) elif self.suffix == ".svs" or self.suffix == ".mrxs": self.handle = OpenSlide(path) else: raise ValueError("File type: {} is not supported.".format(self.suffix)) self._path = path except Exception as e: logger.error(f'{e}\nNote: some system requires absolute path for wsi image.') self.close() def close(self): self._path = None if self.handle is None: return self.handle.close() self.handle = None def get_attrs(self): return get_attrs(self) def get_tile(self, location: tuple, size: tuple, level: int): """ get tile from slide :param location: (x, y) at level 0 :param size: (w, h) at level 0 :param level: read in level :return: RGB img array """ if level == 0: return self.get_tile_for_level0(location, size) # ensure the attrs had been created if self.attrs: pass else: self.get_attrs() # main operations for reading tile tile_size = mpp_transformer(size, self.attrs["mpp"], self.attrs["mpp"] * (self.attrs["level_ratio"] ** level)) if self.suffix in ['.sdpc', '.srp']: # parts exceed right and bottom is filled with (255, 255, 255) location = mpp_transformer(location, self.attrs["mpp"], self.attrs["mpp"] * (self.attrs["level_ratio"] ** level)) tile = self.handle.getTile(level, location[0], location[1], tile_size[0], tile_size[1]) elif self.suffix in ['.svs', '.mrxs']: # parts exceed right and bottom is filled with (0, 0, 0) tile = np.array(self.handle.read_region(location, level, tile_size).convert('RGB')) return tile def get_tile_for_level0(self, location: tuple, size: tuple): """ get tile from slide in level 0 :param location: (x, y) at level 0 :param size: (w, h) at level 0 :return: RGB img array """ # main operations for reading tile if self.suffix in ['.sdpc', '.srp']: # parts exceed right and bottom is filled with (255, 255, 255) tile = self.handle.getTile(0, location[0], location[1], size[0], size[1]) elif self.suffix in ['.svs', '.mrxs']: # parts exceed right and bottom is filled with (0, 0, 0) tile = np.array(self.handle.read_region(location, 0, size).convert('RGB')) return tile def __del__(self): self.close()
for prob_path, wsi_path in zip(prob_paths, wsi_paths): dir_name = wsi_path.split('/')[-1].split('.')[0] dir_name = utils.TEST_RESULT + '/' + dir_name if os.path.exists(dir_name): shutil.rmtree(dir_name) os.mkdir(dir_name) wsi_img = OpenSlide(wsi_path) dispaly_level = int( input('Level from 0 to %d you want to dispaly:' % (wsi_img.level_count - 1))) rgb_img = np.array( wsi_img.read_region( (0, 0), dispaly_level, wsi_img.level_dimensions[dispaly_level]))[:, :, :3].copy() wsi_img.close() plt.imsave(dir_name + '/rgb.png', rgb_img) image_open, mask = generate_image_open(rgb_img.copy()) plt.imsave(dir_name + '/open.png', image_open, cmap=plt.cm.gray) plt.imsave(dir_name + '/mask.png', mask, cmap=plt.cm.gray) prob_img = plt.imread(prob_path) plt.imshow(prob_img, cmap=plt.cm.gray) plt.imsave(dir_name + '/prob_img.png', prob_img, cmap=plt.cm.gray) # features, region_coors = extract_features(heatmap,image_open) # img_contour = image_open.copy() # cv2.drawContours(img_contour,region_coors,-1,(255,0,0),2) # plt.imshow(img_contour) # plt.imsave(dir_name + '/contour.jpg',img_contour)
class WSI(object): """ # ================================ # Class to annotate WSIs with ROIs # ================================ """ index = 0 negative_patch_index = 0 positive_patch_index = 0 wsi_paths = [] mask_paths = [] def_level = 7 level_extract = 2 key = 0 def extract_patches_mask(self, bounding_boxes): """ Extract positive patches targeting annotated tumor region Save extracted patches to desk as .png image files :param bounding_boxes: list of bounding boxes corresponds to tumor regions :return: """ mag_factor = pow(2, self.level_used) print('No. of ROIs to extract patches from: %d' % len(bounding_boxes)) for i, bounding_box in enumerate(bounding_boxes): b_x_start = int(bounding_box[0]) * mag_factor b_y_start = int(bounding_box[1]) * mag_factor b_x_end = (int(bounding_box[0]) + int(bounding_box[2])) * mag_factor b_y_end = (int(bounding_box[1]) + int(bounding_box[3])) * mag_factor X = np.random.random_integers(b_x_start, high=b_x_end, size=500) Y = np.random.random_integers(b_y_start, high=b_y_end, size=500) # X = np.arange(b_x_start, b_x_end-256, 5) # Y = np.arange(b_y_start, b_y_end-256, 5) for x, y in zip(X, Y): mask = self.mask_image.read_region((x, y), 0, (PATCH_SIZE, PATCH_SIZE)) mask_gt = np.array(mask) mask_gt = cv2.cvtColor(mask_gt, cv2.COLOR_BGR2GRAY) white_pixel_cnt_gt = cv2.countNonZero(mask_gt) if white_pixel_cnt_gt > ((PATCH_SIZE * PATCH_SIZE) * 0.90): # mask = Image.fromarray(mask) patch = self.wsi_image.read_region( (x, y), 0, (PATCH_SIZE, PATCH_SIZE)) patch.save( PROCESSED_PATCHES_FROM_USE_MASK_POSITIVE_PATH + PATCH_TUMOR_PREFIX + str(PATCH_SIZE) + '_' + str(self.positive_patch_index), 'PNG') self.positive_patch_index += 1 patch.close() mask.close() def extract_patches_normal(self, bounding_boxes): """ Extract negative patches from Normal WSIs Save extracted patches to desk as .png image files :param bounding_boxes: list of bounding boxes corresponds to detected ROIs :return: """ mag_factor = pow(2, self.level_used) mag_factor2 = pow(2, self.level_extract) bbx_co = [] bby_co = [] print('No. of ROIs to extract patches from: %d' % len(bounding_boxes)) for i, bounding_box in enumerate(bounding_boxes): # sometimes the bounding boxes annotate a very small area not in the ROI if (bounding_box[2] * bounding_box[3]) < 2500: continue b_x_start = int(bounding_box[0]) * mag_factor b_y_start = int(bounding_box[1]) * mag_factor b_x_end = (int(bounding_box[0]) + int(bounding_box[2])) * mag_factor b_y_end = (int(bounding_box[1]) + int(bounding_box[3])) * mag_factor # X = np.arange(b_x_start, b_x_end-256, 5) # Y = np.arange(b_y_start, b_y_end-256, 5) h = int(bounding_box[2]) * mag_factor w = int(bounding_box[3]) * mag_factor print("Size of bounding box = %s" % h + " by %s" % w) tumoridx = 0 ntumoridx = 0 patchidx = 0 for y_left in range(b_y_start, b_y_end, PATCH_SIZE * mag_factor2): bby_co.append(range(y_left, y_left + PATCH_SIZE * mag_factor2)) for x_left in range(b_x_start, b_x_end, PATCH_SIZE * mag_factor2): if x_left in bbx_co and y_left in bby_co: print("Skipping double Bounding Box %s " % self.cur_wsi_path) continue bbx_co.append( range(x_left, x_left + PATCH_SIZE * mag_factor2)) patch = self.wsi_image.read_region( (x_left, y_left), 2, (PATCH_SIZE, PATCH_SIZE)) mask = np.zeros((PATCH_SIZE, PATCH_SIZE), dtype=np.uint8) mask = Image.fromarray(mask) # mask = self.mask_image.read_region((x_left, y_left), 2, (PATCH_SIZE, PATCH_SIZE)) _std = ImageStat.Stat(patch).stddev patch_array = np.array(patch) # thresholding stddev for patch extraction patchidx += 1 if (sum(_std[:3]) / len(_std[:3])) < 15: continue patch_hsv = cv2.cvtColor(patch_array, cv2.COLOR_BGR2HSV) # [20, 20, 20] lower_red = np.array([0, 0, 0]) # [255, 255, 255] upper_red = np.array([200, 200, 200]) mask_patch = cv2.inRange(patch_hsv, lower_red, upper_red) white_pixel_cnt = cv2.countNonZero(mask_patch) if white_pixel_cnt > ((PATCH_SIZE * PATCH_SIZE) * 0.04): # mask = Image.fromarray(mask) if self.negative_patch_index % 2 == 0: patch.save( PROCESSED_PATCHES_POSITIVE_PATH + PATCH_NORMAL_PREFIX + str(PATCH_SIZE) + '_' + str(self.negative_patch_index) + '.png', 'PNG') mask.save( PROCESSED_PATCHES_POSITIVE_MASK_PATH + 'mask_' + PATCH_NORMAL_PREFIX + str(PATCH_SIZE) + '_' + str(self.negative_patch_index) + '.png', 'PNG') self.negative_patch_index += 1 ntumoridx += 1 patch.close() print( "Processed patches in bounding box %s of %s :" % (i, self.cur_wsi_path), "%s" % patchidx, " negative: %s" % ntumoridx) def extract_patches_tumor(self, bounding_boxes): """ Extract both, negative patches from Normal area and positive patches from Tumor area Save extracted patches to desk as .png image files :param bounding_boxes: list of bounding boxes corresponds to detected ROIs :return: """ mag_factor = pow(2, self.level_used) mag_factor2 = pow(2, self.level_extract) bbx_co = [] bby_co = [] print('No. of ROIs to extract patches from: %d' % len(bounding_boxes)) for i, bounding_box in enumerate(bounding_boxes): # sometimes the bounding boxes annotate a very small area not in the ROI if (bounding_box[2] * bounding_box[3]) < 2500: continue b_x_start = int(bounding_box[0]) * mag_factor b_y_start = int(bounding_box[1]) * mag_factor b_x_end = (int(bounding_box[0]) + int(bounding_box[2])) * mag_factor b_y_end = (int(bounding_box[1]) + int(bounding_box[3])) * mag_factor # pdb.set_trace() h = int(bounding_box[2]) * mag_factor w = int(bounding_box[3]) * mag_factor print("Size of bounding box = %s" % h + " by %s" % w) tumoridx = 0 ntumoridx = 0 patchidx = 0 for y_left in range(b_y_start, b_y_end, PATCH_SIZE * mag_factor2): bby_co.append(range(y_left, y_left + PATCH_SIZE * mag_factor2)) for x_left in range(b_x_start, b_x_end, PATCH_SIZE * mag_factor2): if x_left in bbx_co and y_left in bby_co: print("Skipping double Bounding Box %s " % self.cur_wsi_path) continue bbx_co.append( range(x_left, x_left + PATCH_SIZE * mag_factor2)) patch = self.wsi_image.read_region( (x_left, y_left), 2, (PATCH_SIZE, PATCH_SIZE)) mask = self.mask_image.read_region( (x_left, y_left), 2, (PATCH_SIZE, PATCH_SIZE)) # pdb.set_trace() _std = ImageStat.Stat(patch).stddev # thresholding stddev for patch extraction patchidx += 1 if (sum(_std[:3]) / len(_std[:3])) < 15: continue mask_gt = np.array(mask) # mask_gt = cv2.cvtColor(mask_gt, cv2.COLOR_BGR2GRAY) mask_gt = cv2.cvtColor(mask_gt, cv2.COLOR_BGR2GRAY) patch_array = np.array(patch) white_pixel_cnt_gt = cv2.countNonZero(mask_gt) if white_pixel_cnt_gt == 0: # mask_gt does not contain tumor area pass # patch_hsv = cv2.cvtColor(patch_array, cv2.COLOR_BGR2HSV) # lower_red = np.array([0, 0, 0]) # upper_red = np.array([200, 200, 220]) # mask_patch = cv2.inRange(patch_hsv, lower_red, upper_red) # white_pixel_cnt = cv2.countNonZero(mask_patch) # if white_pixel_cnt > ((PATCH_SIZE * PATCH_SIZE) * 0.50): # # mask = Image.fromarray(mask) # if self.negative_patch_index % 4 == 0: # patch.save(PROCESSED_PATCHES_POSITIVE_PATH + PATCH_NORMAL_PREFIX + str(PATCH_SIZE) + '_' + # str(self.negative_patch_index)+ '.png', 'PNG') # mask.save(PROCESSED_PATCHES_POSITIVE_MASK_PATH + 'mask_' + PATCH_NORMAL_PREFIX + str(PATCH_SIZE) + '_' + # str(self.negative_patch_index)+ '.png','PNG') # self.negative_patch_index += 1 # ntumoridx += 1 else: # mask_gt contains tumor area if white_pixel_cnt_gt >= ( (PATCH_SIZE * PATCH_SIZE) * 0.005): patch_hsv = cv2.cvtColor(patch_array, cv2.COLOR_BGR2HSV) lower_red = np.array([0, 0, 0]) upper_red = np.array([200, 200, 220]) mask_patch = cv2.inRange(patch_hsv, lower_red, upper_red) white_pixel_cnt = cv2.countNonZero(mask_patch) # if white_pixel_cnt > ((PATCH_SIZE * PATCH_SIZE) * 0.10): # if self.positive_patch_index % 2 == 0: patch.save( PROCESSED_PATCHES_POSITIVE_PATH + PATCH_TUMOR_PREFIX + str(PATCH_SIZE) + '_' + str(self.positive_patch_index) + '.png', 'PNG') mask.save( PROCESSED_PATCHES_POSITIVE_MASK_PATH + 'mask_' + PATCH_TUMOR_PREFIX + str(PATCH_SIZE) + '_' + str(self.positive_patch_index) + '.png', 'PNG') self.positive_patch_index += 1 tumoridx += 1 patch.close() mask.close() print("Processed patches in bounding box %s " % i, "%s" % patchidx, " positive: %s " % tumoridx, " negative: %s" % ntumoridx) def read_wsi_mask(self, wsi_path, mask_path): try: self.cur_wsi_path = wsi_path self.wsi_image = OpenSlide(wsi_path) self.mask_image = OpenSlide(mask_path) self.level_used = min(self.def_level, self.wsi_image.level_count - 1, self.mask_image.level_count - 1) self.mask_pil = self.mask_image.read_region( (0, 0), self.level_used, self.mask_image.level_dimensions[self.level_used]) self.mask = np.array(self.mask_pil) except OpenSlideUnsupportedFormatError: print('Exception: OpenSlideUnsupportedFormatError') return False return True def read_wsi_normal(self, wsi_path): """ # ===================================================================================== # read WSI image and resize # Due to memory constraint, we use down sampled (4th level, 1/32 resolution) image # ====================================================================================== """ try: self.cur_wsi_path = wsi_path self.wsi_image = OpenSlide(wsi_path) self.level_used = min(self.def_level, self.wsi_image.level_count - 1) self.rgb_image_pil = self.wsi_image.read_region( (0, 0), self.level_used, self.wsi_image.level_dimensions[self.level_used]) self.rgb_image = np.array(self.rgb_image_pil) except OpenSlideUnsupportedFormatError: print('Exception: OpenSlideUnsupportedFormatError') return False return True def read_wsi_tumor(self, wsi_path, mask_path): """ # ===================================================================================== i # read WSI image and resize # Due to memory constraint, we use down sampled (4th level, 1/32 resolution) image # ====================================================================================== """ try: self.cur_wsi_path = wsi_path self.wsi_image = OpenSlide(wsi_path) self.mask_image = OpenSlide(mask_path) self.level_used = min(self.def_level, self.wsi_image.level_count - 1, self.mask_image.level_count - 1) # print(self.level_used) self.rgb_image_pil = self.wsi_image.read_region( (0, 0), self.level_used, self.wsi_image.level_dimensions[self.level_used]) self.rgb_image = np.array(self.rgb_image_pil) except OpenSlideUnsupportedFormatError: print('Exception: OpenSlideUnsupportedFormatError') return False return True def find_roi_n_extract_patches_mask(self): mask = cv2.cvtColor(self.mask, cv2.COLOR_BGR2GRAY) contour_mask, bounding_boxes = self.get_image_contours_mask( np.array(mask), np.array(self.mask)) # contour_mask = cv2.resize(contour_mask, (0, 0), fx=0.40, fy=0.40) # cv2.imshow('contour_mask', np.array(contour_mask)) self.mask_pil.close() self.extract_patches_mask(bounding_boxes) self.wsi_image.close() self.mask_image.close() def find_roi_n_extract_patches_normal(self): hsv = cv2.cvtColor(self.rgb_image, cv2.COLOR_BGR2HSV) # [20, 20, 20] lower_red = np.array([20, 50, 20]) # [255, 255, 255] upper_red = np.array([200, 150, 200]) mask = cv2.inRange(hsv, lower_red, upper_red) # (50, 50) close_kernel = np.ones((25, 25), dtype=np.uint8) image_close = Image.fromarray( cv2.morphologyEx(np.array(mask), cv2.MORPH_CLOSE, close_kernel)) # (30, 30) open_kernel = np.ones((30, 30), dtype=np.uint8) image_open = Image.fromarray( cv2.morphologyEx(np.array(image_close), cv2.MORPH_OPEN, open_kernel)) contour_rgb, bounding_boxes = self.get_image_contours_normal( np.array(image_open), self.rgb_image) # contour_rgb = cv2.resize(contour_rgb, (0, 0), fx=0.40, fy=0.40) # cv2.imshow('contour_rgb', np.array(contour_rgb)) self.rgb_image_pil.close() self.extract_patches_normal(bounding_boxes) self.wsi_image.close() def find_roi_n_extract_patches_tumor(self): hsv = cv2.cvtColor(self.rgb_image, cv2.COLOR_BGR2HSV) lower_red = np.array([20, 20, 20]) upper_red = np.array([255, 255, 255]) mask = cv2.inRange(hsv, lower_red, upper_red) # (50, 50) close_kernel = np.ones((50, 50), dtype=np.uint8) image_close = Image.fromarray( cv2.morphologyEx(np.array(mask), cv2.MORPH_CLOSE, close_kernel)) # (30, 30) open_kernel = np.ones((30, 30), dtype=np.uint8) image_open = Image.fromarray( cv2.morphologyEx(np.array(image_close), cv2.MORPH_OPEN, open_kernel)) contour_rgb, bounding_boxes = self.get_image_contours_tumor( np.array(image_open), self.rgb_image) # pdb.set_trace() # Image.fromarray(np.array(contour_rgb)).show() # pdb.set_trace() # contour_rgb = cv2.resize(contour_rgb, (0, 0), fx=0.40, fy=0.40) # cv2.imshow('contour_rgb', np.array(contour_rgb)) self.rgb_image_pil.close() self.extract_patches_tumor(bounding_boxes) self.wsi_image.close() self.mask_image.close() @staticmethod def get_image_contours_mask(cont_img, mask_img): contours, _ = cv2.findContours(cont_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) bounding_boxes = [cv2.boundingRect(c) for c in contours] contours_mask_image_array = np.array(mask_img) line_color = (255, 0, 0) # blue color code cv2.drawContours(contours_mask_image_array, contours, -1, line_color, 1) return contours_mask_image_array, bounding_boxes @staticmethod def get_image_contours_normal(cont_img, rgb_image): contours, _ = cv2.findContours(cont_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) bounding_boxes = [cv2.boundingRect(c) for c in contours] contours_rgb_image_array = np.array(rgb_image) line_color = (255, 0, 0) # blue color code cv2.drawContours(contours_rgb_image_array, contours, -1, line_color, 3) return contours_rgb_image_array, bounding_boxes @staticmethod def get_image_contours_tumor(cont_img, rgb_image): contours, _ = cv2.findContours(cont_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) bounding_boxes = [cv2.boundingRect(c) for c in contours] contours_rgb_image_array = np.array(rgb_image) line_color = (255, 0, 0) # blue color code cv2.drawContours(contours_rgb_image_array, contours, -1, line_color, 3) # cv2.drawContours(mask_image, contours_mask, -1, line_color, 3) return contours_rgb_image_array, bounding_boxes def wait(self): self.key = cv2.waitKey(0) & 0xFF print('key: %d' % self.key) if self.key == 27: # escape return False elif self.key == 81: # <- (prev) self.index -= 1 if self.index < 0: self.index = len(self.wsi_paths) - 1 elif self.key == 83: # -> (next) self.index += 1 if self.index >= len(self.wsi_paths): self.index = 0 return True
class WSIData: def __init__( self, tif_path, centre, is_excluded, resource_group, label_tif_path=None, label_xml_path=None, patient=None, ): self._wsi_slide = OpenSlide(str(tif_path)) self._tif_path = tif_path self._is_excluded = is_excluded self._label_tif_path = label_tif_path self._label_xml_path = label_xml_path self._label_slide = None self._patient = patient self._resource_group = resource_group if not isinstance(centre, _StainNormalizer.Centre): raise TypeError('centre must be an instance of {}.Centre.'.format( self.__class__.__name__)) self._centre = centre self._name = tif_path.stem def _get_label_slide(self): if self._label_slide is None and self._label_tif_path is not None: self._label_slide = (self._label_tif_path and OpenSlide(str(self._label_tif_path))) return self._label_slide @staticmethod def _threshold_sv(img_np): '''Performs thresholding on the WSI image to extract the tissue region. RGB image is converted to HSV color space. The H and S channels are then thresholded via Otsu's Threshold, then combined via bitwise AND. Morphological transormation was applied by removing small holes and regions, and was filtered using median blur. Parameters ---------- img_np: np.uint8[H, W, 3] np.array RGB WSI as an np.array image Returns ------- bool[H, W] np.array ROI mask of the WSI. ''' img_hsv = rgb2hsv(img_np) # channel_h = img_hsv[:, :, 0] channel_s = img_hsv[:, :, 1] channel_v = img_hsv[:, :, 2] # thresh_h = threshold_otsu(channel_h) thresh_s = threshold_otsu(channel_s) thresh_v = threshold_otsu(channel_v) # binary_h = channel_h > thresh_h binary_s = channel_s > min(thresh_s, 0.1) binary_v = channel_s > min(thresh_v, 0.2) # binary = np.bitwise_and(binary_h, binary_s) binary = np.bitwise_and(binary_s, binary_v) binary = morphology.remove_small_objects(binary, _SMALL_OBJECT_AREA) binary = morphology.remove_small_holes(binary, _SMALL_HOLE_AREA) binary = median(binary, morphology.disk(_MEDIAN_DISK)) return binary.astype(bool) @staticmethod def _threshold_hs(img_np): '''Performs thresholding on the WSI image to extract the tissue region. RGB image is converted to HSV color space. The H and S channels are then thresholded via Otsu's Threshold, then combined via bitwise AND. Morphological transormation was applied by removing small holes and regions, and was filtered using median blur. Parameters ---------- img_np: np.uint8[H, W, 3] np.array RGB WSI as an np.array image Returns ------- bool[H, W] np.array ROI mask of the WSI. ''' img_hsv = rgb2hsv(img_np) channel_h = img_hsv[:, :, 0] channel_s = img_hsv[:, :, 1] thresh_h = threshold_otsu(channel_h) thresh_s = threshold_otsu(channel_s) binary_h = channel_h > thresh_h binary_s = channel_s > thresh_s binary = np.bitwise_and(binary_h, binary_s) binary = morphology.remove_small_objects(binary, _SMALL_OBJECT_AREA) binary = morphology.remove_small_holes(binary, _SMALL_HOLE_AREA) binary = median(binary, morphology.disk(_MEDIAN_DISK)) return binary.astype(bool) @staticmethod def _threshold_gray(img_np): binary = np.full(img_np.shape[:2], False) binary[np.where(rgb2gray(img_np) < 0.8)] = True binary = morphology.remove_small_objects(binary, _SMALL_OBJECT_AREA) binary = morphology.remove_small_holes(binary, _SMALL_HOLE_AREA) binary = median(binary, morphology.disk(_MEDIAN_DISK)) return binary def _roi_threshold(self, img_np): # return self._threshold_sv(img_np) # return self._threshold_gray(img_np) return self._threshold_hs(img_np) def _mark_metastases_regions_in_label(self, blank_mask_np, label_np): '''Marks the blank mask using the given label image. This is to be overriden due to the different nature of the labels from the CAMELYON 16 and 17 data set. Parameters ---------- blank_mask_np: bool[H, W] np.array Mask to serve as the new label label_np: np.uint[H, W] np.array Gray scale image with the mask values. Returns ------- bool[H, W] np.array Mask to serve as the new label ''' blank_mask_np[np.where(label_np == 1)] = True blank_mask_np[np.where(label_np == 2)] = False @property def name(self): return self._name @property def centre(self): return self._centre @property def label_xml_path(self): return self._label_xml_path @property def tif_path(self): return self._tif_path @property def is_excluded(self): return self._is_excluded @property def patient(self): return self._patient @property def resource_group(self): return self._resource_group def get_full_wsi_image(self, level): '''Returns the whole WSI as an RGB image. Parameters ---------- level: int Level downsample to read the WSI. (values: 0..8) Returns ------- np.uint8[H, W, 3] np.array Whole WSI RGB image. ''' wsi_dim = self._wsi_slide.level_dimensions[level] wsi_img = self._wsi_slide.read_region((0, 0), level, wsi_dim) return np.array(wsi_img.convert('RGB')) def get_level_dimension(self, level): '''Returns the dimensions of the WSI for a given level. Parameters ---------- level: int Level downsample to get dimension. (values: 0..8) Returns ------- (width: int, height: int) Dimension of the WSI. ''' return self._wsi_slide.level_dimensions[level] def get_level_downsample(self, level): '''Returns the dimensions of the WSI for a given level. Parameters ---------- level: int Level downsample to get dimension. (values: 0..8) Returns ------- (width: int, height: int) Dimension of the WSI. ''' return self._wsi_slide.level_downsamples[level] def get_roi_mask(self, level): '''Returns the ROI of the WSI i.e. the tissue region. Parameters ---------- level: int Level downsample to read the WSI. (values: 0..8) Returns ------- bool[H, W] np.array Tissue ROI mask of the WSI. ''' wsi_img = self.get_full_wsi_image(level) wsi_img_np = np.array(wsi_img, dtype=np.uint8) roi_mask = self._roi_threshold(wsi_img_np) metastases_roi_mask, label_np = self._get_metastases_mask(level) roi_mask[np.where(metastases_roi_mask)] = True roi_mask[np.where(label_np == 3)] = False return roi_mask def _get_metastases_mask(self, level): '''Get metastases ROI of the WSI. Parameters ---------- level: int Level downsample to read the WSI. (values: 0..8) Returns ------- bool[H, W] np.array Metastases ROI mask of the WSI. ''' wsi_w, wsi_h = self._wsi_slide.level_dimensions[level] metastases_mask = np.full((wsi_h, wsi_w), False) label_slide = self._get_label_slide() if label_slide is not None: label_dim = label_slide.level_dimensions[level] label_img = label_slide.read_region((0, 0), level, label_dim) label_np = np.array(label_img.convert('L')) label_w, label_h = label_dim label_np = label_np[:min(wsi_h, label_h), :min(wsi_w, label_w)] self._mark_metastases_regions_in_label(metastases_mask, label_np) else: label_np = np.full((wsi_h, wsi_w), 0) return metastases_mask, label_np def get_metastases_mask(self, level): return self._get_metastases_mask(level)[0] def read_region_and_label(self, coordinates, level, dimension): '''Extracts a RGB region from the WSI. The given coordinates will be the center of the region, with the given dimension. Parameters ---------- coordinates: (x: int, y: int) Coordinates in the WSI at level 0. Upper-left of the region. level: int Level downsample to read the WSI. (values: 0..8) dimension: (w: int, h: int) Dimension of the region to extract. Returns ------- np.uint8[H, W, 3] np.array RGB region from WSI. bool[H, W] np.array Label mask for the given region. ''' scale_factor = 2**level w, h = dimension w_0, h_0 = w * scale_factor, h * scale_factor x, y = coordinates ## coordinate is at the center of the patches # x, y = x - (w * 2**level // 2), y - (h * 2**level // 2) args = (x, y), 0, (w_0, h_0) patch_img = self._wsi_slide.read_region(*args) patch_np = np.array(patch_img.convert('RGB')) patch_np = resize(patch_np, dimension, mode='reflect') metastases_np = np.full((w, h), False) label_slide = self._get_label_slide() if label_slide is not None: label_img = label_slide.read_region(*args) label_np = np.array(label_img.convert('L')) label_np = label_np[::scale_factor, ::scale_factor] self._mark_metastases_regions_in_label(metastases_np, label_np) return patch_np, metastases_np def _get_roi_patch_positions(self, level, get_roi, stride=256, patch_side=513, ds_level=5): '''Positions returned are in level 0.''' assert ds_level > level, 'level must be less than ds_level' width, height = self._wsi_slide.level_dimensions[level] ds_roi = get_roi(ds_level) ds = 2**(ds_level - level) upscale_factor = 2**level for y in range(0, math.ceil(height / stride) * stride, stride): for x in range(0, math.ceil(width / stride) * stride, stride): y_ds = y // ds x_ds = x // ds y_ds_end = (y + stride) // ds x_ds_end = (x + stride) // ds roi_patch = ds_roi[y_ds:y_ds_end, x_ds:x_ds_end] if (np.sum(roi_patch) / roi_patch.size > _ROI_PATCH_COVER_PERCENTAGE): yield x * upscale_factor, y * upscale_factor def get_roi_patch_positions(self, level, stride=256, patch_side=513, ds_level=5, force_not_excluded=False): if not self._is_excluded or force_not_excluded: get_roi = self.get_roi_mask else: get_roi = self.get_metastases_mask return self._get_roi_patch_positions( level, get_roi, stride, patch_side, ds_level, ) def close(self): self._wsi_slide.close() self._label_slide is not None and self._label_slide.close() def __repr__(self): return '<Centre: {}, Name: {}>'.format(self._centre, self._name)
def predict_wsi(model, global_fixed, slide_path): # size_g, size_p = (244, 244), (244, 244) # size_g, size_p = (1008, 1008), (1008, 1008) # n_class = 2 # sub_batch_size = 1 def predict(image_as_tensor, size_g=(244, 244), size_p=(244, 244), n_class=2): images_glb = resize(image_as_tensor, size_g) scores = [ np.zeros((1, n_class, image_as_tensor[i].size[1], image_as_tensor[i].size[0])) for i in range(len(image_as_tensor)) ] images_glb = images_transform(images_glb) patches, coordinates, templates, sizes, ratios = global2patch( image_as_tensor, size_p) predicted_ensembles = [ np.zeros((len(coordinates[i]), n_class, size_p[0], size_p[1])) for i in range(len(image_as_tensor)) ] for i in range(len(image_as_tensor)): j = 0 while j < len(coordinates[i]): patches_var = images_transform(patches[i][j:j + 1]) # b, c, h, w fm_patches, _ = model.module.collect_local_fm( images_glb[i:i + 1], patches_var, ratios[i], coordinates[i], [j, j + 1], len(image_as_tensor), global_model=global_fixed, template=templates[i], n_patch_all=len(coordinates[i]), ) j += 1 _, fm_global = model.forward(images_glb, None, None, None, mode=PhaseMode.GlobalFromLocal) for i in range(len(image_as_tensor)): j = 0 # while j < n ** 2: while j < len(coordinates[i]): fl = fm_patches[i][j:j + 1].cuda() fg = crop_global(fm_global[i:i + 1], coordinates[i][j:j + 1], ratios[i])[0] fg = F.interpolate(fg, size=fl.size()[2:], mode="bilinear") output_ensembles = model.module.ensemble( fl, fg) # include cordinates # output_ensembles = F.interpolate(model.module.ensemble(fl, fg), size_p, **model.module._up_kwargs) # ensemble predictions predicted_ensembles[i][j:j + output_ensembles.size()[0]] += ( F.interpolate( output_ensembles, size=size_p, mode="nearest", ).data.cpu().numpy()) j += 1 scores[i] += np.rot90( np.array( patch2global( predicted_ensembles[i:i + 1], n_class, sizes[i:i + 1], coordinates[i:i + 1], size_p, )), k=0, axes=(3, 2), ) return [score.argmax(1)[0] for score in scores] slide = OpenSlide(slide_path) w, h = slide.level_dimensions[2] img = slide.read_region((0, 0), 2, (w, h)) slide.close() img.convert('RGB').save('/tmp/temp.jpg') slide = ImageSlide('/tmp/temp.jpg') dz = deepzoom.DeepZoomGenerator(slide, tile_size=1024, overlap=0) cols, rows = dz.level_tiles[-1] out = np.zeros((rows * 1024, cols * 1024), dtype=np.uint8) for row in range(rows): for col in range(cols): tile = dz.get_tile(dz.level_count - 1, (col, row)) # col, row tile_coors = dz.get_tile_coordinates(dz.level_count - 1, (col, row)) left, top = tile_coors[0] t_w, t_h = tile_coors[2] if tile.size != (1024, 1024): tile = add_extra_pixels(tile, expected_shape=(1024, 1024)) tile = np.array(tile) processed = apply_filters(tile) pred = predict([Image.fromarray(processed)]) pil_pred = pred[0].astype(np.uint8) newmask = remove_mask_overlay_background(processed, pil_pred) # applied_mask = apply_mask(tile, newmask) applied_mask = newmask out[top:top + t_h, left:left + t_w] = applied_mask[:t_h, :t_w] return out[:h, :w]
class OpenSlideStore(Mapping): """Wraps an OpenSlide object as a multiscale Zarr Store. Parameters ---------- path: str The file to open with OpenSlide. tilesize: int Desired "chunk" size for zarr store. """ def __init__(self, path: str, tilesize: int = 512): self._slide = OpenSlide(path) self._tilesize = tilesize self._store = create_meta_store(self._slide, tilesize) def __getitem__(self, key: str): if key in self._store: # key is for metadata return self._store[key] # key should now be a path to an array chunk # e.g '3/4.5.0' -> '<level>/<chunk_key>' try: x, y, level = _parse_chunk_path(key) location = self._ref_pos(x, y, level) size = (self._tilesize, self._tilesize) tile = self._slide.read_region(location, level, size) except ArgumentError as err: # Can occur if trying to read a closed slide raise err except: # TODO: probably need better error handling. # If anything goes wrong, we just signal the chunk # is missing from the store. raise KeyError(key) return np.array(tile).tobytes() def __contains__(self, key: str): return key in self._store def __eq__(self, other): return (isinstance(other, OpenSlideStore) and self._slide._filename == other._slide._filename) def __iter__(self): return iter(self.keys()) def __len__(self): return sum(1 for _ in self) def __enter__(self): return self def __exit__(self, *args): self.close() def _ref_pos(self, x: int, y: int, level: int): dsample = self._slide.level_downsamples[level] xref = int(x * dsample * self._tilesize) yref = int(y * dsample * self._tilesize) return xref, yref def keys(self): return self._store.keys() def close(self): self._slide.close()
class Opener: def __init__(self, path): self.warning = '' self.path = path self.reader = None self.tilesize = 1024 self.ext = check_ext(path) self.default_dtype = np.uint16 if self.ext == '.ome.tif' or self.ext == '.ome.tiff': self.io = TiffFile(self.path, is_ome=False) self.group = zarr.open(self.io.series[0].aszarr()) self.reader = 'tifffile' self.ome_version = self._get_ome_version() print("OME ", self.ome_version) num_channels = self.get_shape()[0] tile_0 = self.get_tifffile_tile(num_channels, 0, 0, 0, 0, 1024) if tile_0 is not None: self.default_dtype = tile_0.dtype if (num_channels == 3 and tile_0.dtype == 'uint8'): self.rgba = True self.rgba_type = '3 channel' elif (num_channels == 1 and tile_0.dtype == 'uint8'): self.rgba = True self.rgba_type = '1 channel' else: self.rgba = False self.rgba_type = None print("RGB ", self.rgba) print("RGB type ", self.rgba_type) elif self.ext == '.svs': self.io = OpenSlide(self.path) self.dz = DeepZoomGenerator(self.io, tile_size=1024, overlap=0, limit_bounds=True) self.reader = 'openslide' self.rgba = True self.rgba_type = None self.default_dtype = np.uint8 print("RGB ", self.rgba) print("RGB type ", self.rgba_type) else: self.reader = None def _get_ome_version(self): try: software = self.io.pages[0].tags[305].value sub_ifds = self.io.pages[0].tags[330].value if "Faas" in software or sub_ifds is None: return 5 m = re.search('OME\\sBio-Formats\\s(\\d+)\\.\\d+\\.\\d+', software) if m is None: return 5 return int(m.group(1)) except Exception as e: print(e) return 5 def load_xml_markers(self): if self.ext == '.ome.tif' or self.ext == '.ome.tiff': try: metadata = ome_types.from_tiff(self.path) except Exception as e: return [] if not metadata or not metadata.images or not metadata.images[0]: return [] metadata_pixels = metadata.images[0].pixels if not metadata_pixels or not metadata_pixels.channels: return [] return [c.name for c in metadata_pixels.channels] else: return [] def close(self): self.io.close() def is_rgba(self, rgba_type=None): if rgba_type is None: return self.rgba else: return self.rgba and rgba_type == self.rgba_type def get_level_tiles(self, level, tile_size): if self.reader == 'tifffile': # Negative indexing to support shape len 3 or len 2 ny = int(np.ceil(self.group[level].shape[-2] / tile_size)) nx = int(np.ceil(self.group[level].shape[-1] / tile_size)) return (nx, ny) elif self.reader == 'openslide': l = self.dz.level_count - 1 - level return self.dz.level_tiles[l] def get_shape(self): def parse_shape(shape): if len(shape) >= 3: (num_channels, shape_y, shape_x) = shape[-3:] else: (shape_y, shape_x) = shape num_channels = 1 return (num_channels, shape_x, shape_y) if self.reader == 'tifffile': (num_channels, shape_x, shape_y) = parse_shape(self.group[0].shape) all_levels = [parse_shape(v.shape) for v in self.group.values()] num_levels = len( [shape for shape in all_levels if max(shape[1:]) > 512]) return (num_channels, num_levels, shape_x, shape_y) elif self.reader == 'openslide': (width, height) = self.io.dimensions def has_one_tile(counts): return max(counts) == 1 small_levels = list(filter(has_one_tile, self.dz.level_tiles)) level_count = self.dz.level_count - len(small_levels) + 1 return (3, level_count, width, height) def read_tiles(self, level, channel_number, tx, ty, tilesize): ix = tx * tilesize iy = ty * tilesize num_channels = self.get_shape()[0] try: if num_channels == 1: tile = self.group[level][iy:iy + tilesize, ix:ix + tilesize] else: tile = self.group[level][channel_number, iy:iy + tilesize, ix:ix + tilesize] tile = np.squeeze(tile) return tile except Exception as e: G['logger'].error(e) return None def get_tifffile_tile(self, num_channels, level, tx, ty, channel_number, tilesize=1024): if self.reader == 'tifffile': tile = self.read_tiles(level, channel_number, tx, ty, tilesize) if tile is None: return np.zeros((tilesize, tilesize), dtype=self.default_dtype) return tile def get_tile(self, num_channels, level, tx, ty, channel_number, fmt=None): if self.reader == 'tifffile': if self.is_rgba('3 channel'): tile_0 = self.get_tifffile_tile(num_channels, level, tx, ty, 0, 1024) tile_1 = self.get_tifffile_tile(num_channels, level, tx, ty, 1, 1024) tile_2 = self.get_tifffile_tile(num_channels, level, tx, ty, 2, 1024) tile = np.zeros((tile_0.shape[0], tile_0.shape[1], 3), dtype=np.uint8) tile[:, :, 0] = tile_0 tile[:, :, 1] = tile_1 tile[:, :, 2] = tile_2 _format = 'I;8' else: tile = self.get_tifffile_tile(num_channels, level, tx, ty, channel_number, 1024) _format = fmt if fmt else 'I;16' if (_format == 'RGBA' and tile.dtype != np.uint32): tile = tile.astype(np.uint32) if (_format == 'I;16' and tile.dtype != np.uint16): if tile.dtype == np.uint8: tile = 255 * tile.astype(np.uint16) else: # TODO: real support for uint32, signed values, and floats tile = np.clip(tile, 0, 65535).astype(np.uint16) return Image.fromarray(tile, _format) elif self.reader == 'openslide': l = self.dz.level_count - 1 - level img = self.dz.get_tile(l, (tx, ty)) return img def save_mask_tiles(self, filename, mask_params, logger, tile_size, level, tx, ty): should_skip_tile = {} def get_empty_path(path): basename = os.path.splitext(path)[0] return pathlib.Path(f'{basename}_tmp.txt') for image_params in mask_params['images']: output_file = str(image_params['out_path'] / filename) path_exists = os.path.exists(output_file) or os.path.exists( get_empty_path(output_file)) should_skip = path_exists and image_params['is_up_to_date'] should_skip_tile[output_file] = should_skip if all(should_skip_tile.values()): logger.warning(f'Not saving tile level {level} ty {ty} tx {tx}') logger.warning( f'Every mask {filename} exists with same rendering settings') return if self.reader == 'tifffile': num_channels = self.get_shape()[0] tile = self.get_tifffile_tile(num_channels, level, tx, ty, 0, tile_size) for image_params in mask_params['images']: output_file = str(image_params['out_path'] / filename) if should_skip_tile[output_file]: continue target = np.zeros(tile.shape + (4, ), np.uint8) skip_empty_tile = True for channel in image_params['settings']['channels']: rgba_color = [ int(255 * i) for i in (colors.to_rgba(channel['color'])) ] ids = channel['ids'] if len(ids) > 0: bool_tile = np.isin(tile, ids) # Signal that we must actually save the image if not skip_empty_tile or np.any(bool_tile): skip_empty_tile = False target[bool_tile] = rgba_color else: # Note, any channel without ids to map will override all others target = colorize_mask(target, tile) skip_empty_tile = False if skip_empty_tile: empty_file = get_empty_path(output_file) if not os.path.exists(empty_file): with open(empty_file, 'w') as fp: pass else: img = Image.frombytes('RGBA', target.T.shape[1:], target.tobytes()) img.save(output_file, quality=85) def save_tile(self, output_file, settings, tile_size, level, tx, ty): if self.reader == 'tifffile' and self.is_rgba('3 channel'): num_channels = self.get_shape()[0] tile_0 = self.get_tifffile_tile(num_channels, level, tx, ty, 0, tile_size) tile_1 = self.get_tifffile_tile(num_channels, level, tx, ty, 1, tile_size) tile_2 = self.get_tifffile_tile(num_channels, level, tx, ty, 2, tile_size) tile = np.zeros((tile_0.shape[0], tile_0.shape[1], 3), dtype=np.uint8) tile[:, :, 0] = tile_0 tile[:, :, 1] = tile_1 tile[:, :, 2] = tile_2 img = Image.fromarray(tile, 'RGB') img.save(output_file, quality=85) elif self.reader == 'tifffile' and self.is_rgba('1 channel'): num_channels = self.get_shape()[0] tile = self.get_tifffile_tile(num_channels, level, tx, ty, 0, tile_size) img = Image.fromarray(tile, 'RGB') img.save(output_file, quality=85) elif self.reader == 'tifffile': target = None for i, (marker, color, start, end) in enumerate( zip(settings['Channel Number'], settings['Color'], settings['Low'], settings['High'])): num_channels = self.get_shape()[0] tile = self.get_tifffile_tile(num_channels, level, tx, ty, int(marker), tile_size) if (tile.dtype != np.uint16): if tile.dtype == np.uint8: tile = 255 * tile.astype(np.uint16) else: tile = tile.astype(np.uint16) if i == 0 or target is None: target = np.zeros(tile.shape + (3, ), np.float32) composite_channel(target, tile, colors.to_rgb(color), float(start), float(end)) if target is not None: np.clip(target, 0, 1, out=target) target_u8 = (target * 255).astype(np.uint8) img = Image.frombytes('RGB', target.T.shape[1:], target_u8.tobytes()) img.save(output_file, quality=85) elif self.reader == 'openslide': l = self.dz.level_count - 1 - level img = self.dz.get_tile(l, (tx, ty)) img.save(output_file, quality=85)
tumor_mask = Image.fromarray(mask_np) mask_save_path = save_path + "mask" + "/mask_" + os.path.basename(wsi_path) + "_level_0" + "_x_" + str(x_cut_id) + "_y_" + str(y_cut_id) +".png" meta_dict["mask_path"] = mask_save_path tumor_mask.save(mask_save_path) img_pil = Image.fromarray(np_img[:,:,0:3]) img_save_path = save_path + "patch" + "/img_" + os.path.basename(wsi_path) + "_level_0" + "_x_" + str(x_cut_id) + "_y_" + str(y_cut_id) +".png" meta_dict["img_path"] = img_save_path img_pil.save(img_save_path) meta_data_array["patches"].append(meta_dict) # pdb.set_trace() print(len(meta_data_array["patches"])) if label == "normal": patch_json_path = save_path + "json_fgmask" + "/" + str(num) + "_json_" + os.path.basename(wsi_path) + "_level_0" + '.json' else: patch_json_path = save_path + "json_fgmask" + "/" + str(num) +"_json_" + os.path.basename(wsi_path) + "_level_0" + '.json' # save_json_path = os.path.join(json_base_path, wsi_name + "_level_0" + '.json') # if not os.path.exists(patch_json_path): # os.makedirs(patch_json_path) if len(meta_data_array["patches"]) != 0 : with io.open(patch_json_path, 'w', encoding='utf8') as outfile: json_patch = json.dumps(meta_data_array) outfile.write(str(json_patch)) slide.close() print('INFO: json save done!') else: print("pass")
class _SlideTest(object): def setUp(self): self.osr = OpenSlide(file_path(self.FILENAME)) def tearDown(self): self.osr.close()
class WSI(object): """ # ================================ # Class to annotate WSIs with ROIs # ================================ """ index = 0 negative_patch_index = 118456 positive_patch_index = 2230 wsi_paths = [] mask_paths = [] def_level = 7 key = 0 def extract_patches_mask(self, bounding_boxes): """ Extract positive patches targeting annotated tumor region Save extracted patches to desk as .png image files :param bounding_boxes: list of bounding boxes corresponds to tumor regions :return: """ mag_factor = pow(2, self.level_used) print('No. of ROIs to extract patches from: %d' % len(bounding_boxes)) for i, bounding_box in enumerate(bounding_boxes): b_x_start = int(bounding_box[0]) * mag_factor b_y_start = int(bounding_box[1]) * mag_factor b_x_end = (int(bounding_box[0]) + int(bounding_box[2])) * mag_factor b_y_end = (int(bounding_box[1]) + int(bounding_box[3])) * mag_factor X = np.random.random_integers(b_x_start, high=b_x_end, size=500) Y = np.random.random_integers(b_y_start, high=b_y_end, size=500) # X = np.arange(b_x_start, b_x_end-256, 5) # Y = np.arange(b_y_start, b_y_end-256, 5) for x, y in zip(X, Y): mask = self.mask_image.read_region((x, y), 0, (PATCH_SIZE, PATCH_SIZE)) mask_gt = np.array(mask) mask_gt = cv2.cvtColor(mask_gt, cv2.COLOR_BGR2GRAY) white_pixel_cnt_gt = cv2.countNonZero(mask_gt) if white_pixel_cnt_gt > ((PATCH_SIZE * PATCH_SIZE) * 0.90): # mask = Image.fromarray(mask) patch = self.wsi_image.read_region((x, y), 0, (PATCH_SIZE, PATCH_SIZE)) patch.save(PROCESSED_PATCHES_FROM_USE_MASK_POSITIVE_PATH + PATCH_TUMOR_PREFIX + str(self.positive_patch_index), 'PNG') self.positive_patch_index += 1 patch.close() mask.close() def extract_patches_normal(self, bounding_boxes): """ Extract negative patches from Normal WSIs Save extracted patches to desk as .png image files :param bounding_boxes: list of bounding boxes corresponds to detected ROIs :return: """ mag_factor = pow(2, self.level_used) print('No. of ROIs to extract patches from: %d' % len(bounding_boxes)) for i, bounding_box in enumerate(bounding_boxes): b_x_start = int(bounding_box[0]) * mag_factor b_y_start = int(bounding_box[1]) * mag_factor b_x_end = (int(bounding_box[0]) + int(bounding_box[2])) * mag_factor b_y_end = (int(bounding_box[1]) + int(bounding_box[3])) * mag_factor X = np.random.random_integers(b_x_start, high=b_x_end, size=500) Y = np.random.random_integers(b_y_start, high=b_y_end, size=500) # X = np.arange(b_x_start, b_x_end-256, 5) # Y = np.arange(b_y_start, b_y_end-256, 5) for x, y in zip(X, Y): patch = self.wsi_image.read_region((x, y), 0, (PATCH_SIZE, PATCH_SIZE)) patch_array = np.array(patch) patch_hsv = cv2.cvtColor(patch_array, cv2.COLOR_BGR2HSV) # [20, 20, 20] lower_red = np.array([20, 20, 20]) # [255, 255, 255] upper_red = np.array([200, 200, 200]) mask = cv2.inRange(patch_hsv, lower_red, upper_red) white_pixel_cnt = cv2.countNonZero(mask) if white_pixel_cnt > ((PATCH_SIZE * PATCH_SIZE) * 0.50): # mask = Image.fromarray(mask) patch.save(PROCESSED_PATCHES_NORMAL_NEGATIVE_PATH + PATCH_NORMAL_PREFIX + str(self.negative_patch_index), 'PNG') # mask.save(PROCESSED_PATCHES_NORMAL_PATH + PATCH_NORMAL_PREFIX + str(self.patch_index), # 'PNG') self.negative_patch_index += 1 patch.close() def extract_patches_tumor(self, bounding_boxes): """ Extract both, negative patches from Normal area and positive patches from Tumor area Save extracted patches to desk as .png image files :param bounding_boxes: list of bounding boxes corresponds to detected ROIs :return: """ mag_factor = pow(2, self.level_used) print('No. of ROIs to extract patches from: %d' % len(bounding_boxes)) for i, bounding_box in enumerate(bounding_boxes): b_x_start = int(bounding_box[0]) * mag_factor b_y_start = int(bounding_box[1]) * mag_factor b_x_end = (int(bounding_box[0]) + int(bounding_box[2])) * mag_factor b_y_end = (int(bounding_box[1]) + int(bounding_box[3])) * mag_factor X = np.random.random_integers(b_x_start, high=b_x_end, size=500) Y = np.random.random_integers(b_y_start, high=b_y_end, size=500) # X = np.arange(b_x_start, b_x_end-256, 5) # Y = np.arange(b_y_start, b_y_end-256, 5) for x, y in zip(X, Y): patch = self.wsi_image.read_region((x, y), 0, (PATCH_SIZE, PATCH_SIZE)) mask = self.mask_image.read_region((x, y), 0, (PATCH_SIZE, PATCH_SIZE)) mask_gt = np.array(mask) # mask_gt = cv2.cvtColor(mask_gt, cv2.COLOR_BGR2GRAY) mask_gt = cv2.cvtColor(mask_gt, cv2.COLOR_BGR2GRAY) patch_array = np.array(patch) white_pixel_cnt_gt = cv2.countNonZero(mask_gt) if white_pixel_cnt_gt == 0: # mask_gt does not contain tumor area patch_hsv = cv2.cvtColor(patch_array, cv2.COLOR_BGR2HSV) lower_red = np.array([20, 20, 20]) upper_red = np.array([200, 200, 200]) mask_patch = cv2.inRange(patch_hsv, lower_red, upper_red) white_pixel_cnt = cv2.countNonZero(mask_patch) if white_pixel_cnt > ((PATCH_SIZE * PATCH_SIZE) * 0.50): # mask = Image.fromarray(mask) patch.save(PROCESSED_PATCHES_TUMOR_NEGATIVE_PATH + PATCH_NORMAL_PREFIX + str(self.negative_patch_index), 'PNG') # mask.save(PROCESSED_PATCHES_NORMAL_PATH + PATCH_NORMAL_PREFIX + str(self.patch_index), # 'PNG') self.negative_patch_index += 1 else: # mask_gt contains tumor area if white_pixel_cnt_gt >= ((PATCH_SIZE * PATCH_SIZE) * 0.85): patch.save(PROCESSED_PATCHES_POSITIVE_PATH + PATCH_TUMOR_PREFIX + str(self.positive_patch_index), 'PNG') self.positive_patch_index += 1 patch.close() mask.close() def read_wsi_mask(self, wsi_path, mask_path): try: self.cur_wsi_path = wsi_path self.wsi_image = OpenSlide(wsi_path) self.mask_image = OpenSlide(mask_path) self.level_used = min(self.def_level, self.wsi_image.level_count - 1, self.mask_image.level_count - 1) self.mask_pil = self.mask_image.read_region((0, 0), self.level_used, self.mask_image.level_dimensions[self.level_used]) self.mask = np.array(self.mask_pil) except OpenSlideUnsupportedFormatError: print('Exception: OpenSlideUnsupportedFormatError') return False return True def read_wsi_normal(self, wsi_path): """ # ===================================================================================== # read WSI image and resize # Due to memory constraint, we use down sampled (4th level, 1/32 resolution) image # ====================================================================================== """ try: self.cur_wsi_path = wsi_path self.wsi_image = OpenSlide(wsi_path) self.level_used = min(self.def_level, self.wsi_image.level_count - 1) self.rgb_image_pil = self.wsi_image.read_region((0, 0), self.level_used, self.wsi_image.level_dimensions[self.level_used]) self.rgb_image = np.array(self.rgb_image_pil) except OpenSlideUnsupportedFormatError: print('Exception: OpenSlideUnsupportedFormatError') return False return True def read_wsi_tumor(self, wsi_path, mask_path): """ # ===================================================================================== # read WSI image and resize # Due to memory constraint, we use down sampled (4th level, 1/32 resolution) image # ====================================================================================== """ try: self.cur_wsi_path = wsi_path self.wsi_image = OpenSlide(wsi_path) self.mask_image = OpenSlide(mask_path) self.level_used = min(self.def_level, self.wsi_image.level_count - 1, self.mask_image.level_count - 1) self.rgb_image_pil = self.wsi_image.read_region((0, 0), self.level_used, self.wsi_image.level_dimensions[self.level_used]) self.rgb_image = np.array(self.rgb_image_pil) except OpenSlideUnsupportedFormatError: print('Exception: OpenSlideUnsupportedFormatError') return False return True def find_roi_n_extract_patches_mask(self): mask = cv2.cvtColor(self.mask, cv2.COLOR_BGR2GRAY) contour_mask, bounding_boxes = self.get_image_contours_mask(np.array(mask), np.array(self.mask)) # contour_mask = cv2.resize(contour_mask, (0, 0), fx=0.40, fy=0.40) # cv2.imshow('contour_mask', np.array(contour_mask)) self.mask_pil.close() self.extract_patches_mask(bounding_boxes) self.wsi_image.close() self.mask_image.close() def find_roi_n_extract_patches_normal(self): hsv = cv2.cvtColor(self.rgb_image, cv2.COLOR_BGR2HSV) # [20, 20, 20] lower_red = np.array([20, 50, 20]) # [255, 255, 255] upper_red = np.array([200, 150, 200]) mask = cv2.inRange(hsv, lower_red, upper_red) # (50, 50) close_kernel = np.ones((25, 25), dtype=np.uint8) image_close = Image.fromarray(cv2.morphologyEx(np.array(mask), cv2.MORPH_CLOSE, close_kernel)) # (30, 30) open_kernel = np.ones((30, 30), dtype=np.uint8) image_open = Image.fromarray(cv2.morphologyEx(np.array(image_close), cv2.MORPH_OPEN, open_kernel)) contour_rgb, bounding_boxes = self.get_image_contours_normal(np.array(image_open), self.rgb_image) # contour_rgb = cv2.resize(contour_rgb, (0, 0), fx=0.40, fy=0.40) # cv2.imshow('contour_rgb', np.array(contour_rgb)) self.rgb_image_pil.close() self.extract_patches_normal(bounding_boxes) self.wsi_image.close() def find_roi_n_extract_patches_tumor(self): hsv = cv2.cvtColor(self.rgb_image, cv2.COLOR_BGR2HSV) lower_red = np.array([20, 20, 20]) upper_red = np.array([255, 255, 255]) mask = cv2.inRange(hsv, lower_red, upper_red) # (50, 50) close_kernel = np.ones((50, 50), dtype=np.uint8) image_close = Image.fromarray(cv2.morphologyEx(np.array(mask), cv2.MORPH_CLOSE, close_kernel)) # (30, 30) open_kernel = np.ones((30, 30), dtype=np.uint8) image_open = Image.fromarray(cv2.morphologyEx(np.array(image_close), cv2.MORPH_OPEN, open_kernel)) contour_rgb, bounding_boxes = self.get_image_contours_tumor(np.array(image_open), self.rgb_image) # contour_rgb = cv2.resize(contour_rgb, (0, 0), fx=0.40, fy=0.40) # cv2.imshow('contour_rgb', np.array(contour_rgb)) self.rgb_image_pil.close() self.extract_patches_tumor(bounding_boxes) self.wsi_image.close() self.mask_image.close() @staticmethod def get_image_contours_mask(cont_img, mask_img): _, contours, _ = cv2.findContours(cont_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) bounding_boxes = [cv2.boundingRect(c) for c in contours] contours_mask_image_array = np.array(mask_img) line_color = (255, 0, 0) # blue color code cv2.drawContours(contours_mask_image_array, contours, -1, line_color, 1) return contours_mask_image_array, bounding_boxes @staticmethod def get_image_contours_normal(cont_img, rgb_image): _, contours, _ = cv2.findContours(cont_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) bounding_boxes = [cv2.boundingRect(c) for c in contours] contours_rgb_image_array = np.array(rgb_image) line_color = (255, 0, 0) # blue color code cv2.drawContours(contours_rgb_image_array, contours, -1, line_color, 3) return contours_rgb_image_array, bounding_boxes @staticmethod def get_image_contours_tumor(cont_img, rgb_image): _, contours, _ = cv2.findContours(cont_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) bounding_boxes = [cv2.boundingRect(c) for c in contours] contours_rgb_image_array = np.array(rgb_image) line_color = (255, 0, 0) # blue color code cv2.drawContours(contours_rgb_image_array, contours, -1, line_color, 3) # cv2.drawContours(mask_image, contours_mask, -1, line_color, 3) return contours_rgb_image_array, bounding_boxes def wait(self): self.key = cv2.waitKey(0) & 0xFF print('key: %d' % self.key) if self.key == 27: # escape return False elif self.key == 81: # <- (prev) self.index -= 1 if self.index < 0: self.index = len(self.wsi_paths) - 1 elif self.key == 83: # -> (next) self.index += 1 if self.index >= len(self.wsi_paths): self.index = 0 return True
def delete_label_annotion(file_dir): mrxs_lst = [] is_label = input( "Please input mrxs filename and ID of its delete label:\n" "For example, mrxs_file1:ID1 (DD10:UENFI10JD-DKKEMIJD-19DNK937,DD11:JFD73JF-2KD93-DKV10)\n" ) # cv2.destroyAllWindows() label_lst = is_label.split(",") label_dict = {} if label_lst != ['']: for li in label_lst: if label_dict.get(li.split(":")[0]) is not None: label_dict[li.split(":")[0]].append(li.split(":")[1]) else: label_dict[li.split(":")[0]] = [li.split(":")[1]] if not os.path.isdir(file_dir): mrxs_lst[0] = file_dir else: for i in os.listdir(file_dir): if os.path.splitext(i)[-1] == '.mrxs': print( "===============================================================================" ) print(os.path.join(file_dir, os.path.splitext(i)[0])) mrxs_lst.append(os.path.join(file_dir, os.path.splitext(i)[0])) for mrxs_fille_name in mrxs_lst: # mrxs_fille_name = "/home/gytang/medicine/fake/DCB-003" osr = OpenSlide(mrxs_fille_name + '.mrxs') axis_list = [] find_file_flag = 0 file_list = os.listdir(mrxs_fille_name) file_list.sort() delete_label = None if label_dict.get(os.path.basename(mrxs_fille_name)) is not None: delete_label = label_dict.get(os.path.basename(mrxs_fille_name)) for i in file_list[::-1]: a = magic.from_file(os.path.join(mrxs_fille_name, i)) #print("{}: magic tool read file property: {}".format(i, a)) if a.find("ASCII text") != -1 or a.find("UTF-8 Unicode") != -1: with open(os.path.join(mrxs_fille_name, i)) as f: lines = f.readlines() lines.reverse() if lines[-1].find("<attributes>") == -1: continue if find_file_flag == 1: print( "There are already exsited a .dat label config file. The mrxs is not as normal as we occured." ) find_file_flag = 1 ID_name = [] res = [] for idx, line in enumerate(lines): try: line = parseString(line) # print("ok:", line[0]) except: # print("Can't analysis the {}th line".format(idx)) # print(line) res.append(line) continue descript_lst = line.getElementsByTagName('descriptor') for elements in descript_lst: ID = elements.getAttribute('ID')[1:-1] if ID not in ID_name: ID_name.append(ID) if delete_label is not None: if ID in delete_label: continue sib_lst = line.getElementsByTagName( 'SimpleBookmark') if sib_lst == []: continue label = sib_lst[0].getAttribute('Caption') detail_label = sib_lst[0].getAttribute('Desc') if label.find( "Annotation") == 0 and detail_label != "": # print("alter annotation label.") for node in sib_lst: node.setAttribute("Caption", detail_label) res.append(line) with open(os.path.join(mrxs_fille_name, i), 'w') as fd: if res is not None: res.reverse() for re in res: try: # print("ok:", line[0]) line = re.toxml() + '\n' if line.find('<?xml version="1.0" ?>') == 0: line = line.replace('<?xml version="1.0" ?>', '') # print(line) fd.write(line) except: # print("Can't analysis the {}th line") # print(re) fd.write(re) continue break if find_file_flag == 0: print( "No .dat label file in mrxs {} which searched by magic tool.". format(mrxs_fille_name)) osr.close()