def get_contour_check_fn(contour_fn='four_pt_hard', cont=None, ref_patch_size=None, center_shift=None): if contour_fn == 'four_pt_hard': cont_check_fn = isInContourV3_Hard(contour=cont, patch_size=ref_patch_size, center_shift=center_shift) elif contour_fn == 'four_pt_easy': cont_check_fn = isInContourV3_Easy(contour=cont, patch_size=ref_patch_size, center_shift=0.5) elif contour_fn == 'center': cont_check_fn = isInContourV2(contour=cont, patch_size=ref_patch_size) elif contour_fn == 'basic': cont_check_fn = isInContourV1(contour=cont) else: raise NotImplementedError return cont_check_fn
def process_contour(self, cont, contour_holes, patch_level, save_path, patch_size=256, step_size=256, contour_fn='four_pt', use_padding=True, top_left=None, bot_right=None): start_x, start_y, w, h = cv2.boundingRect( cont) if cont is not None else (0, 0, self.level_dim[patch_level][0], self.level_dim[patch_level][1]) patch_downsample = (int(self.level_downsamples[patch_level][0]), int(self.level_downsamples[patch_level][1])) ref_patch_size = (patch_size * patch_downsample[0], patch_size * patch_downsample[1]) img_w, img_h = self.level_dim[0] if use_padding: stop_y = start_y + h stop_x = start_x + w else: stop_y = min(start_y + h, img_h - ref_patch_size[1] + 1) stop_x = min(start_x + w, img_w - ref_patch_size[0] + 1) print("Bounding Box:", start_x, start_y, w, h) print("Contour Area:", cv2.contourArea(cont)) if bot_right is not None: stop_y = min(bot_right[1], stop_y) stop_x = min(bot_right[0], stop_x) if top_left is not None: start_y = max(top_left[1], start_y) start_x = max(top_left[0], start_x) if bot_right is not None or top_left is not None: w, h = stop_x - start_x, stop_y - start_y if w <= 0 or h <= 0: print("Contour is not in specified ROI, skip") return {}, {} else: print("Adjusted Bounding Box:", start_x, start_y, w, h) if isinstance(contour_fn, str): if contour_fn == 'four_pt': cont_check_fn = isInContourV3_Easy( contour=cont, patch_size=ref_patch_size[0], center_shift=0.5) elif contour_fn == 'four_pt_hard': cont_check_fn = isInContourV3_Hard( contour=cont, patch_size=ref_patch_size[0], center_shift=0.5) elif contour_fn == 'center': cont_check_fn = isInContourV2(contour=cont, patch_size=ref_patch_size[0]) elif contour_fn == 'basic': cont_check_fn = isInContourV1(contour=cont) else: raise NotImplementedError else: assert isinstance(contour_fn, Contour_Checking_fn) cont_check_fn = contour_fn step_size_x = step_size * patch_downsample[0] step_size_y = step_size * patch_downsample[1] x_range = np.arange(start_x, stop_x, step=step_size_x) y_range = np.arange(start_y, stop_y, step=step_size_y) x_coords, y_coords = np.meshgrid(x_range, y_range, indexing='ij') coord_candidates = np.array([x_coords.flatten(), y_coords.flatten()]).transpose() num_workers = mp.cpu_count() if num_workers > 4: num_workers = 4 pool = mp.Pool(num_workers) iterable = [(coord, contour_holes, ref_patch_size[0], cont_check_fn) for coord in coord_candidates] results = pool.starmap(WholeSlideImage.process_coord_candidate, iterable) pool.close() results = np.array( [result for result in results if result is not None]) print('Extracted {} coordinates'.format(len(results))) if len(results) > 1: asset_dict = {'coords': results} attr = { 'patch_size': patch_size, # To be considered... 'patch_level': patch_level, 'downsample': self.level_downsamples[patch_level], 'downsampled_level_dim': tuple(np.array(self.level_dim[patch_level])), 'level_dim': self.level_dim[patch_level], 'name': self.name, 'save_path': save_path } attr_dict = {'coords': attr} return asset_dict, attr_dict else: return {}, {}
def _getPatchGenerator(self, cont, cont_idx, patch_level, save_path, patch_size=256, step_size=256, custom_downsample=1, white_black=True, white_thresh=15, black_thresh=50, contour_fn='four_pt', use_padding=True): start_x, start_y, w, h = cv2.boundingRect( cont) if cont is not None else (0, 0, self.level_dim[patch_level][0], self.level_dim[patch_level][1]) print("Bounding Box:", start_x, start_y, w, h) print("Contour Area:", cv2.contourArea(cont)) if custom_downsample > 1: assert custom_downsample == 2 target_patch_size = patch_size patch_size = target_patch_size * 2 step_size = step_size * 2 print( "Custom Downsample: {}, Patching at {} x {}, But Final Patch Size is {} x {}" .format(custom_downsample, patch_size, patch_size, target_patch_size, target_patch_size)) patch_downsample = (int(self.level_downsamples[patch_level][0]), int(self.level_downsamples[patch_level][1])) ref_patch_size = (patch_size * patch_downsample[0], patch_size * patch_downsample[1]) step_size_x = step_size * patch_downsample[0] step_size_y = step_size * patch_downsample[1] if isinstance(contour_fn, str): if contour_fn == 'four_pt': cont_check_fn = isInContourV3_Easy( contour=cont, patch_size=ref_patch_size[0], center_shift=0.5) elif contour_fn == 'four_pt_hard': cont_check_fn = isInContourV3_Hard( contour=cont, patch_size=ref_patch_size[0], center_shift=0.5) elif contour_fn == 'center': cont_check_fn = isInContourV2(contour=cont, patch_size=ref_patch_size[0]) elif contour_fn == 'basic': cont_check_fn = isInContourV1(contour=cont) else: raise NotImplementedError else: assert isinstance(contour_fn, Contour_Checking_fn) cont_check_fn = contour_fn img_w, img_h = self.level_dim[0] if use_padding: stop_y = start_y + h stop_x = start_x + w else: stop_y = min(start_y + h, img_h - ref_patch_size[1]) stop_x = min(start_x + w, img_w - ref_patch_size[0]) count = 0 for y in range(start_y, stop_y, step_size_y): for x in range(start_x, stop_x, step_size_x): if not self.isInContours( cont_check_fn, (x, y), self.holes_tissue[cont_idx], ref_patch_size[0] ): #point not inside contour and its associated holes continue count += 1 patch_PIL = self.wsi.read_region( (x, y), patch_level, (patch_size, patch_size)).convert('RGB') if custom_downsample > 1: patch_PIL = patch_PIL.resize( (target_patch_size, target_patch_size)) if white_black: if isBlackPatch(np.array(patch_PIL), rgbThresh=black_thresh) or isWhitePatch( np.array(patch_PIL), satThresh=white_thresh): continue patch_info = { 'x': x // (patch_downsample[0] * custom_downsample), 'y': y // (patch_downsample[1] * custom_downsample), 'cont_idx': cont_idx, 'patch_level': patch_level, 'downsample': self.level_downsamples[patch_level], 'downsampled_level_dim': tuple( np.array(self.level_dim[patch_level]) // custom_downsample), 'level_dim': self.level_dim[patch_level], 'patch_PIL': patch_PIL, 'name': self.name, 'save_path': save_path } yield patch_info print("patches extracted: {}".format(count))