def _add_submasks_and_decisions_one_section(self, sec, submasks, submask_decisions): for submask_ind, decision in submask_decisions.iteritems(): m = submasks[submask_ind] cnts = find_contour_points(m, sample_every=1)[m.max()] if len(cnts) == 0: raise Exception('ERROR: section %d %d, submask %d - no contour' % (sec, submask_ind, len(cnts))) elif len(cnts) > 1: sys.stderr.write('WARNING: section %d, submask %d - %d contours\n' % (sec, submask_ind, len(cnts))) cnt = sorted(cnts, key=lambda c: len(c), reverse=True)[0] else: cnt = cnts[0] self._submask_decisions[sec][submask_ind] = decision if decision: color = 'g' else: color = 'r' # self.add_polygon(path=vertices_to_path(cnt), section=sec, linewidth=2, color=color) resampled_contour_vertices = resample_polygon(cnt, len_interval=20) self.add_polygon_with_circles(path=vertices_to_path(resampled_contour_vertices), section=sec, linewidth=2, linecolor=color, vertex_radius=4)
def generate_mask_contour_visualization(fn): try: mask_fn = os.path.join(mask_dir, '%(fn)s_mask.png' % dict(fn=fn)) mask = imread(mask_fn) contour_xys = find_contour_points(mask.astype(np.bool), sample_every=1)[1] image_fn = os.path.join(image_dir, '%(fn)s.tif' % dict(fn=fn)) image = imread(image_fn) viz = image.copy() for cnt in contour_xys: cv2.polylines(viz, [cnt.astype(np.int)], True, (255,0,0), 1) output_fn = os.path.join(output_dir, '%(fn)s_mask_contour_viz.tif' % dict(fn=fn)) if os.path.exists(output_fn): execute_command('rm -rf %s' % output_fn) imsave(output_fn, viz) except Exception as e: sys.stderr.write('%s\n'%e.message) sys.stderr.write('Mask error: %s\n' % fn) return
def _add_submasks_and_decisions_one_section(self, sec, submasks, submask_decisions): for submask_ind, decision in submask_decisions.iteritems(): m = submasks[submask_ind] cnts = find_contour_points(m, sample_every=1)[m.max()] if len(cnts) == 0: raise Exception( 'ERROR: section %d %d, submask %d - no contour' % (sec, submask_ind, len(cnts))) elif len(cnts) > 1: sys.stderr.write( 'WARNING: section %d, submask %d - %d contours\n' % (sec, submask_ind, len(cnts))) cnt = sorted(cnts, key=lambda c: len(c), reverse=True)[0] else: cnt = cnts[0] self._submask_decisions[sec][submask_ind] = decision if decision: color = 'g' else: color = 'r' # self.add_polygon(path=vertices_to_path(cnt), section=sec, linewidth=2, color=color) resampled_contour_vertices = resample_polygon(cnt, len_interval=20) self.add_polygon_with_circles( path=vertices_to_path(resampled_contour_vertices), section=sec, linewidth=2, linecolor=color, vertex_radius=4)
def generate_mask_contour_visualization(fn, tb_fmt): try: mask_fn = os.path.join(mask_dir, '%(fn)s_mask.png' % dict(fn=fn)) mask = imread(mask_fn) contour_xys = find_contour_points(mask.astype(np.bool), sample_every=1)[1] image_fn = os.path.join(image_dir, '%(fn)s.%(tb_fmt)s' % dict(fn=fn, tb_fmt=tb_fmt)) image = imread(image_fn) if image.ndim == 2: viz = gray2rgb(image) elif image.ndim == 3: viz = image.copy() else: raise for cnt in contour_xys: cv2.polylines(viz, [cnt.astype(np.int)], True, (255,0,0), 1) output_fn = os.path.join(output_dir, '%(fn)s_mask_contour_viz.tif' % dict(fn=fn)) if os.path.exists(output_fn): execute_command('rm -rf %s' % output_fn) imsave(output_fn, viz) except Exception as e: sys.stderr.write('%s\n'%e.message) sys.stderr.write('Mask error: %s\n' % fn) return
def generate_mask(fn): try: img_rgb = imread(os.path.join(input_dir, fn + '.tif')) img = rgb2gray(img_rgb) entropy_mask = generate_entropy_mask(img) init_contours = [ xys for xys in find_contour_points(entropy_mask.astype(np.int), sample_every=1)[1] if len(xys) > 50 ] assert len(init_contours ) > 0, 'No contour is detected from entropy mask %s' % fn img_adap = threshold_adaptive(img, 51) img_adap[~entropy_mask] = 1 final_masks = [] for init_cnt in init_contours: img_adap_gauss = gaussian(img_adap.astype(np.float), 1) snake = active_contour(img_adap_gauss, init_cnt.astype(np.float), alpha=1., beta=1000., gamma=1., w_line=0., w_edge=10., max_iterations=1000) bg = np.zeros(img.shape[:2], bool) xys = points_inside_contour(snake.astype(np.int)) bg[np.minimum(xys[:, 1], bg.shape[0] - 1), np.minimum(xys[:, 0], bg.shape[1] - 1)] = 1 final_mask = bg & entropy_mask final_masks.append(final_mask) final_mask = np.any(final_masks, axis=0) mask_fn = os.path.join(output_dir, '%(fn)s_mask.png' % dict(fn=fn)) if os.path.exists(mask_fn): sys.stderr.write('Mask exists, overwrite: %s\n' % mask_fn) imsave(mask_fn, img_as_ubyte(final_mask)) except Exception as e: sys.stderr.write(e.message + '\n') sys.stderr.write('%d, Mask error: %s\n' % (len(final_masks), fn)) return
def generate_submasks_viz(img, submasks, color=(255,0,0), linewidth=3): """Generate visualization of submasks.""" viz = gray2rgb(img) for i, submask in enumerate(submasks): cnts = find_contour_points(submask) if 1 not in cnts or len(cnts[1]) == 0: sys.stderr.write('Submask %d has no contour.\n' % i) continue for cnt in cnts[1]: cv2.polylines(viz, [cnt.astype(np.int)], True, color, linewidth) # blue return viz
def save_submasks_and_decisions(self, sec): # submasks = self.user_submasks # submask_decisions = self.user_submask_decisions if sec not in self.user_submasks or sec not in self.user_submasks_gscene._submask_decisions: return fn = self.valid_sections_to_filenames[sec] # submask_fn_dir = os.path.join(submasks_dir, fn) submask_fn_dir = DataManager.get_user_modified_submask_dir_filepath(stack=self.stack, fn=fn) execute_command('rm -rf \"%(dir_fp)s\"; mkdir -p \"%(dir_fp)s\"' % {'dir_fp': submask_fn_dir}) # Save submasks for submask_ind, m in self.user_submasks[sec].iteritems(): # submask_fp = os.path.join(submask_fn_dir, fn + '_alignedTo_' + self.anchor_fn + '_submask_%d.png' % submask_ind) submask_fp = DataManager.get_user_modified_submask_filepath(stack=self.stack, fn=fn, what='submask', submask_ind=submask_ind) imsave(submask_fp, np.uint8(m)*255) # Save submask contour vertices. submask_contour_vertices_fp = DataManager.get_user_modified_submask_filepath(stack=self.stack, fn=fn, what='contour_vertices') # submask_contour_vertices_fp = os.path.join(submask_fn_dir, fn + '_alignedTo_' + self.anchor_fn + '_submask_contour_vertices.pkl') submask_contour_vertices_dict = {} for submask_ind, m in self.user_submasks[sec].iteritems(): cnts = find_contour_points(m)[1] if len(cnts) != 1: sys.stderr.write("Must have exactly one contour per submask, section %d, but the sizes are %s.\n" % (sec, map(len, cnts))) submask_contour_vertices_dict[submask_ind] = cnts[np.argsort(map(len, cnts))[-1]] save_pickle(submask_contour_vertices_dict, submask_contour_vertices_fp) # Save submask decisions. decisions_fp = DataManager.get_user_modified_submask_filepath(stack=self.stack, fn=fn, what='decisions') # decisions_fp = os.path.join(submask_fn_dir, fn +'_alignedTo_' + self.anchor_fn + '_submasksUserReview.txt') from pandas import Series Series(self.user_submasks_gscene._submask_decisions[sec]).to_csv(decisions_fp) # save_json({k: int(v) for k,v in submask_decisions[sec].iteritems()}, decisions_fp) # Save parameters. params_fp = DataManager.get_user_modified_submask_filepath(stack=self.stack, fn=fn, what='parameters') params = {} if sec in self.selected_channels: params['channel'] = self.selected_channels[sec] if sec in self.selected_snake_lambda1: params['snake_lambda1'] = self.selected_snake_lambda1[sec] if sec in self.selected_snake_min_size: params['min_size'] = self.selected_snake_min_size[sec] if len(params) > 0: save_json(params, params_fp)
def snake(img, init_submasks=None, init_contours=None, lambda1=MORPHSNAKE_LAMBDA1, return_masks=True, min_size=MIN_SUBMASK_SIZE, crop_margin=50): """ Args: crop_margin (int): crop the image to fit the extent of all masks plus a margin of `margin`. Set to negative for no cropping. """ if init_contours is None: assert init_submasks is not None, "If no init_contours is given, must provide init_submasks." init_submasks = [ m for m in init_submasks if np.count_nonzero(init_submasks) <= min_size ] assert len( init_submasks ) > 0, "No initial submasks are above the minimal area of %d." % min_size assert all([ (m.shape[0] == img.shape[0]) & (m.shape[1] == img.shape[1]) for m in init_submasks ]), "Not all shapes of initial submasks match the input image." bbox_all_submasks = [bbox_2d(m) for m in init_submasks] xmin, ymin = np.min(bbox_all_submasks[:, [0, 2]]) xmax, ymax = np.max(bbox_all_submasks[:, [1, 3]]) else: init_contours = [c.astype(np.int) for c in init_contours] xmin, ymin = np.min([np.min(c, axis=0) for c in init_contours], axis=0) xmax, ymax = np.max([np.max(c, axis=0) for c in init_contours], axis=0) # Crop to fit the extent. if crop_margin >= 0: crop_xmin = max(0, xmin - crop_margin) crop_ymin = max(0, ymin - crop_margin) crop_xmax = min(img.shape[1] - 1, xmax + crop_margin) crop_ymax = min(img.shape[0] - 1, ymax + crop_margin) cropped_img = img[crop_ymin:crop_ymax + 1, crop_xmin:crop_xmax + 1] else: crop_xmin = 0 crop_ymin = 0 crop_xmax = img.shape[1] - 1 crop_ymax = img.shape[0] - 1 cropped_img = img # Form initial levelsets init_levelsets = [] if init_contours is None: for m in init_submasks: init_levelset = m[crop_ymin:crop_ymax + 1, crop_xmin:crop_xmax + 1] init_levelset[:10, :] = 0 init_levelset[-10:, :] = 0 init_levelset[:, :10] = 0 init_levelset[:, -10:] = 0 init_levelsets.append(init_levelset) else: for cnt in init_contours: init_contours_on_cropped_img = cnt - (crop_xmin, crop_ymin) init_levelset = contours_to_mask([init_contours_on_cropped_img], cropped_img.shape[:2]) init_levelset[:10, :] = 0 init_levelset[-10:, :] = 0 init_levelset[:, :10] = 0 init_levelset[:, -10:] = 0 init_levelsets.append(init_levelset) sys.stderr.write('Found %d levelsets.\n' % len(init_levelsets)) ##################### # Evolve morphsnake # ##################### final_masks = [] for levelset_ind, init_levelset in enumerate(init_levelsets): sys.stderr.write('\nContour %d\n' % levelset_ind) discard = False init_area = np.count_nonzero(init_levelset) t = time.time() msnake = morphsnakes.MorphACWE(cropped_img.astype(np.float), smoothing=int(MORPHSNAKE_SMOOTHING), lambda1=lambda1, lambda2=MORPHSNAKE_LAMBDA2) msnake.levelset = init_levelset.copy() dq = deque([None, None]) for i in range(MORPHSNAKE_MAXITER): # At stable stage, the levelset (thus contour) will oscilate, # so instead of comparing to previous levelset, must compare to the one before the previous oneBefore_levelset = dq.popleft() # If less than 3 pixels are changed, stop. if i > MORPHSNAKE_MINITER: if np.count_nonzero(msnake.levelset - oneBefore_levelset ) < PIXEL_CHANGE_TERMINATE_CRITERIA: break # area = np.count_nonzero(msnake.levelset) # if area < min_size: # discard = True # sys.stderr.write('Too small, stop iteration.\n') # break labeled_mask = label(msnake.levelset.astype(np.bool)) component_sizes = [] for l in np.unique(labeled_mask): if l != 0: m = labeled_mask == l component_area = np.count_nonzero(m) component_sizes.append(component_area) if component_area / float( init_area) > AREA_CHANGE_RATIO_MAX: msnake.levelset[m] = 0 sys.stderr.write( 'Component area expands too much - nullified.\n') elif component_area < min_size: msnake.levelset[m] = 0 sys.stderr.write( 'Component area is too small - nullified.\n') print component_sizes if np.count_nonzero(msnake.levelset) / float( init_area) < AREA_CHANGE_RATIO_MIN: discard = True sys.stderr.write('Area shrinks too much, stop iteration.\n') break dq.append(msnake.levelset) # t = time.time() msnake.step() # sys.stderr.write('Step: %f seconds\n' % (time.time()-t)) # 0.6 second/step, roughly 200 steps takes 120s sys.stderr.write('Snake finished at iteration %d.\n' % i) sys.stderr.write('Snake: %.2f seconds\n' % (time.time() - t)) # 72s if discard: sys.stderr.write('Discarded.\n') continue else: # Handles the case that a single initial contour morphs into multiple contours labeled_mask = label(msnake.levelset.astype(np.bool)) for l in np.unique(labeled_mask): if l != 0: m = labeled_mask == l if np.count_nonzero(m) > min_size: final_masks.append(m) sys.stderr.write('Final masks added.\n') if len(final_masks) == 0: sys.stderr.write('Snake return no valid submasks.\n') return [] if return_masks: final_masks_uncropped = [] for m in final_masks: uncropped_mask = np.zeros(img.shape[:2], np.bool) uncropped_mask[crop_ymin:crop_ymax + 1, crop_xmin:crop_xmax + 1] = m final_masks_uncropped.append(uncropped_mask) return final_masks_uncropped else: final_contours = [] for m in final_masks: cnts = [ cnt_on_cropped + (crop_xmin, crop_ymin) for cnt_on_cropped in find_contour_points(m)[1] ] final_contours += cnts return final_contours
def generate_mask(fn, tb_fmt='png'): try: submask_dir = create_if_not_exists(os.path.join(output_dir, fn)) execute_command('rm -f %s/*' % submask_dir) img_rgb = imread( os.path.join(input_dir, '%(fn)s.%(tb_fmt)s' % dict(fn=fn, tb_fmt=tb_fmt))) stds = [] images = [] # for c in range(3): for c in [0]: img = img_rgb[..., c].copy() border = np.median(np.r_[img[:10, :].flatten(), img[-10:, :].flatten(), img[:, :10].flatten(), img[:, -10:].flatten()]) if border < 123: # dark background, fluorescent img = img.max( ) - img # invert, make tissue dark on bright background # Stretch contrast img_flattened = img.flatten() vmax_perc = VMAX_PERCENTILE while vmax_perc > 80: vmax = np.percentile(img_flattened, vmax_perc) if vmax < 255: break else: vmax_perc -= 1 vmin_perc = VMIN_PERCENTILE while vmin_perc < 20: vmin = np.percentile(img_flattened, vmin_perc) if vmin > 0: break else: vmin_perc += 1 sys.stderr.write('%d(%d percentile), %d(%d percentile)\n' % (vmin, vmin_perc, vmax, vmax_perc)) img = img_as_ubyte(rescale_intensity(img, in_range=(vmin, vmax))) # img[(img <= vmax) & (img >= vmin)] = 255./(vmax-vmin)*(img[(img <= vmax) & (img >= vmin)]-vmin) # img[img > vmax] = 255 # img[img < vmin] = 0 # img = img.astype(np.uint8) images.append(img) std = np.std(img) stds.append(std) sys.stderr.write('std: %.2f\n' % (std)) best_channel_id = np.argmax(stds) sys.stderr.write('Use channel %s.\n' % ['RED', 'GREEN', 'BLUE'][best_channel_id]) img = images[best_channel_id] ############################# ## Graph cut based method. ## ############################# # Input to slic() must be float slic_labels = slic(img.astype(np.float), sigma=SLIC_SIGMA, compactness=SLIC_COMPACTNESS, n_segments=SLIC_N_SEGMENTS, multichannel=False, max_iter=SLIC_MAXITER) # sim_graph = rag_mean_color(img, slic_labels, mode='similarity', sigma=SUPERPIXEL_SIMILARITY_SIGMA) # Normalized cut - merge superpixels. # for _ in range(3): # try: # t = time.time() # ncut_labels = cut_normalized(slic_labels, sim_graph, in_place=False, thresh=SUPERPIXEL_MERGE_SIMILARITY_THRESH, # num_cuts=GRAPHCUT_NUM_CUTS) # sys.stderr.write('Normalized Cut: %.2f seconds.\n' % (time.time() - t)) # break # except ArpackError as e: # sys.stderr.write('ArpackError encountered.\n') # continue # ncut_boundaries_viz = mark_boundaries(img, label_img=ncut_labels, background_label=-1) # imsave(os.path.join(submask_dir, '%(fn)s_.png' % dict(fn=fn)), ncut_boundaries_viz) ncut_labels = slic_labels # Find background superpixels. background_labels = np.unique( np.concatenate([ ncut_labels[:, 0], ncut_labels[:, -1], ncut_labels[0, :], ncut_labels[-1, :] ])) # Collect training superpixels. border_histos = [] for b in background_labels: histo = np.histogram(img[ncut_labels == b], bins=np.arange(0, 256, 5))[0].astype(np.float) histo = histo / np.sum(histo) border_histos.append(histo) histos = {} for l in np.unique(ncut_labels): histo = np.histogram(img[ncut_labels == l], bins=np.arange(0, 256, 5))[0].astype(np.float) histo = histo / np.sum(histo) histos[l] = histo hist_distances = {} for l, h in histos.iteritems(): hist_distances[l] = np.percentile( [chi2(h, th) for th in border_histos], BORDER_DISSIMILARITY_PERCENTILE) # min is too sensitive if there is a blob at the border if FOREGROUND_DISSIMILARITY_THRESHOLD is None: # Automatically determine this value dist_vals = np.asarray(hist_distances.values()) ticks = np.linspace(0, dist_vals.max(), 100) percentages = [ np.count_nonzero(dist_vals < th) / float(len(dist_vals)) for th in ticks ] def moving_average(interval, window_size): window = np.ones(int(window_size)) / float(window_size) return np.convolve(interval, window, 'same') grad = np.gradient(percentages, 3) # smoothed_grad = moving_average(grad, 1) hessian = np.gradient(grad, 3) # plt.figure(); # plt.plot(ticks, grad, label='grad'); # plt.plot(ticks, smoothed_grad, label='smoothed'); # plt.legend(); # plt.xlabel('Chi2 distance'); # plt.savefig(os.path.join(submask_dir, '%(fn)s_spDissimCumDistGradient.png' % dict(fn=fn))); # plt.close(); # plt.figure(); # plt.plot(ticks, h); # plt.title('Hessian - minima is the plateau point of cum. distr.'); # plt.xlabel('Dissimlarity threshold'); # plt.savefig(os.path.join(submask_dir, '%(fn)s_spDissimCumDistHessian.png' % dict(fn=fn))); # plt.close(); # ticks_sorted = ticks[np.argsort(grad, kind='mergesort')] # ticks_sorted = ticks[np.argsort(smoothed_grad, kind='mergesort')] ticks_sorted = ticks[10:][hessian[10:].argsort()] ticks_sorted_reduced = ticks_sorted[ ticks_sorted < FOREGROUND_DISSIMILARITY_THRESHOLD_MAX] init_contour_percentages = np.asarray([ np.sum([ np.count_nonzero(ncut_labels == l) for l, d in hist_distances.iteritems() if d > th ]) / float(img.size) for th in ticks_sorted_reduced ]) threshold_candidates = ticks_sorted_reduced[(init_contour_percentages < INIT_CONTOUR_COVERAGE_MAX) &\ (init_contour_percentages > 0)] # np.savetxt(os.path.join(submask_dir, '%(fn)s_spThreshCandidates.txt' % dict(fn=fn)), threshold_candidates, fmt='%.3f') print threshold_candidates[:10] foreground_dissimilarity_threshold = threshold_candidates[0] else: foreground_dissimilarity_threshold = FOREGROUND_DISSIMILARITY_THRESHOLD sys.stderr.write('FOREGROUND_DISSIMILARITY_THRESHOLD: %.2f\n' % foreground_dissimilarity_threshold) # Visualize superpixel border distance map superpixel_border_distances = np.zeros_like(img, np.float) for l, s in hist_distances.iteritems(): superpixel_border_distances[ncut_labels == l] = s # plt.figure(); # im = plt.imshow(superpixel_border_distances, vmin=0, vmax=2); # plt.title('Superpixels distance to border'); # plt.colorbar(im, fraction=0.025, pad=0.02); # plt.savefig(os.path.join(submask_dir, '%(fn)s_spBorderDissim.png' % dict(fn=fn))); # plt.close(); # Generate mask for snake's initial contours. superpixel_mask = np.zeros_like(img, np.bool) for l, d in hist_distances.iteritems(): if d > foreground_dissimilarity_threshold: superpixel_mask[ncut_labels == l] = 1 superpixel_mask = remove_small_objects(superpixel_mask, min_size=MIN_SIZE) labelmap, n_submasks = label(superpixel_mask, return_num=True) # superpixel_submasks = [] dilated_superpixel_submasks = [] for i in range(1, n_submasks + 1): m = labelmap == i # superpixel_submasks.append(m) dilated_m = binary_dilation(m, disk(10)) dilated_m = remove_small_objects(dilated_m, min_size=MIN_SIZE) dilated_superpixel_submasks.append(dilated_m) # Visualize # viz = img_as_ubyte(ncut_boundaries_viz) # for submask in superpixel_submasks: # for cnt in find_contour_points(submask)[1]: # cv2.polylines(viz, [cnt.astype(np.int)], True, (255,0,0), 1) # red # for submask in dilated_superpixel_submasks: # for cnt in find_contour_points(submask)[1]: # cv2.polylines(viz, [cnt.astype(np.int)], True, (0,0,255), 1) # blue # imsave(os.path.join(submask_dir, '%(fn)s_ncutSubmasks.png' % dict(fn=fn)), viz) ##################### # Find contours from mask. init_contours = [] for submask in dilated_superpixel_submasks: cnts = find_contour_points(submask.astype(np.int), sample_every=1) if 1 not in cnts or len(cnts[1]) == 0: continue for cnt in cnts[1]: if len(cnt) > INIT_CONTOUR_MINLEN: init_contours.append(cnt) # assert len(init_contours) > 0, 'No contour is detected from entropy mask %s' % fn sys.stderr.write('Extracted %d contours from mask.\n' % len(init_contours)) # Create initial levelset init_levelsets = [] for cnt in init_contours: init_levelset = np.zeros_like(img, np.float) init_levelset[contours_to_mask([cnt], img.shape[:2])] = 1. init_levelset[:10, :] = 0 init_levelset[-10:, :] = 0 init_levelset[:, :10] = 0 init_levelset[:, -10:] = 0 init_levelsets.append(init_levelset) ####################### # Binary Thresholding # ####################### img_enhanced = img ##################### # Evolve morphsnake # ##################### final_masks = [] for init_levelset in init_levelsets: discard = False init_area = np.count_nonzero(init_levelset) t = time.time() msnake = morphsnakes.MorphACWE(img_enhanced.astype(np.float), smoothing=int(MORPHSNAKE_SMOOTHING), lambda1=MORPHSNAKE_LAMBDA1, lambda2=MORPHSNAKE_LAMBDA2) msnake.levelset = init_levelset.copy() dq = deque([None, None]) for i in range(MORPHSNAKE_MAXITER): # At stable stage, the levelset (thus contour) will oscilate, # so instead of comparing to previous levelset, must compare to the one before the previous oneBefore_levelset = dq.popleft() # If less than 3 pixels are changed, stop. if i > MORPHSNAKE_MINITER: if np.count_nonzero(msnake.levelset - oneBefore_levelset ) < PIXEL_CHANGE_TERMINATE_CRITERIA: break area = np.count_nonzero(msnake.levelset) if area < MIN_SIZE: discard = True sys.stderr.write('Too small, stop iteration.\n') break labeled_mask = label(msnake.levelset.astype(np.bool)) for l in np.unique(labeled_mask): if l != 0: m = labeled_mask == l if np.count_nonzero(m) / float( init_area) > AREA_CHANGE_RATIO_MAX: msnake.levelset[m] = 0 sys.stderr.write('Area nullified.\n') if np.count_nonzero(msnake.levelset) / float( init_area) < AREA_CHANGE_RATIO_MIN: discard = True sys.stderr.write( 'Area shrinks too much, stop iteration.\n') break dq.append(msnake.levelset) # t = time.time() msnake.step() # sys.stderr.write('Step: %f seconds\n' % (time.time()-t)) # 0.6 second / step sys.stderr.write('Snake finished at iteration %d.\n' % i) sys.stderr.write('Snake: %.2f seconds\n' % (time.time() - t)) # 72s if discard: sys.stderr.write('Discarded.\n') continue else: # Handles the case that a single initial contour morphs into multiple contours labeled_mask = label(msnake.levelset.astype(np.bool)) for l in np.unique(labeled_mask): if l != 0: m = labeled_mask == l if np.count_nonzero(m) > MIN_SIZE: final_masks.append(m) sys.stderr.write('Final masks added.\n') ############ ## Export ## ############ # submask_viz_dir = create_if_not_exists('/home/yuncong/CSHL_data_processed/%(stack)s/%(stack)s_submask_overlayViz/%(fn)s' % dict(stack=stack, fn=fn)) # execute_command('rm %s/*' % submask_viz_dir) # img_rgb = imread(os.path.join(input_dir, fn + '.' + tb_fmt)) # all_init_cnts = [] # for i, init_levelset in enumerate(init_levelsets): # all_init_cnts += find_contour_points(init_levelset)[1] for i, mask in enumerate(final_masks): imsave( os.path.join(submask_dir, '%(fn)s_submask_%(i)d.png' % { 'fn': fn, 'i': i }), img_as_ubyte(mask)) # viz = img_rgb.copy() # # cnts = find_contour_points(mask) # if len(cnts) == 0: # raise # for cnt in cnts[1]: # cv2.polylines(viz, [cnt.astype(np.int)], True, (255,0,0), 3) # red # # for cnt in all_init_cnts: # cv2.polylines(viz, [cnt.astype(np.int)], True, (0,255,0), 3) # green # viz_fn = os.path.join(submask_dir, '%(fn)s_submask_%(i)d_overlayViz.png' % dict(fn=fn, i=i+1)) # sys.stderr.write(viz_fn + '\n') # imsave(viz_fn, viz) ################################################ # Automatically judge the goodness of each mask ################################################ submasks = final_masks n = len(final_masks) rank1 = np.argsort([np.count_nonzero(m) for m in submasks])[::-1] image_center = np.r_[submasks[0].shape[1] / 2, submasks[0].shape[0] / 2] bbox_to_image_center_distance = [] for m in submasks: xmin, xmax, ymin, ymax = bbox_2d(m) dist = np.sqrt( np.sum((image_center - ((xmin + xmax) / 2, (ymin + ymax) / 2))**2)) bbox_to_image_center_distance.append(dist) rank2 = np.argsort(bbox_to_image_center_distance) r1 = np.asarray( [r for r, i in sorted(enumerate(rank1), key=lambda (r, i): i)]) r2 = np.asarray( [r for r, i in sorted(enumerate(rank2), key=lambda (r, i): i)]) rank = np.argsort( r1 + 1.01 * r2) # weight being close to center a bit more to break tie best_mask_ind = rank[0] decisions = [False for _ in range(n)] decisions[best_mask_ind] = True np.savetxt(os.path.join(submask_dir, '%(fn)s_submasksAlgReview.txt' % dict(fn=fn)), decisions, fmt='%d') except Exception as e: sys.stderr.write('%s\n' % e) sys.stderr.write('Mask error: %s\n' % fn) return
sys.stderr.write('Too few voxels %d.\n' % nz) continue init_cnt_xmin, init_cnt_xmax, init_cnt_ymin, init_cnt_ymax = bbox_2d(vol[..., z]) margin = 20 crop_xmin = init_cnt_xmin - margin crop_ymin = init_cnt_ymin - margin crop_xmax = init_cnt_xmax + margin crop_ymax = init_cnt_ymax + margin print crop_xmin, crop_xmax, crop_ymin, crop_ymax # score volume resol. : thumbnail resol init_mask = vol[crop_ymin:crop_ymax+1, crop_xmin:crop_xmax+1, z] > .3 contours = find_contour_points(init_mask, sample_every=1)[1] assert len(contours) == 1 init_contour = contours[0] # Crop around initial contour. img = imread(DataManager.get_image_filepath(stack=stack, section=sec)) # lossless resol. img_cropped = img[crop_ymin*32:crop_ymax*32, crop_xmin*32:crop_xmax*32] im_cropped_h, im_cropped_w = img_cropped.shape[:2] print im_cropped_w, im_cropped_h # plt.figure(figsize=(20,20)); # plt.imshow(img_cropped) # plt.title('Image cropped');
img_fn = DataManager.get_image_filepath(stack=stack_fixed, section=sec, resol='thumbnail', version='cropped_tif') img = imread(img_fn) viz = img.copy() z = voxel_z_size * (sec - 1) - zmin_vol_f # Find fixed volume annotation contours # contours_f_on_volume = find_contour_points(volume_fixed[..., int(z)]) # contours_f_on_cropped = {i: [cnt + (xmin_vol_f, ymin_vol_f) for cnt in cnts] for i, cnts in contours_f_on_volume.iteritems()} # Find moving volume annotation contours for stack, volume_m_aligned_to_f in annotation_volumes_volume_m_aligned_to_f.iteritems(): contours_m_alignedTo_f_on_volume = find_contour_points(volume_m_aligned_to_f[..., int(z)]) contours_m_alignedTo_f_on_cropped = {i: [cnt + (xmin_vol_f, ymin_vol_f) for cnt in cnts] for i, cnts in contours_m_alignedTo_f_on_volume.iteritems()} # # Draw fixed volume annotation contours # for ind_f, cnts_f in contours_f_on_cropped.iteritems(): # for cnt_f in cnts_f: # cv2.polylines(viz, [cnt_f.astype(np.int)], True, (0,255,0), 2) # Draw moving volume annotation contours for ind_m, cnts_m in contours_m_alignedTo_f_on_cropped.iteritems(): for cnt_m in cnts_m: cv2.polylines(viz, [cnt_m.astype(np.int)], True, stack_colors[stack], 2) viz_fn = os.path.join(viz_dir, '%(stack_moving)s_to_%(stack_fixed)s_%(sec)04d.jpg' % \ {'stack_moving': stack_moving, 'stack_fixed': stack_fixed, 'sec': sec})
def snake(img, init_submasks=None, init_contours=None, lambda1=MORPHSNAKE_LAMBDA1, return_masks=True, min_size=MIN_SIZE): # Find contours from mask. if init_contours is None: init_contours = [] for submask in init_submasks: cnts = find_contour_points(submask.astype(np.int), sample_every=1) if 1 not in cnts or len(cnts[1]) == 0: continue for cnt in cnts[1]: if len(cnt) > INIT_CONTOUR_MINLEN: init_contours.append(cnt) else: init_contours = [c.astype(np.int) for c in init_contours] xmin, ymin = np.min([np.min(c, axis=0) for c in init_contours], axis=0) xmax, ymax = np.max([np.max(c, axis=0) for c in init_contours], axis=0) margin = 50 crop_xmin = max(0, xmin - margin) crop_ymin = max(0, ymin - margin) crop_xmax = min(img.shape[1], xmax + margin) crop_ymax = min(img.shape[0], ymax + margin) cropped_img = img[crop_ymin:crop_ymax+1, crop_xmin:crop_xmax+1] # cropped_img_height, cropped_img_width = cropped_img.shape[:2] init_contours_on_cropped_img = [c-(crop_xmin, crop_ymin) for c in init_contours] # init_contours = [xys for submask in submasks # for xys in find_contour_points(submask.astype(np.int), sample_every=1)[1] # if len(xys) > INIT_CONTOUR_MINLEN] sys.stderr.write('Extracted %d contours from mask.\n' % len(init_contours)) # Create initial levelset init_levelsets = [] for cnt in init_contours_on_cropped_img: init_levelset = np.zeros_like(cropped_img, np.float) t = time.time() init_levelset[contours_to_mask([cnt], cropped_img.shape[:2])] = 1. sys.stderr.write('Contour to levelset: %.2f seconds\n' % (time.time() - t)) # 10s init_levelset[:10, :] = 0 init_levelset[-10:, :] = 0 init_levelset[:, :10] = 0 init_levelset[:, -10:] = 0 init_levelsets.append(init_levelset) # img_enhanced = img.copy() ##################### # Evolve morphsnake # ##################### final_masks = [] for levelset_ind, init_levelset in enumerate(init_levelsets): sys.stderr.write('\nContour %d\n' % levelset_ind) discard = False init_area = np.count_nonzero(init_levelset) t = time.time() msnake = morphsnakes.MorphACWE(cropped_img.astype(np.float), smoothing=int(MORPHSNAKE_SMOOTHING), lambda1=lambda1, lambda2=MORPHSNAKE_LAMBDA2) msnake.levelset = init_levelset.copy() dq = deque([None, None]) for i in range(MORPHSNAKE_MAXITER): # At stable stage, the levelset (thus contour) will oscilate, # so instead of comparing to previous levelset, must compare to the one before the previous oneBefore_levelset = dq.popleft() # If less than 3 pixels are changed, stop. if i > MORPHSNAKE_MINITER: if np.count_nonzero(msnake.levelset - oneBefore_levelset) < PIXEL_CHANGE_TERMINATE_CRITERIA: break area = np.count_nonzero(msnake.levelset) if area < min_size: discard = True sys.stderr.write('Too small, stop iteration.\n') break # If area changes more than 2, stop. labeled_mask = label(msnake.levelset.astype(np.bool)) for l in np.unique(labeled_mask): if l != 0: m = labeled_mask == l if np.count_nonzero(m)/float(init_area) > AREA_CHANGE_RATIO_MAX: msnake.levelset[m] = 0 sys.stderr.write('Area expands too much - nullified.\n') if np.count_nonzero(msnake.levelset)/float(init_area) < AREA_CHANGE_RATIO_MIN: discard = True sys.stderr.write('Area shrinks too much, stop iteration.\n') break dq.append(msnake.levelset) # t = time.time() msnake.step() # sys.stderr.write('Step: %f seconds\n' % (time.time()-t)) # 0.6 second/step, roughly 200 steps takes 120s sys.stderr.write('Snake finished at iteration %d.\n' % i) sys.stderr.write('Snake: %.2f seconds\n' % (time.time()-t)) # 72s if discard: sys.stderr.write('Discarded.\n') continue else: # Handles the case that a single initial contour morphs into multiple contours labeled_mask = label(msnake.levelset.astype(np.bool)) for l in np.unique(labeled_mask): if l != 0: m = labeled_mask == l if np.count_nonzero(m) > min_size: final_masks.append(m) sys.stderr.write('Final masks added.\n') if len(final_masks) == 0: sys.stderr.write('Snake return no valid submasks.\n') if return_masks: final_masks_uncropped = [] for m in final_masks: uncropped_mask = np.zeros(img.shape[:2], np.bool) uncropped_mask[crop_ymin:crop_ymax+1, crop_xmin:crop_xmax+1] = m final_masks_uncropped.append(uncropped_mask) return final_masks_uncropped else: final_contours = [] for m in final_masks: cnts = [cnt_on_cropped + (crop_xmin, crop_ymin) for cnt_on_cropped in find_contour_points(m)[1]] final_contours += cnts return final_contours
def generate_mask(fn, tb_fmt='png'): try: submask_dir = create_if_not_exists(os.path.join(output_dir, fn)) execute_command('rm -f %s/*' % submask_dir) img_rgb = imread(os.path.join(input_dir, '%(fn)s.%(tb_fmt)s' % dict(fn=fn, tb_fmt=tb_fmt))) stds = [] images = [] # for c in range(3): for c in [0]: img = img_rgb[..., c].copy() border = np.median(np.r_[img[:10, :].flatten(), img[-10:, :].flatten(), img[:, :10].flatten(), img[:, -10:].flatten()]) if border < 123: # dark background, fluorescent img = img.max() - img # invert, make tissue dark on bright background # Stretch contrast img_flattened = img.flatten() vmax_perc = VMAX_PERCENTILE while vmax_perc > 80: vmax = np.percentile(img_flattened, vmax_perc) if vmax < 255: break else: vmax_perc -= 1 vmin_perc = VMIN_PERCENTILE while vmin_perc < 20: vmin = np.percentile(img_flattened, vmin_perc) if vmin > 0: break else: vmin_perc += 1 sys.stderr.write('%d(%d percentile), %d(%d percentile)\n' % (vmin, vmin_perc, vmax, vmax_perc) ) img = img_as_ubyte(rescale_intensity(img, in_range=(vmin, vmax))) # img[(img <= vmax) & (img >= vmin)] = 255./(vmax-vmin)*(img[(img <= vmax) & (img >= vmin)]-vmin) # img[img > vmax] = 255 # img[img < vmin] = 0 # img = img.astype(np.uint8) images.append(img) std = np.std(img) stds.append(std) sys.stderr.write('std: %.2f\n' % (std)) best_channel_id = np.argmax(stds) sys.stderr.write('Use channel %s.\n' % ['RED', 'GREEN', 'BLUE'][best_channel_id]) img = images[best_channel_id] ############################# ## Graph cut based method. ## ############################# # Input to slic() must be float slic_labels = slic(img.astype(np.float), sigma=SLIC_SIGMA, compactness=SLIC_COMPACTNESS, n_segments=SLIC_N_SEGMENTS, multichannel=False, max_iter=SLIC_MAXITER) # sim_graph = rag_mean_color(img, slic_labels, mode='similarity', sigma=SUPERPIXEL_SIMILARITY_SIGMA) # Normalized cut - merge superpixels. # for _ in range(3): # try: # t = time.time() # ncut_labels = cut_normalized(slic_labels, sim_graph, in_place=False, thresh=SUPERPIXEL_MERGE_SIMILARITY_THRESH, # num_cuts=GRAPHCUT_NUM_CUTS) # sys.stderr.write('Normalized Cut: %.2f seconds.\n' % (time.time() - t)) # break # except ArpackError as e: # sys.stderr.write('ArpackError encountered.\n') # continue # ncut_boundaries_viz = mark_boundaries(img, label_img=ncut_labels, background_label=-1) # imsave(os.path.join(submask_dir, '%(fn)s_.png' % dict(fn=fn)), ncut_boundaries_viz) ncut_labels = slic_labels # Find background superpixels. background_labels = np.unique(np.concatenate([ncut_labels[:,0], ncut_labels[:,-1], ncut_labels[0,:], ncut_labels[-1,:]])) # Collect training superpixels. border_histos = [] for b in background_labels: histo = np.histogram(img[ncut_labels == b], bins=np.arange(0,256,5))[0].astype(np.float) histo = histo/np.sum(histo) border_histos.append(histo) histos = {} for l in np.unique(ncut_labels): histo = np.histogram(img[ncut_labels == l], bins=np.arange(0,256,5))[0].astype(np.float) histo = histo/np.sum(histo) histos[l] = histo hist_distances = {} for l, h in histos.iteritems(): hist_distances[l] = np.percentile([chi2(h, th) for th in border_histos], BORDER_DISSIMILARITY_PERCENTILE) # min is too sensitive if there is a blob at the border if FOREGROUND_DISSIMILARITY_THRESHOLD is None: # Automatically determine this value dist_vals = np.asarray(hist_distances.values()) ticks = np.linspace(0, dist_vals.max(), 100) percentages = [np.count_nonzero(dist_vals < th) / float(len(dist_vals)) for th in ticks] def moving_average(interval, window_size): window = np.ones(int(window_size))/float(window_size) return np.convolve(interval, window, 'same') grad = np.gradient(percentages, 3) # smoothed_grad = moving_average(grad, 1) hessian = np.gradient(grad, 3) # plt.figure(); # plt.plot(ticks, grad, label='grad'); # plt.plot(ticks, smoothed_grad, label='smoothed'); # plt.legend(); # plt.xlabel('Chi2 distance'); # plt.savefig(os.path.join(submask_dir, '%(fn)s_spDissimCumDistGradient.png' % dict(fn=fn))); # plt.close(); # plt.figure(); # plt.plot(ticks, h); # plt.title('Hessian - minima is the plateau point of cum. distr.'); # plt.xlabel('Dissimlarity threshold'); # plt.savefig(os.path.join(submask_dir, '%(fn)s_spDissimCumDistHessian.png' % dict(fn=fn))); # plt.close(); # ticks_sorted = ticks[np.argsort(grad, kind='mergesort')] # ticks_sorted = ticks[np.argsort(smoothed_grad, kind='mergesort')] ticks_sorted = ticks[10:][hessian[10:].argsort()] ticks_sorted_reduced = ticks_sorted[ticks_sorted < FOREGROUND_DISSIMILARITY_THRESHOLD_MAX] init_contour_percentages = np.asarray([np.sum([np.count_nonzero(ncut_labels == l) for l, d in hist_distances.iteritems() if d > th]) / float(img.size) for th in ticks_sorted_reduced]) threshold_candidates = ticks_sorted_reduced[(init_contour_percentages < INIT_CONTOUR_COVERAGE_MAX) &\ (init_contour_percentages > 0)] # np.savetxt(os.path.join(submask_dir, '%(fn)s_spThreshCandidates.txt' % dict(fn=fn)), threshold_candidates, fmt='%.3f') print threshold_candidates[:10] foreground_dissimilarity_threshold = threshold_candidates[0] else: foreground_dissimilarity_threshold = FOREGROUND_DISSIMILARITY_THRESHOLD sys.stderr.write('FOREGROUND_DISSIMILARITY_THRESHOLD: %.2f\n' % foreground_dissimilarity_threshold) # Visualize superpixel border distance map superpixel_border_distances = np.zeros_like(img, np.float) for l, s in hist_distances.iteritems(): superpixel_border_distances[ncut_labels == l] = s # plt.figure(); # im = plt.imshow(superpixel_border_distances, vmin=0, vmax=2); # plt.title('Superpixels distance to border'); # plt.colorbar(im, fraction=0.025, pad=0.02); # plt.savefig(os.path.join(submask_dir, '%(fn)s_spBorderDissim.png' % dict(fn=fn))); # plt.close(); # Generate mask for snake's initial contours. superpixel_mask = np.zeros_like(img, np.bool) for l, d in hist_distances.iteritems(): if d > foreground_dissimilarity_threshold: superpixel_mask[ncut_labels == l] = 1 superpixel_mask = remove_small_objects(superpixel_mask, min_size=MIN_SIZE) labelmap, n_submasks = label(superpixel_mask, return_num=True) # superpixel_submasks = [] dilated_superpixel_submasks = [] for i in range(1, n_submasks+1): m = labelmap == i # superpixel_submasks.append(m) dilated_m = binary_dilation(m, disk(10)) dilated_m = remove_small_objects(dilated_m, min_size=MIN_SIZE) dilated_superpixel_submasks.append(dilated_m) # Visualize # viz = img_as_ubyte(ncut_boundaries_viz) # for submask in superpixel_submasks: # for cnt in find_contour_points(submask)[1]: # cv2.polylines(viz, [cnt.astype(np.int)], True, (255,0,0), 1) # red # for submask in dilated_superpixel_submasks: # for cnt in find_contour_points(submask)[1]: # cv2.polylines(viz, [cnt.astype(np.int)], True, (0,0,255), 1) # blue # imsave(os.path.join(submask_dir, '%(fn)s_ncutSubmasks.png' % dict(fn=fn)), viz) ##################### # Find contours from mask. init_contours = [] for submask in dilated_superpixel_submasks: cnts = find_contour_points(submask.astype(np.int), sample_every=1) if 1 not in cnts or len(cnts[1]) == 0: continue for cnt in cnts[1]: if len(cnt) > INIT_CONTOUR_MINLEN: init_contours.append(cnt) # assert len(init_contours) > 0, 'No contour is detected from entropy mask %s' % fn sys.stderr.write('Extracted %d contours from mask.\n' % len(init_contours)) # Create initial levelset init_levelsets = [] for cnt in init_contours: init_levelset = np.zeros_like(img, np.float) init_levelset[contours_to_mask([cnt], img.shape[:2])] = 1. init_levelset[:10, :] = 0 init_levelset[-10:, :] = 0 init_levelset[:, :10] = 0 init_levelset[:, -10:] = 0 init_levelsets.append(init_levelset) ####################### # Binary Thresholding # ####################### img_enhanced = img ##################### # Evolve morphsnake # ##################### final_masks = [] for init_levelset in init_levelsets: discard = False init_area = np.count_nonzero(init_levelset) t = time.time() msnake = morphsnakes.MorphACWE(img_enhanced.astype(np.float), smoothing=int(MORPHSNAKE_SMOOTHING), lambda1=MORPHSNAKE_LAMBDA1, lambda2=MORPHSNAKE_LAMBDA2) msnake.levelset = init_levelset.copy() dq = deque([None, None]) for i in range(MORPHSNAKE_MAXITER): # At stable stage, the levelset (thus contour) will oscilate, # so instead of comparing to previous levelset, must compare to the one before the previous oneBefore_levelset = dq.popleft() # If less than 3 pixels are changed, stop. if i > MORPHSNAKE_MINITER: if np.count_nonzero(msnake.levelset - oneBefore_levelset) < PIXEL_CHANGE_TERMINATE_CRITERIA: break area = np.count_nonzero(msnake.levelset) if area < MIN_SIZE: discard = True sys.stderr.write('Too small, stop iteration.\n') break labeled_mask = label(msnake.levelset.astype(np.bool)) for l in np.unique(labeled_mask): if l != 0: m = labeled_mask == l if np.count_nonzero(m)/float(init_area) > AREA_CHANGE_RATIO_MAX: msnake.levelset[m] = 0 sys.stderr.write('Area nullified.\n') if np.count_nonzero(msnake.levelset)/float(init_area) < AREA_CHANGE_RATIO_MIN: discard = True sys.stderr.write('Area shrinks too much, stop iteration.\n') break dq.append(msnake.levelset) # t = time.time() msnake.step() # sys.stderr.write('Step: %f seconds\n' % (time.time()-t)) # 0.6 second / step sys.stderr.write('Snake finished at iteration %d.\n' % i) sys.stderr.write('Snake: %.2f seconds\n' % (time.time()-t)) # 72s if discard: sys.stderr.write('Discarded.\n') continue else: # Handles the case that a single initial contour morphs into multiple contours labeled_mask = label(msnake.levelset.astype(np.bool)) for l in np.unique(labeled_mask): if l != 0: m = labeled_mask == l if np.count_nonzero(m) > MIN_SIZE: final_masks.append(m) sys.stderr.write('Final masks added.\n') ############ ## Export ## ############ # submask_viz_dir = create_if_not_exists('/home/yuncong/CSHL_data_processed/%(stack)s/%(stack)s_submask_overlayViz/%(fn)s' % dict(stack=stack, fn=fn)) # execute_command('rm %s/*' % submask_viz_dir) # img_rgb = imread(os.path.join(input_dir, fn + '.' + tb_fmt)) # all_init_cnts = [] # for i, init_levelset in enumerate(init_levelsets): # all_init_cnts += find_contour_points(init_levelset)[1] for i, mask in enumerate(final_masks): imsave(os.path.join(submask_dir,'%(fn)s_submask_%(i)d.png' % {'fn': fn, 'i':i}), img_as_ubyte(mask)) # viz = img_rgb.copy() # # cnts = find_contour_points(mask) # if len(cnts) == 0: # raise # for cnt in cnts[1]: # cv2.polylines(viz, [cnt.astype(np.int)], True, (255,0,0), 3) # red # # for cnt in all_init_cnts: # cv2.polylines(viz, [cnt.astype(np.int)], True, (0,255,0), 3) # green # viz_fn = os.path.join(submask_dir, '%(fn)s_submask_%(i)d_overlayViz.png' % dict(fn=fn, i=i+1)) # sys.stderr.write(viz_fn + '\n') # imsave(viz_fn, viz) ################################################ # Automatically judge the goodness of each mask ################################################ submasks = final_masks n = len(final_masks) rank1 = np.argsort([np.count_nonzero(m) for m in submasks])[::-1] image_center = np.r_[submasks[0].shape[1]/2, submasks[0].shape[0]/2] bbox_to_image_center_distance = [] for m in submasks: xmin, xmax, ymin, ymax = bbox_2d(m) dist = np.sqrt(np.sum((image_center - ((xmin + xmax)/2, (ymin+ymax)/2))**2)) bbox_to_image_center_distance.append(dist) rank2 = np.argsort(bbox_to_image_center_distance) r1 = np.asarray([r for r, i in sorted(enumerate(rank1), key=lambda (r,i): i)])