def __getitem__(self, idx): path = self.info_list[idx] data = np.load(path) # split stacked channel into image and label img = (data[..., :3]).astype("uint8") # RGB images ann = (data[..., 3:]).astype("int32") # instance ID map and type map if self.shape_augs is not None: shape_augs = self.shape_augs.to_deterministic() img = shape_augs.augment_image(img) ann = shape_augs.augment_image(ann) if self.input_augs is not None: input_augs = self.input_augs.to_deterministic() img = input_augs.augment_image(img) img = cropping_center(img, self.input_shape) feed_dict = {"img": img} inst_map = ann[..., 0] # HW1 -> HW if self.with_type: type_map = (ann[..., 1]).copy() type_map = cropping_center(type_map, self.mask_shape) #type_map[type_map == 5] = 1 # merge neoplastic and non-neoplastic feed_dict["tp_map"] = type_map # TODO: document hard coded assumption about #input target_dict = self.target_gen_func(inst_map, self.mask_shape, **self.target_gen_kwargs) feed_dict.update(target_dict) return feed_dict
def gen_targets(ann, crop_shape, **kwargs): """Generate the targets for the network.""" hv_map = gen_instance_hv_map(ann, crop_shape) np_map = ann.copy() np_map[np_map > 0] = 1 hv_map = cropping_center(hv_map, crop_shape) np_map = cropping_center(np_map, crop_shape) target_dict = { "hv_map": hv_map, "np_map": np_map, } return target_dict
def _augment(self, img, _): img = np.copy(img) orig_ann = img[..., 0] # instance ID map fixed_ann = self._fix_mirror_padding(orig_ann) # re-cropping with fixed instance id map crop_ann = cropping_center(fixed_ann, self.crop_shape) # setting 1 boundary pix of each instance to background contour_map = np.zeros(fixed_ann.shape[:2], np.uint8) inst_list = list(np.unique(crop_ann)) inst_list.remove(0) # 0 is background k_disk = np.array([ [0, 0, 0, 1, 0, 0, 0], [0, 0, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 0], [1, 1, 1, 1, 1, 1, 1], [0, 1, 1, 1, 1, 1, 0], [0, 0, 1, 1, 1, 0, 0], [0, 0, 0, 1, 0, 0, 0], ], np.uint8) for inst_id in inst_list: inst_map = np.array(fixed_ann == inst_id, np.uint8) inner = cv2.erode(inst_map, k_disk, iterations=1) outer = cv2.dilate(inst_map, k_disk, iterations=1) contour_map += outer - inner contour_map[contour_map > 0] = 1 # binarize img = np.dstack([fixed_ann, contour_map]) return img
def viz_step_output(raw_data, nr_types=None): """ `raw_data` will be implicitly provided in the similar format as the return dict from train/valid step, but may have been accumulated across N running step """ imgs = raw_data["img"] true_np, pred_np = raw_data["np"] true_hv, pred_hv = raw_data["hv"] if nr_types is not None: true_tp, pred_tp = raw_data["tp"] aligned_shape = [ list(imgs.shape), list(true_np.shape), list(pred_np.shape) ] aligned_shape = np.min(np.array(aligned_shape), axis=0)[1:3] cmap = plt.get_cmap("jet") def colorize(ch, vmin, vmax): """ Will clamp value value outside the provided range to vmax and vmin """ ch = np.squeeze(ch.astype("float32")) ch[ch > vmax] = vmax # clamp value ch[ch < vmin] = vmin ch = (ch - vmin) / (vmax - vmin + 1.0e-16) # take RGB from RGBA heat map ch_cmap = (cmap(ch)[..., :3] * 255).astype("uint8") # ch_cmap = center_pad_to_shape(ch_cmap, aligned_shape) return ch_cmap viz_list = [] for idx in range(imgs.shape[0]): # img = center_pad_to_shape(imgs[idx], aligned_shape) img = cropping_center(imgs[idx], aligned_shape) true_viz_list = [img] # cmap may randomly fails if of other types true_viz_list.append(colorize(true_np[idx], 0, 1)) true_viz_list.append(colorize(true_hv[idx][..., 0], -1, 1)) true_viz_list.append(colorize(true_hv[idx][..., 1], -1, 1)) if nr_types is not None: # TODO: a way to pass through external info true_viz_list.append(colorize(true_tp[idx], 0, nr_types)) true_viz_list = np.concatenate(true_viz_list, axis=1) pred_viz_list = [img] # cmap may randomly fails if of other types pred_viz_list.append(colorize(pred_np[idx], 0, 1)) pred_viz_list.append(colorize(pred_hv[idx][..., 0], -1, 1)) pred_viz_list.append(colorize(pred_hv[idx][..., 1], -1, 1)) if nr_types is not None: pred_viz_list.append(colorize(pred_tp[idx], 0, nr_types)) pred_viz_list = np.concatenate(pred_viz_list, axis=1) viz_list.append(np.concatenate([true_viz_list, pred_viz_list], axis=0)) viz_list = np.concatenate(viz_list, axis=0) return viz_list
def __run_list(self, net, dataloader, output_dir): pbar = ProgressBar('Processing', max=len(dataloader), width=48) for _, batch_data in enumerate(dataloader): imgs_input, codenames, sizes = batch_data imgs_recon = self.__infer_step(imgs_input, net) for idx in range(0, imgs_recon.shape[0]): orig_img_size = sizes[idx].numpy() recon_img = cv2.cvtColor(imgs_recon[idx], cv2.COLOR_RGB2BGR) recon_img = utils.cropping_center(recon_img, orig_img_size) cv2.imwrite('%s/%s.jpg' % (output_dir, codenames[idx]), recon_img) pbar.next() pbar.finish() return
def _augment(self, img, _): img = np.copy(img) orig_ann = img[..., 0] # instance ID map fixed_ann = self._fix_mirror_padding(orig_ann) # re-cropping with fixed instance id map crop_ann = cropping_center(fixed_ann, self.crop_shape) orig_dst = np.zeros(orig_ann.shape, dtype=np.float32) inst_list = list(np.unique(crop_ann)) inst_list.remove(0) # 0 is background for inst_id in inst_list: inst_map = np.array(fixed_ann == inst_id, np.uint8) inst_box = bounding_box(inst_map) # expand the box by 2px inst_box[0] -= 2 inst_box[2] -= 2 inst_box[1] += 2 inst_box[3] += 2 inst_map = inst_map[inst_box[0]:inst_box[1], inst_box[2]:inst_box[3]] if inst_map.shape[0] < 2 or \ inst_map.shape[1] < 2: continue # chessboard distance map generation # normalize distance to 0-1 inst_dst = distance_transform_cdt(inst_map) inst_dst = inst_dst.astype('float32') if self.inst_norm: max_value = np.amax(inst_dst) if max_value <= 0: continue # HACK: temporay patch for divide 0 i.e no nuclei (how?) inst_dst = (inst_dst / np.amax(inst_dst)) #### dst_map_box = orig_dst[inst_box[0]:inst_box[1], inst_box[2]:inst_box[3]] dst_map_box[inst_map > 0] = inst_dst[inst_map > 0] # img = img.astype('float32') img = np.dstack([img, orig_dst]) return img
def assemble_cache_patch(src_path_list, cache_dir, output_dir, patch_size, ext='.jpg'): """ If the `path_list` is in the same order as when inputted to `create_cache_patch`, the assembled will be in the same order """ path_list = glob.glob('%s/*%s' % (cache_dir, ext)) path_list.sort() basename_list = [os.path.basename(path) for path in path_list] src_idx_list = [name.split('.')[0].split('_')[0] for name in basename_list] src_idx_list = list(set(src_idx_list)) for src_idx in src_idx_list: patch_name_list = [name for name in basename_list if src_idx in name] patch_name_list.sort() src_img = cv2.imread(src_path_list[int(src_idx)]) nr_row_patches, nr_col_patches = get_patches_stat( src_img, (patch_size, patch_size)) patch_list = [ cv2.imread('%s/%s' % (cache_dir, name)) for name in patch_name_list ] img_recon = np.array(patch_list) img_recon = np.reshape( img_recon, (nr_row_patches, nr_col_patches, patch_size, patch_size, 3)) img_recon = np.transpose(img_recon, (0, 2, 1, 3, 4)) img_recon = np.reshape( img_recon, (nr_row_patches * patch_size, nr_col_patches * patch_size, 3)) img_recon = utils.cropping_center(img_recon, src_img.shape[:2]) cv2.imwrite('%s/%s.jpg' % (output_dir, src_idx), img_recon) return
def _augment(self, img, _): img = np.copy(img) orig_ann = img[..., 0] # instance ID map fixed_ann = self._fix_mirror_padding(orig_ann) # re-cropping with fixed instance id map crop_ann = cropping_center(fixed_ann, self.crop_shape) # TODO: deal with 1 label warning crop_ann = morph.remove_small_objects(crop_ann, min_size=30) x_map = np.zeros(orig_ann.shape[:2], dtype=np.float32) y_map = np.zeros(orig_ann.shape[:2], dtype=np.float32) inst_list = list(np.unique(crop_ann)) inst_list.remove(0) # 0 is background for inst_id in inst_list: inst_map = np.array(fixed_ann == inst_id, np.uint8) inst_box = bounding_box(inst_map) # expand the box by 2px # Because we first pad the ann at line 207, the bboxes # will remain valid after expansion inst_box[0] -= 2 inst_box[2] -= 2 inst_box[1] += 2 inst_box[3] += 2 inst_map = inst_map[inst_box[0]:inst_box[1], inst_box[2]:inst_box[3]] if inst_map.shape[0] < 2 or \ inst_map.shape[1] < 2: continue # instance center of mass, rounded to nearest pixel inst_com = list(measurements.center_of_mass(inst_map)) inst_com[0] = int(inst_com[0] + 0.5) inst_com[1] = int(inst_com[1] + 0.5) inst_x_range = np.arange(1, inst_map.shape[1] + 1) inst_y_range = np.arange(1, inst_map.shape[0] + 1) # shifting center of pixels grid to instance center of mass inst_x_range -= inst_com[1] inst_y_range -= inst_com[0] inst_x, inst_y = np.meshgrid(inst_x_range, inst_y_range) # remove coord outside of instance inst_x[inst_map == 0] = 0 inst_y[inst_map == 0] = 0 inst_x = inst_x.astype('float32') inst_y = inst_y.astype('float32') # normalize min into -1 scale if np.min(inst_x) < 0: inst_x[inst_x < 0] /= (-np.amin(inst_x[inst_x < 0])) if np.min(inst_y) < 0: inst_y[inst_y < 0] /= (-np.amin(inst_y[inst_y < 0])) # normalize max into +1 scale if np.max(inst_x) > 0: inst_x[inst_x > 0] /= (np.amax(inst_x[inst_x > 0])) if np.max(inst_y) > 0: inst_y[inst_y > 0] /= (np.amax(inst_y[inst_y > 0])) #### x_map_box = x_map[inst_box[0]:inst_box[1], inst_box[2]:inst_box[3]] x_map_box[inst_map > 0] = inst_x[inst_map > 0] y_map_box = y_map[inst_box[0]:inst_box[1], inst_box[2]:inst_box[3]] y_map_box[inst_map > 0] = inst_y[inst_map > 0] img = img.astype('float32') img = np.dstack([img, x_map, y_map]) return img
def gen_instance_hv_map(ann, crop_shape): """Input annotation must be of original shape. The map is calculated only for instances within the crop portion but based on the original shape in original image. Perform following operation: Obtain the horizontal and vertical distance maps for each nuclear instance. """ orig_ann = ann.copy() # instance ID map fixed_ann = fix_mirror_padding(orig_ann) # re-cropping with fixed instance id map crop_ann = cropping_center(fixed_ann, crop_shape) # TODO: deal with 1 label warning crop_ann = morph.remove_small_objects(crop_ann, min_size=30) x_map = np.zeros(orig_ann.shape[:2], dtype=np.float32) y_map = np.zeros(orig_ann.shape[:2], dtype=np.float32) inst_list = list(np.unique(crop_ann)) inst_list.remove(0) # 0 is background for inst_id in inst_list: inst_map = np.array(fixed_ann == inst_id, np.uint8) inst_box = get_bounding_box(inst_map) # expand the box by 2px # Because we first pad the ann at line 207, the bboxes # will remain valid after expansion inst_box[0] -= 2 inst_box[2] -= 2 inst_box[1] += 2 inst_box[3] += 2 inst_map = inst_map[inst_box[0]:inst_box[1], inst_box[2]:inst_box[3]] if inst_map.shape[0] < 2 or inst_map.shape[1] < 2: continue # instance center of mass, rounded to nearest pixel inst_com = list(measurements.center_of_mass(inst_map)) inst_com[0] = int(inst_com[0] + 0.5) inst_com[1] = int(inst_com[1] + 0.5) inst_x_range = np.arange(1, inst_map.shape[1] + 1) inst_y_range = np.arange(1, inst_map.shape[0] + 1) # shifting center of pixels grid to instance center of mass inst_x_range -= inst_com[1] inst_y_range -= inst_com[0] inst_x, inst_y = np.meshgrid(inst_x_range, inst_y_range) # remove coord outside of instance inst_x[inst_map == 0] = 0 inst_y[inst_map == 0] = 0 inst_x = inst_x.astype("float32") inst_y = inst_y.astype("float32") # normalize min into -1 scale if np.min(inst_x) < 0: inst_x[inst_x < 0] /= -np.amin(inst_x[inst_x < 0]) if np.min(inst_y) < 0: inst_y[inst_y < 0] /= -np.amin(inst_y[inst_y < 0]) # normalize max into +1 scale if np.max(inst_x) > 0: inst_x[inst_x > 0] /= np.amax(inst_x[inst_x > 0]) if np.max(inst_y) > 0: inst_y[inst_y > 0] /= np.amax(inst_y[inst_y > 0]) #### x_map_box = x_map[inst_box[0]:inst_box[1], inst_box[2]:inst_box[3]] x_map_box[inst_map > 0] = inst_x[inst_map > 0] y_map_box = y_map[inst_box[0]:inst_box[1], inst_box[2]:inst_box[3]] y_map_box[inst_map > 0] = inst_y[inst_map > 0] hv_map = np.dstack([x_map, y_map]) return hv_map
def __gen_prediction(self, x, predictor): """ Using 'predictor' to generate the prediction of image 'x' Args: x : input image to be segmented. It will be split into patches to run the prediction upon before being assembled back tissue: tissue mask created via otsu -> only process tissue regions if it is provided """ step_size = self.infer_output_shape msk_size = self.infer_output_shape win_size = self.infer_input_shape def get_last_steps(length, msk_size, step_size): nr_step = math.ceil((length - msk_size) / step_size) last_step = (nr_step + 1) * step_size return int(last_step), int(nr_step + 1) im_h = x.shape[0] im_w = x.shape[1] last_h, nr_step_h = get_last_steps(im_h, msk_size[0], step_size[0]) last_w, nr_step_w = get_last_steps(im_w, msk_size[1], step_size[1]) diff_h = win_size[0] - step_size[0] padt = diff_h // 2 padb = last_h + win_size[0] - im_h diff_w = win_size[1] - step_size[1] padl = diff_w // 2 padr = last_w + win_size[1] - im_w x = np.lib.pad(x, ((padt, padb), (padl, padr), (0, 0)), "reflect") #### TODO: optimize this sub_patches = [] skipped_idx = [] # generating subpatches from orginal idx = 0 for row in range(0, last_h, step_size[0]): for col in range(0, last_w, step_size[1]): win = x[row:row + win_size[0], col:col + win_size[1]] sub_patches.append(win) idx += 1 pred_map = deque() while len(sub_patches) > self.inf_batch_size: mini_batch = sub_patches[:self.inf_batch_size] sub_patches = sub_patches[self.inf_batch_size:] mini_output = predictor(mini_batch)[0] if win_size[0] > msk_size[0]: mini_output = cropping_center(mini_output, (diff_h, diff_w)) mini_output = np.split(mini_output, self.inf_batch_size, axis=0) pred_map.extend(mini_output) if len(sub_patches) != 0: mini_output = predictor(sub_patches)[0] if win_size[0] > msk_size[0]: mini_output = cropping_center(mini_output, (diff_h, diff_w)) mini_output = np.split(mini_output, len(sub_patches), axis=0) pred_map.extend(mini_output) #### Assemble back into full image output_patch_shape = np.squeeze(pred_map[0]).shape ch = 1 if len(output_patch_shape) == 2 else output_patch_shape[-1] #### Assemble back into full image pred_map = np.squeeze(np.array(pred_map)) pred_map = np.reshape(pred_map, (nr_step_h, nr_step_w) + pred_map.shape[1:]) pred_map = (np.transpose(pred_map, [0, 2, 1, 3, 4]) if ch != 1 else np.transpose(pred_map, [0, 2, 1, 3])) pred_map = np.reshape( pred_map, ( pred_map.shape[0] * pred_map.shape[1], pred_map.shape[2] * pred_map.shape[3], ch, ), ) # just crop back to original size pred_map = np.squeeze(pred_map[:im_h, :im_w]) return pred_map