def quick_Rand(gt, pred, seq=False): counter_pairwise = fast64counter.ValueCountInt64() counter_gt = fast64counter.ValueCountInt64() counter_pred = fast64counter.ValueCountInt64() mask = (gt > 0) pred = thin_boundaries(pred, mask) gt = gt[mask].astype(np.int32) pred = pred[mask].astype(np.int32) counter_pairwise.add_values_pair32(gt, pred) counter_gt.add_values_32(gt) counter_pred.add_values_32(pred) # fetch counts frac_pairwise = counter_pairwise.get_counts()[1] frac_gt = counter_gt.get_counts()[1] frac_pred = counter_pred.get_counts()[1] #print 'frac_pairwise:', frac_pairwise #print 'frac_gt:', frac_gt #print 'frac_pred:', frac_pred # normalize to probabilities frac_pairwise = frac_pairwise.astype(np.double) / frac_pairwise.sum() frac_gt = frac_gt.astype(np.double) / frac_gt.sum() frac_pred = frac_pred.astype(np.double) / frac_pred.sum() return Rand(frac_pairwise, frac_gt, frac_pred, 0.5)
def segmentation_metrics(ground_truth, prediction, seq=False): '''Computes adjusted FRand and VI between ground_truth and prediction. Metrics from: Crowdsourcing the creation of image segmentation algorithms for connectomics, Arganda-Carreras, et al., 2015, Frontiers in Neuroanatomy ground_truth - correct labels prediction - predicted labels Boundaries (label == 0) in prediction are thinned until gone, then are masked to foreground (label > 0) in ground_truth. Return value is ((FRand, FRand_split, FRand_merge), (VI, VI_split, VI_merge)). If seq is True, then it is assumed that the ground_truth and prediction are sequences that should be processed elementwise. ''' # make non-sequences into sequences to simplify the code below if not seq: ground_truth = [ground_truth] prediction = [prediction] counter_pairwise = fast64counter.ValueCountInt64() counter_gt = fast64counter.ValueCountInt64() counter_pred = fast64counter.ValueCountInt64() for gt, pred in zip(ground_truth, prediction): mask = (gt > 0) pred = thin_boundaries(pred, mask) gt = gt[mask].astype(np.int32) pred = pred[mask].astype(np.int32) counter_pairwise.add_values_pair32(gt, pred) counter_gt.add_values_32(gt) counter_pred.add_values_32(pred) # fetch counts frac_pairwise = counter_pairwise.get_counts()[1] frac_gt = counter_gt.get_counts()[1] frac_pred = counter_pred.get_counts()[1] # normalize to probabilities frac_pairwise = frac_pairwise.astype(np.double) / frac_pairwise.sum() frac_gt = frac_gt.astype(np.double) / frac_gt.sum() frac_pred = frac_pred.astype(np.double) / frac_pred.sum() alphas = {'F-score': 0.5, 'split': 0.0, 'merge': 1.0} Rand_scores = { k: Rand(frac_pairwise, frac_gt, frac_pred, v) for k, v in alphas.items() } VI_scores = { k: VI(frac_pairwise, frac_gt, frac_pred, v) for k, v in alphas.items() } return {'Rand': Rand_scores, 'VI': VI_scores}
def overlaps(): for z_spacing in range(1, maximum_link_distance + 1): overlap_areas = fast64counter.ValueCountInt64() for Z in range(numslices - z_spacing): for xslice, yslice in work_by_chunks(labels): subimages_d1 = [ labels[yslice, xslice, Seg, Z][...].ravel() for Seg in range(numsegs) ] subimages_d2 = [ labels[yslice, xslice, Seg, Z + z_spacing][...].ravel() for Seg in range(numsegs) ] for s1 in subimages_d1: for s2 in subimages_d2: overlap_areas.add_values_pair32(s1, s2) idxs1, idxs2, overlap_areas = overlap_areas.get_counts_pair32() mask = (idxs1 > 0) & (idxs2 > 0) idxs1 = idxs1[mask] idxs2 = idxs2[mask] overlap_areas = overlap_areas[mask] print len(idxs1), "Overlaps at spacing", z_spacing for idx1, idx2, overlap_area in zip(idxs1, idxs2, overlap_areas): yield idx1, idx2, link_worth(float(areas[idx1]), float(areas[idx2]), float(overlap_area), z_spacing)
def count_overlaps_exclusionsets(numslices, numsegs, labels, link_worth): areacounter = fast64counter.ValueCountInt64() # Count areas of each label for xslice, yslice in work_by_chunks(labels): for Z in range(numslices): for Seg in range(numsegs): lbls = labels[yslice, xslice, Seg, Z][...] areacounter.add_values_32(lbls.ravel()) keys, areas = areacounter.get_counts() areas = areas[np.argsort(keys)] areas[0] = 0 # sanity check assert np.all(np.sort(keys) == np.arange(len(keys))) def exclusions(): for Z in range(numslices): print "exl numslices", Z excls = set() for xslice, yslice in work_by_chunks(labels): subimages = [ labels[yslice, xslice, Seg, Z][...].ravel() for Seg in range(numsegs) ] excls.update(set(zip(*subimages))) # filter out zeros excls = set(tuple(i for i in s if i) for s in excls) for excl in excls: if len(excl) > 1: yield excl def overlaps(): overlap_areas = fast64counter.ValueCountInt64() for Z in range(numslices - 1): for xslice, yslice in work_by_chunks(labels): subimages_d1 = [ labels[yslice, xslice, Seg, Z][...].ravel() for Seg in range(numsegs) ] subimages_d2 = [ labels[yslice, xslice, Seg, Z + 1][...].ravel() for Seg in range(numsegs) ] for s1 in subimages_d1: for s2 in subimages_d2: overlap_areas.add_values_pair32(s1, s2) idxs1, idxs2, overlap_areas = overlap_areas.get_counts_pair32() mask = (idxs1 > 0) & (idxs2 > 0) idxs1 = idxs1[mask] idxs2 = idxs2[mask] overlap_areas = overlap_areas[mask] print len(idxs1), "Overlaps" for idx1, idx2, overlap_area in zip(idxs1, idxs2, overlap_areas): yield idx1, idx2, link_worth(float(areas[idx1]), float(areas[idx2]), float(overlap_area)) return areas, exclusions(), overlaps()
def count_overlaps(depth, numsegs, labels): st = time.time() htable = fast64counter.ValueCountInt64() # Count areas of each label for D in range(depth): for Seg in range(numsegs): lbls = labels[Seg, D, :, :][...] htable.add_values(lbls.ravel()) keys, areas = htable.get_counts() areas = areas[np.argsort(keys)] areas[0] = 0 # sanity check assert np.all(np.sort(keys) == np.arange(len(keys))) def exclusions(): for D in range(depth): print "exl depth", D excls = set() for xpos in range(0, labels.shape[2], chunksize): for ypos in range(0, labels.shape[3], chunksize): subimages = [labels[Seg, D, xpos:(xpos + chunksize), ypos:(ypos + chunksize)][...].ravel() for Seg in range(numsegs)] excls.update(set(zip(*subimages))) for excl in excls: yield excl def overlaps(): overlap_areas = fast64counter.ValueCountInt64() for D in range(depth - 1): print "depth", D for xpos in range(0, labels.shape[2], chunksize): for ypos in range(0, labels.shape[3], chunksize): subimages_d1 = [labels[Seg, D, xpos:(xpos + chunksize), ypos:(ypos + chunksize)][...].ravel().astype(np.int32) for Seg in range(numsegs)] subimages_d2 = [labels[Seg, D + 1, xpos:(xpos + chunksize), ypos:(ypos + chunksize)][...].ravel().astype(np.int32) for Seg in range(numsegs)] for s1 in subimages_d1: for s2 in subimages_d2: overlap_areas.add_values_32(s1, s2) combined_idxs, overlap_areas = overlap_areas.get_counts() idxs1 = combined_idxs >> 32 idxs2 = combined_idxs & 0xffffffff mask = (idxs1 > 0) & (idxs2 > 0) idxs1 = idxs1[mask] idxs2 = idxs2[mask] overlap_areas = overlap_areas[mask] print len(idxs1), "Overlaps" return idxs1, idxs2, link_worth(areas[idxs1], areas[idxs2], overlap_areas) _overlaps = overlaps() print "Area counting and overlaps took", int(time.time() - st), "seconds" return areas, exclusions(), _overlaps
def overlaps(): overlap_areas = fast64counter.ValueCountInt64() for D in range(depth - 1): print "depth", D for xpos in range(0, labels.shape[2], chunksize): for ypos in range(0, labels.shape[3], chunksize): subimages_d1 = [labels[Seg, D, xpos:(xpos + chunksize), ypos:(ypos + chunksize)][...].ravel().astype(np.int32) for Seg in range(numsegs)] subimages_d2 = [labels[Seg, D + 1, xpos:(xpos + chunksize), ypos:(ypos + chunksize)][...].ravel().astype(np.int32) for Seg in range(numsegs)] for s1 in subimages_d1: for s2 in subimages_d2: overlap_areas.add_values_32(s1, s2) combined_idxs, overlap_areas = overlap_areas.get_counts() idxs1 = combined_idxs >> 32 idxs2 = combined_idxs & 0xffffffff mask = (idxs1 > 0) & (idxs2 > 0) idxs1 = idxs1[mask] idxs2 = idxs2[mask] overlap_areas = overlap_areas[mask] print len(idxs1), "Overlaps" return idxs1, idxs2, link_worth(areas[idxs1], areas[idxs2], overlap_areas)
def pairwise_multimatch(self, cutout1, cutout2): '''Match the segments in two cutouts using pairwise marriage :param cutout1: The area to examine for overlapping segmentation from the first volume. :param cutout2: The area to examine for overlapping segmentation from the second volume. The code in this routine is adapted from Seymour Knowles-Barley's pairwise_multimatch: https://github.com/Rhoana/rhoana/blob/29526687202921e7173b33ec909fcd6e5b9e18bf/PairwiseMatching/pairwise_multimatch.py ''' counter = fast64counter.ValueCountInt64() counter.add_values_pair32( cutout1.astype(np.int32).ravel(), cutout2.astype(np.int32).ravel()) overlap_labels1, overlap_labels2, overlap_areas = \ counter.get_counts_pair32() areacounter = fast64counter.ValueCountInt64() areacounter.add_values(np.int64(cutout1.ravel())) areacounter.add_values(np.int64(cutout2.ravel())) areas = dict(zip(*areacounter.get_counts())) # Merge with stable marrige matches best match = greatest overlap to_merge = [] to_merge_overlap_areas = [] m_preference = {} w_preference = {} # Generate preference lists for l1, l2, overlap_area in zip(overlap_labels1, overlap_labels2, overlap_areas): if l1 != 0 and l2 != 0 and\ overlap_area >= self.min_overlap_volume: if l1 not in m_preference: m_preference[l1] = [(l2, overlap_area)] else: m_preference[l1].append((l2, overlap_area)) if l2 not in w_preference: w_preference[l2] = [(l1, overlap_area)] else: w_preference[l2].append((l1, overlap_area)) def get_area(l1, l2): return [_ for _ in m_preference[l1] if _[0] == l2][0][1] # Sort preference lists for mk in m_preference.keys(): m_preference[mk] = sorted(m_preference[mk], key=lambda x: x[1], reverse=True) for wk in w_preference.keys(): w_preference[wk] = sorted(w_preference[wk], key=lambda x: x[1], reverse=True) # Prep for proposals mlist = sorted(m_preference.keys()) wlist = sorted(w_preference.keys()) mfree = mlist[:] * self.max_poly_matches engaged = {} mprefers2 = copy.deepcopy(m_preference) wprefers2 = copy.deepcopy(w_preference) # Stable marriage loop rh_logger.logger.report_event("Entering stable marriage loop") t0 = time.time() while mfree: m = mfree.pop(0) mlist = mprefers2[m] if mlist: w = mlist.pop(0)[0] fiance = engaged.get(w) if not fiance: # She's free engaged[w] = [m] rh_logger.logger.report_event( " {0} and {1} engaged".format(w, m), log_level=logging.DEBUG) elif len(fiance) < self.max_poly_matches and m not in fiance: # Allow polygamy engaged[w].append(m) rh_logger.logger.report_event( " {0} and {1} engaged".format(w, m), log_level=logging.DEBUG) else: # m proposes w wlist = list(x[0] for x in wprefers2[w]) dumped = False for current_match in fiance: if wlist.index(current_match) > wlist.index(m): # w prefers new m engaged[w].remove(current_match) engaged[w].append(m) dumped = True rh_logger.logger.report_event( " {0} dumped {1} for {2}".format( w, current_match, m), log_level=logging.DEBUG) if mprefers2[current_match]: # current_match has more w to try mfree.append(current_match) break if not dumped and mlist: # She is faithful to old fiance - look again mfree.append(m) rh_logger.logger.report_metric("Stable marriage loop time (sec)", time.time() - t0) # m_can_adopt = copy.deepcopy(overlap_labels1) # w_can_adopt = copy.deepcopy(overlap_labels1) m_partner = {} w_partner = {} t0 = time.time() for l2 in engaged.keys(): for l1 in engaged[l2]: rh_logger.logger.report_event( "Merging segments {1} and {0}.".format(l1, l2), log_level=logging.DEBUG) to_merge.append((l1, l2)) to_merge_overlap_areas.append(get_area(l1, l2)) # Track partners if l1 in m_partner: m_partner[l1].append(l2) else: m_partner[l1] = [l2] if l2 in w_partner: w_partner[l2].append(l1) else: w_partner[l2] = [l1] rh_logger.logger.report_metric("Pairwise multimatch merge time (sec)", time.time() - t0) # Join all orphans that fit overlap proportion critera (no limit) if not self.dont_join_orphans: t0 = time.time() for l1 in m_preference.keys(): # ignore any labels with a match # if l1 in m_partner.keys(): # continue l2, overlap_area = m_preference[l1][0] # ignore if this pair is already matched if l1 in m_partner.keys() and l2 in m_partner[l1]: continue overlap_ratio = overlap_area / np.float32(areas[l1]) if overlap_ratio >= self.orphan_min_overlap_ratio and \ overlap_area >= self.orphan_min_overlap_volume: rh_logger.logger.report_event( "Merging orphan segment {0} to {1} ({2} voxel overlap = {3:0.2f}%)." .format(l1, l2, overlap_area, overlap_ratio * 100), log_level=logging.DEBUG) to_merge.append((l1, l2)) to_merge_overlap_areas.append(get_area(l1, l2)) for l2 in w_preference.keys(): # ignore any labels with a match # if l2 in w_partner.keys(): # continue l1, overlap_area = w_preference[l2][0] # ignore if this pair is already matched if l2 in w_partner.keys() and l1 in w_partner[l2]: continue overlap_ratio = overlap_area / np.float32(areas[l2]) if overlap_ratio >= self.orphan_min_overlap_ratio and \ overlap_area >= self.orphan_min_overlap_volume: rh_logger.logger.report_event( "Merging orphan segment {0} to {1} ({2} voxel overlap = {3:0.2f}%)." .format(l2, l1, overlap_area, overlap_ratio * 100), log_level=logging.DEBUG) to_merge.append((l1, l2)) to_merge_overlap_areas.append(get_area(l1, l2)) rh_logger.logger.report_metric( "Pairwise multimatch orphan joining time", time.time() - t0) # # Convert from np.uint32 or whatever to int to make JSON serializable # to_merge = [(int(a), int(b)) for a, b in to_merge] to_merge_overlap_areas = map(int, to_merge_overlap_areas) return to_merge, to_merge_overlap_areas
hi_block2[direction] = 2 * halo_size if single_image_matching: lo_block1[direction] = lo_block1[direction] + halo_size lo_block2[direction] = lo_block2[direction] + halo_size hi_block1[direction] = lo_block1[direction] + 1 hi_block2[direction] = lo_block2[direction] + 1 block1_slice = tuple(slice(l, h) for l, h in zip(lo_block1, hi_block1)) block2_slice = tuple(slice(l, h) for l, h in zip(lo_block2, hi_block2)) packed_overlap1 = packed_block1[block1_slice] packed_overlap2 = packed_block2[block2_slice] print "block1", block1_slice, packed_overlap1.shape print "block2", block2_slice, packed_overlap2.shape counter = fast64counter.ValueCountInt64() counter.add_values_pair32( packed_overlap1.astype(np.int32).ravel(), packed_overlap2.astype(np.int32).ravel()) overlap_labels1, overlap_labels2, overlap_areas = counter.get_counts_pair32( ) areacounter = fast64counter.ValueCountInt64() areacounter.add_values(packed_overlap1.ravel()) areacounter.add_values(packed_overlap2.ravel()) areas = dict(zip(*areacounter.get_counts())) if Debug: from libtiff import TIFF # output full block images
def segmentation_metrics(ground_truth, prediction, seq=False, per_object=False): '''Computes adjusted FRand and VI between ground_truth and prediction. Metrics from: Crowdsourcing the creation of image segmentation algorithms for connectomics, Arganda-Carreras, et al., 2015, Frontiers in Neuroanatomy ground_truth - correct labels prediction - predicted labels Boundaries (label == 0) in prediction are thinned until gone, then are masked to foreground (label > 0) in ground_truth. Return value is ((FRand, FRand_split, FRand_merge), (VI, VI_split, VI_merge)). If seq is True, then it is assumed that the ground_truth and prediction are sequences that should be processed elementwise. ''' # make non-sequences into sequences to simplify the code below if not seq: ground_truth = [ground_truth] prediction = [prediction] counter_pairwise = fast64counter.ValueCountInt64() counter_gt = fast64counter.ValueCountInt64() counter_pred = fast64counter.ValueCountInt64() for gt, pred in zip(ground_truth, prediction): mask = (gt > 0) pred = thin_boundaries(pred, mask) gt = gt[mask].astype(np.int32) pred = pred[mask].astype(np.int32) counter_pairwise.add_values_pair32(gt, pred) counter_gt.add_values_32(gt) counter_pred.add_values_32(pred) # fetch counts tot_pairwise = counter_pairwise.get_counts()[1] frac_gt = counter_gt.get_counts()[1] frac_pred = counter_pred.get_counts()[1] # normalize to probabilities frac_pairwise = tot_pairwise.astype(np.double) / tot_pairwise.sum() frac_gt = frac_gt.astype(np.double) / frac_gt.sum() frac_pred = frac_pred.astype(np.double) / frac_pred.sum() alphas = {'F-score': 0.5, 'split': 0.0, 'merge': 1.0} Rand_scores = { k: Rand(frac_pairwise, frac_gt, frac_pred, v) for k, v in alphas.items() } finfo_scores = { k: f_info(frac_pairwise, frac_gt, frac_pred, v) for k, v in alphas.items() } vi_merge, vi_split = split_vi(pred, gt) vi = vi_merge + vi_split vi_scores = {"F-score": vi, "split": vi_split, "merge": vi_merge} result = { 'Rand': Rand_scores, 'F_Info': finfo_scores, 'VI': vi_scores, "tot_pairwise": counter_pairwise.get_counts_pair32() } if per_object: # # Compute summary statistics per object # ij, counts = counter_pairwise.get_counts() # # The label of predicted objects # i = ij & 0xffffffff # # The label of ground truth objects # j = ij >> 32 # # # of pixels per predicted object # per_object_counts = np.bincount(i, weights=counts) # # Fraction of predicted object per ground truth object # frac = counts.astype(float) / per_object_counts[i] # # Entropy is - sum(p * log2(p)) # Entropy = 0 for an exact match # entropy = -frac * np.log(frac) / np.log(2) tot_entropy = np.bincount(i, weights=entropy) unique_i = np.unique(i) # # area # area = np.bincount(np.hstack([_.flatten() for _ in prediction])) result["per_object"] = dict( object_id=unique_i.tolist(), area=area[unique_i].tolist(), overlap_area=per_object_counts[unique_i].tolist(), entropy=tot_entropy[unique_i].tolist()) return result
def condense_labels(Z, numsegs, labels): # project all labels at a given Z into a single plane, then merge any that # end up mapping to the same projected subregions (merge = remove one of # them) # # Regions in the presegmentations tend to be similar. This step merges # segments that differ only near the boundaries. # work by chunks for speed. Note that this approach splits projected # segments at chunk boundaries, but since we're just using them for # identifying similar regions, this is fine. overlap_counter = fast64counter.ValueCountInt64() sublabel_offset = 0 for xslice, yslice in work_by_chunks(labels): chunklabels = [ labels[yslice, xslice, S, Z][...] for S in range(numsegs) ] projected = chunklabels[0] > 0 for S in range(1, numsegs): projected &= chunklabels[S] > 0 # Mask out boundaries labeled_projected, num_found = ndimage_label(projected, output=np.int32) labeled_projected[labeled_projected > 0] += sublabel_offset sublabel_offset += num_found for sub in chunklabels: overlap_counter.add_values_pair32(labeled_projected.ravel(), sub.ravel()) # Build original label to set of projected label map projected_labels, original_labels, areas = overlap_counter.get_counts_pair32( ) label_to_projected = defaultdict(set) for pl, ol in zip(projected_labels, original_labels): if ol and pl: label_to_projected[ol].add(pl) # Reverse the map projected_to_label = defaultdict(list) for ol, plset in label_to_projected.iteritems(): projected_to_label[tuple(sorted(plset))].append(ol) # Build a remapper to remove merged labels remapper = np.arange(np.max(original_labels) + 1) for original_label_list in projected_to_label.itervalues(): # keep the first, but zero the rest if len(original_label_list) > 1: remapper[original_label_list[1:]] = 0 # pack the labels in the remapper final_label_count = np.sum(remapper > 0) remapper[0] = 1 # simplify next line remapper[remapper > 0] = np.arange(final_label_count + 1, dtype=np.int32) # remap the labels by chunk for xslice, yslice in work_by_chunks(labels): for S in range(numsegs): l = labels[yslice, xslice, S, Z] labels[yslice, xslice, S, Z] = remapper[l] if DEBUG: assert len(np.unique(labels[:, :, :, Z].ravel())) == final_label_count + 1 return final_label_count
def compute_split_merge_errors_2d_image(gt_image, seg_image, min_seg_size=10, epsilon=0.4, display_results=True): error_image = np.zeros(gt_image.shape, dtype=np.int32) gt_profiles, gt_nprofiles, gt_profile_sizes = split_profiles_connected_components( gt_image) #seg_profiles, seg_nprofiles, seg_profile_sizes = split_profiles_connected_components(seg_image) # Ignore regions where the ground-truth label is zero seg_profiles, seg_nprofiles, seg_profile_sizes = split_profiles_connected_components( seg_image, gt_image == 0) counter = fast64counter.ValueCountInt64() counter.add_values_pair32( gt_profiles.astype(np.int32).ravel(), seg_profiles.astype(np.int32).ravel()) overlap_labels_gt, overlap_labels_seg, overlap_areas = counter.get_counts_pair32( ) gt_valid_regions = 0 ngood_regions = 0 nsplit_errors = 0 nmerge_errors = 0 nunique_merge_errors = 0 nadjust_errors = 0 goodRegionIndex = [] good_area = 0 gt_good_area = 0 split_area = 0 merge_area = 0 adjust_area = 0 gt_total_area = 0 seg_total_area = 0 repeat_merge_errors = {} for gt_id in range(1, gt_nprofiles + 1): gt_size = float(gt_profile_sizes[gt_id]) if gt_size < min_seg_size: continue gt_valid_regions += 1 gt_total_area += gt_size valid_overlaps = np.nonzero( np.logical_and(overlap_labels_gt == gt_id, overlap_labels_seg != 0))[0] seg_counts = overlap_areas[valid_overlaps] #consider segs in order of size seg_order = np.argsort(seg_counts)[-1::-1] seg_counts = seg_counts[seg_order] seg_ids = overlap_labels_seg[valid_overlaps][seg_order] has_good_region = False has_merge_errors = False has_new_merge_errors = False has_split_errors = False has_adjust_errors = False for i, seg_id in enumerate(seg_ids): seg_size = float(seg_profile_sizes[seg_id]) if seg_size < min_seg_size: continue overlap_size = seg_counts[i] seg_total_area += overlap_size gt_ratio = float(overlap_size) / gt_size seg_ratio = float(overlap_size) / seg_size match_gt = gt_ratio > 1 - epsilon # big enough to match gt match_seg = seg_ratio > 1 - epsilon # big enough to match seg overlap_region = np.logical_and(gt_profiles == gt_id, seg_profiles == seg_id) if match_gt and match_seg: has_good_region = True goodRegionIndex.append(gt_id) error_image[overlap_region] = 1 good_area += overlap_size break elif match_gt and not match_seg: # false positive (merge error) if not seg_id in repeat_merge_errors: repeat_merge_errors[seg_id] = True has_new_merge_errors = True has_merge_errors = True error_image[overlap_region] = 2 merge_area += overlap_size elif not match_gt and match_seg: # false negative (split error) has_split_errors = True error_image[overlap_region] = 3 split_area += overlap_size else: # both has_adjust_errors = True error_image[overlap_region] = 4 adjust_area += overlap_size if has_good_region: ngood_regions += 1 gt_good_area += gt_size elif has_merge_errors: nmerge_errors += 1 if has_new_merge_errors: nunique_merge_errors += 1 elif has_split_errors: nsplit_errors += 1 elif has_adjust_errors: nadjust_errors += 1 if display_results: # dx, dy = np.gradient(gt_image) # gt_boundaries = boundary = np.logical_or(dx!=0, dy!=0) # dx, dy = np.gradient(seg_image) # seg_boundaries = boundary = np.logical_or(dx!=0, dy!=0) # color_boundary_image = np.zeros((gt_image.shape[0], gt_image.shape[1], 3), dtype=np.uint8) # color_boundary_image[:,:,0] = seg_boundaries * 255 # color_boundary_image[:,:,1] = gt_boundaries * 255 # color_boundary_image[:,:,2] = seg_boundaries * 255 # figure(figsize=(20,20)) # imshow(color_boundary_image) color_error_image = np.zeros((gt_image.shape[0], gt_image.shape[1], 3), dtype=np.uint8) color_error_image[:, :, 0] = np.logical_or(error_image == 2, error_image == 4) * 255 color_error_image[:, :, 1] = (error_image == 1) * 255 color_error_image[:, :, 2] = np.logical_or(error_image == 3, error_image == 4) * 255 figure(figsize=(20, 20)) imshow(color_error_image) print '{0:8d} good (2d) regions ={1:6.2f}% of regions ={2:6.2f}% of pixels.'.format( ngood_regions, float(ngood_regions) / gt_valid_regions * 100, float(gt_good_area) / gt_total_area * 100) print '{0:8d} split error regions ={1:6.2f}% of regions ={2:6.2f}% of pixels.'.format( nsplit_errors, float(nsplit_errors) / gt_valid_regions * 100, float(split_area) / gt_total_area * 100) print '{0:8d} merge error regions ={1:6.2f}% of regions ={2:6.2f}% of pixels.'.format( nmerge_errors, float(nmerge_errors) / gt_valid_regions * 100, float(merge_area) / gt_total_area * 100) print '{0:8d} Umerge error regions ={1:6.2f}% of regions ={2:6.2f}% of pixels.'.format( nunique_merge_errors, float(nunique_merge_errors) / gt_valid_regions * 100, float(merge_area) / gt_total_area * 100) print '{0:8d} adjust error regions ={1:6.2f}% of regions ={2:6.2f}% of pixels.'.format( nadjust_errors, float(nadjust_errors) / gt_valid_regions * 100, float(adjust_area) / gt_total_area * 100) return ngood_regions, nsplit_errors, nmerge_errors, nunique_merge_errors, nadjust_errors, gt_valid_regions, error_image