def calc_dist_map(seg): '''Creator of dist map''' res = np.zeros_like(seg) posmask = seg.astype(np.bool) if posmask.any(): negmask = ~posmask res = distance(negmask) * negmask - (distance(posmask) - 1) * posmask return res
def calc_dist_map(seg: np.ndarray) -> np.ndarray: res = np.zeros_like(seg) posmask = seg.astype(np.bool) if posmask.any(): negmask = ~posmask res = distance(negmask) * negmask - (distance(posmask) - 1) * posmask return res
def one_hot2dist(label): label = np.array(label) C = len(label) dist = np.zeros_like(label) for c in range(C): posmask = label[c].astype(np.bool) if posmask.any(): negmask = ~posmask dist[c] = distance(negmask) * negmask - (distance(posmask) - 1) * posmask return dist
def calc_dist_map(seg): """Calculate the distance map for a ground truth segmentation.""" # Form a boolean mask from "soft" labels, which are set to 0.95 for FFN. posmask = (seg >= 0.95).astype(np.bool) assert posmask.any( ), "ground truth must contain at least one active voxel" negmask = ~posmask res = distance(negmask) * negmask - (distance(posmask) - 1) * posmask res /= max_dist return res
def one_hot2dist(seg: np.ndarray) -> np.ndarray: B = seg.shape[0] C = seg.shape[1] res = np.zeros_like(seg) for b in range(B): for c in range(C): posmask = seg[b,c].astype(np.bool) if posmask.any(): negmask = ~posmask res[b,c] = distance(negmask) * negmask - (distance(posmask) - 1) * posmask return res
def one_hot2dist(self, label): assert one_hot(torch.Tensor(label), axis=0) C: int = len(label) dist = np.zeros_like(label) for c in range(C): posmask = label[c].astype(np.bool) if posmask.any(): negmask = ~posmask dist[c] = distance(negmask) * negmask - (distance(posmask) - 1) * posmask return dist
def one_hot2dist(posmask): # Input: Mask. Will be converted to Bool. # Author: Rakshit Kothari assert len(posmask.shape) == 2 h, w = posmask.shape res = np.zeros_like(posmask) posmask = posmask.astype(np.bool) mxDist = np.sqrt((h - 1)**2 + (w - 1)**2) if posmask.any(): negmask = ~posmask res = distance(negmask) * negmask - (distance(posmask) - 1) * posmask return res / mxDist
def one_hot2dist(seg: np.ndarray) -> np.ndarray: assert one_hot(torch.Tensor(seg), axis=0) C: int = len(seg) res = np.zeros_like(seg) for c in range(C): posmask = seg[c].astype(np.bool) if posmask.any(): negmask = ~posmask res[c] = distance(negmask) * negmask - (distance(posmask) - 1) * posmask return res
def one_hot2dist(seg: np.ndarray) -> np.ndarray: """https://raw.githubusercontent.com/LIVIAETS/surface-loss/master/utils.py""" assert one_hot(torch.Tensor(seg), axis=0) C: int = len(seg) res = np.zeros_like(seg) for c in range(C): posmask = seg[c].astype(np.bool) if posmask.any(): negmask = ~posmask res[c] = distance(negmask) * negmask - (distance(posmask) - 1) * posmask return res
def one_hot2dist(seg): n_classes = seg.shape[1] posmask = seg[:, 1:n_classes, :, :] # BACKGROUND is skipped (!) posmask = posmask.astype(np.bool) res = np.zeros_like(posmask) if posmask.any(): negmask = ~posmask res = distance(negmask) * negmask - (distance(posmask) - 1) * posmask return res
def one_hot2dist(seg): """ Given a NCLASSES x HEIGHT x WIDTH segmentation matrix it returns the corresponding distance map per-classes. """ C = seg.shape[0] res = np.zeros_like(seg) for c in range(1, C): # background is excluded (C=0) posmask = seg[c].astype(np.bool) if posmask.any(): negmask = ~posmask res[c] = distance(negmask) * negmask - (distance(posmask) - 1) * posmask return res
def ecl_distance(A): B=np.zeros((A.shape[0],128,128,3)) for I in range(A.shape[0]): for J in range(A.shape[3]): t=distance(np.squeeze(1-A[I,:,:,J]))-distance(np.squeeze(A[I,:,:,J])) # t=normalize(t) t=np.float32(t)/64 t[t>1]=1 t[t<-1]=-1 t = MaxPooling2D((2, 2)) (np.reshape(t,(1,256,256,1))) t=np.float64(t) B[I,:,:,J]=np.squeeze(t) return B
def one_hot2dist(seg): # seg:np.ndarray assert one_hot(torch.Tensor(seg), axis=0) C = len(seg) # int # res = torch.zeros_like(seg) seg = seg.numpy() res = np.zeros_like(seg) # convert res to numpy.ndarry for c in range(C): posmask = seg[c].astype(np.bool) if posmask.any(): negmask = ~posmask res[c] = distance(negmask) * negmask - (distance(posmask) - 1) * posmask res = torch.from_numpy(res) return res
def compute_distance_map(mask: np.ndarray) -> np.ndarray: """ Computes the euclidean distance map of the given segmentation mask. Expected shape: (C, H, W) """ num_classes = len(mask) result = np.zeros_like(mask) for c in range(num_classes): posmask = mask[c].astype(np.bool) if posmask.any(): negmask = ~posmask result[c] = (distance(negmask) * negmask) - ( (distance(posmask) - 1) * posmask) return result / 255.0
def one_hot2dist(seg: np.ndarray, resolution: Tuple[float, float, float] = None) -> np.ndarray: assert one_hot(torch.tensor(seg), axis=0) K: int = len(seg) res = np.zeros_like(seg) for k in range(K): posmask = seg[k].astype(np.bool) if posmask.any(): negmask = ~posmask res[k] = distance(negmask, sampling=resolution) * negmask \ - (distance(posmask, sampling=resolution) - 1) * posmask # The idea is to leave blank the negative classes # since this is one-hot encoded, another class will supervise that pixel return res
def compute_fore_dist(segmentation): """ compute the foreground of binary mask input: segmentation, shape = (batch_size, class, x, y, z) output: the Signed Distance Map (SDM) sdm(x) = 0; x in segmentation boundary -inf|x-y|; x in segmentation +inf|x-y|; x out of segmentation """ # print(type(segmentation), segmentation.shape) segmentation = segmentation.astype(np.uint8) if len(segmentation.shape) == 4: # 3D image segmentation = np.expand_dims(segmentation, 1) normalized_sdf = np.zeros(segmentation.shape) if segmentation.shape[1] == 1: dis_id = 0 else: dis_id = 1 for b in range(segmentation.shape[0]): # batch size for c in range(dis_id, segmentation.shape[1]): # class_num # ignore background posmask = segmentation[b][c] posdis = distance(posmask) normalized_sdf[b][c] = posdis / np.max(posdis) return normalized_sdf
def one_hot2dist(posmask): # Input: Mask. Will be converted to Bool. h, w = posmask.shape mxDist = np.sqrt((h-1)**2 + (w-1)**2) if np.any(posmask): assert len(posmask.shape) == 2 res = np.zeros_like(posmask) posmask = posmask.astype(np.bool) if posmask.any(): negmask = ~posmask res = distance(negmask) * negmask - (distance(posmask) - 1) * posmask res = res/mxDist else: # No valid element exists for that category res = np.zeros_like(posmask) return res
def _cv_small_disk(image_size): """Generates a disk level set function. The disk covers half of the image along its smallest dimension. """ res = np.ones(image_size) centerY = int((image_size[0] - 1) / 2) centerX = int((image_size[1] - 1) / 2) res[centerY, centerX] = 0. radius = float(min(centerX, centerY)) / 2.0 return (radius - distance(res)) / (radius * 3)
def gen_dist_map(self, label): """ :param label: numpy array, [1, w, h] :return: """ res = np.zeros_like(label, np.float32) posmask = np.zeros_like(label, np.float32) negmask = np.zeros_like(label, np.float32) posmask[label >= 0.2] = 1 posmask[label < 0.2] = 0 negmask[label >= 0.2] = 0 negmask[label < 0.2] = 1 if posmask.sum() >= 1: res = distance(negmask) * negmask - (distance(posmask) - 1) * posmask return res
def _cv_small_disk(image_size): """Generates a disk level set function. The disk covers half of the image along its smallest dimension. """ res = np.ones(image_size) centerY = int((image_size[0]-1) / 2) centerX = int((image_size[1]-1) / 2) res[centerY, centerX] = 0. radius = float(min(centerX, centerY)) / 2.0 return (radius-distance(res)) / (radius*3)
def compute_sdf(img_gt, out_shape): """ compute the signed distance map of binary mask input: segmentation, shape = (batch_size, x, y, z) output: the Signed Distance Map (SDM) sdf(x) = 0; x in segmentation boundary -inf|x-y|; x in segmentation +inf|x-y|; x out of segmentation normalize sdf to [-1,1] """ # print(type(segmentation), segmentation.shape) img_gt = img_gt.astype(np.uint8) normalized_sdf = np.zeros(out_shape) for b in range(out_shape[0]): # batch size for c in range(out_shape[1]): posmask = img_gt[b].astype(np.bool) if posmask.any(): negmask = 1 - posmask posdis = distance(posmask) negdis = distance(negmask) boundary = skimage_seg.find_boundaries( posmask, mode='inner').astype(np.uint8) sdf = (negdis - np.min(negdis)) / (np.max(negdis) - np.min( negdis)) - (posdis - np.min(posdis)) / (np.max(posdis) - np.min(posdis)) sdf[boundary == 1] = 0 normalized_sdf[b][c] = sdf assert np.min(sdf) == -1.0, print(np.min(posdis), np.max(posdis), np.min(negdis), np.max(negdis)) assert np.max(sdf) == 1.0, print(np.min(posdis), np.min(negdis), np.max(posdis), np.max(negdis)) return normalized_sdf
def compute_sdf(img_gt): img_gt = img_gt.astype(np.uint8) normalized_sdf = np.zeros( [img_gt.shape[0], 1, img_gt.shape[2], img_gt.shape[3]]) for b in range(normalized_sdf.shape[0]): # batch size posmask = img_gt[b, 1, :, :].astype(np.bool) negmask = img_gt[b, 0, :, :].astype(np.bool) if posmask.any(): posdis = distance(posmask) negdis = distance(negmask) boundary = skimage_seg.find_boundaries( posmask, mode='inner').astype(np.uint8) sdf = (negdis - np.min(negdis)) / ( np.max(negdis) - np.min(negdis) ) - (posdis - np.min(posdis)) / (np.max(posdis) - np.min(posdis)) sdf[boundary == 1] = 0 normalized_sdf[b, 0, :, :] = sdf return normalized_sdf
def compute_dtm(img_gt, out_shape, normalize=False, fg=False): """ compute the distance transform map of foreground in binary mask input: segmentation, shape = (batch_size, x, y, z) output: the foreground Distance Map (SDM) dtm(x) = 0; x in segmentation boundary inf|x-y|; x in segmentation """ fg_dtm = np.zeros(out_shape) for b in range(out_shape[0]): # batch size posmask = img_gt[b].astype(np.bool) if not fg: if posmask.any(): negmask = 1 - posmask posdis = distance(posmask) negdis = distance(negmask) boundary = skimage_seg.find_boundaries( posmask, mode='inner').astype(np.uint8) if normalize: fg_dtm[b] = (negdis - np.min(negdis)) / ( np.max(negdis) - np.min(negdis)) + (posdis - np.min( posdis)) / (np.max(posdis) - np.min(posdis)) else: fg_dtm[b] = posdis + negdis fg_dtm[b][boundary == 1] = 0 else: if posmask.any(): posdis = distance(posmask) boundary = skimage_seg.find_boundaries( posmask, mode='inner').astype(np.uint8) if normalize: fg_dtm[b] = (posdis - np.min(posdis)) / (np.max(posdis) - np.min(posdis)) else: fg_dtm[b] = posdis fg_dtm[b][boundary == 1] = 0 return fg_dtm
def compute_sdf_np(arr, truncate_value=20): """ compute the signed distance map of binary mask input: segmentation, shape = (x, y, z) output: the Signed Distance Map (SDM) sdf(t) = 0; t in segmentation boundary +inf|t-b|; x in segmentation -inf|t-b|; x out of segmentation normalize sdf to [-1,1] """ posmask = arr.astype(np.bool) if posmask.any(): negmask = ~posmask posdis = distance(posmask) negdis = distance(negmask) posdis[posdis > truncate_value] = truncate_value negdis[negdis > truncate_value] = truncate_value boundary = skimage_seg.find_boundaries(posmask, mode='inner').astype(np.uint8) tsdf = (posdis - np.min(posdis)) / (np.max(posdis) - np.min(posdis)) - \ (negdis - np.min(negdis)) / (np.max(negdis) - np.min(negdis)) tsdf[boundary == 1] = 0 return tsdf
def save_sdf(gt_path=None): ''' generate SDM for gt segmentation ''' import nibabel as nib dir_path = 'C:/Seolen/PycharmProjects/semi_seg/semantic-semi-supervised-master/model/gan_sdfloss3D_0229_04/test' gt_path = dir_path + '/00_gt.nii.gz' gt_img = nib.load(gt_path) gt = gt_img.get_data().astype(np.uint8) posmask = gt.astype(np.bool) negmask = ~posmask posdis = distance(posmask) negdis = distance(negmask) boundary = skimage_seg.find_boundaries(posmask, mode='inner').astype(np.uint8) # sdf = (negdis - np.min(negdis)) / (np.max(negdis) - np.min(negdis)) - (posdis - np.min(posdis)) / ( np.max(posdis) - np.min(posdis)) sdf = (posdis - np.min(posdis)) / (np.max(posdis) - np.min(posdis)) sdf[boundary == 1] = 0 sdf = sdf.astype(np.float32) sdf = nib.Nifti1Image(sdf, gt_img.affine) save_path = dir_path + '/00_sdm_pos.nii.gz' nib.save(sdf, save_path)
def compute_dtm(img_gt, out_shape): """ compute the distance transform map of foreground in binary mask input: segmentation, shape = (batch_size, x, y, z) output: the foreground Distance Map (SDM) dtm(x) = 0; x in segmentation boundary inf|x-y|; x in segmentation """ fg_dtm = np.zeros(out_shape) for b in range(out_shape[0]): # batch size for c in range(out_shape[1]): posmask = img_gt[b].astype(np.bool) if posmask.any(): posdis = distance(posmask) fg_dtm[b][c] = posdis return fg_dtm
def calc_DM_edge(seg): """ Computes Non-Signed (Euclidean) Distance Map of input ground-truth volume CONTOURS using scipy function. It separately computes 2D Distance Maps for each single slice in the volume. Args: seg: 3D binary volume of ground truth contours Returns: res: distance map """ res = np.zeros_like(seg) posmask = seg.astype(np.bool) for i in range(seg.shape[2]): pos = posmask[:, :, i] if pos.any(): neg = ~pos res[:, :, i] = distance(neg) return res
def compute_dtm01(img_gt, out_shape): """ compute the normalized distance transform map of foreground in binary mask input: segmentation, shape = (batch_size, x, y, z) output: the foreground Distance Map (SDM) shape=out_shape sdf(x) = 0; x in segmentation boundary inf|x-y|; x in segmentation 0; x out of segmentation normalize sdf to [0, 1] """ normalized_dtm = np.zeros(out_shape) for b in range(out_shape[0]): # batch size # ignore background for c in range(1, out_shape[1]): posmask = img_gt[b].astype(np.bool) if posmask.any(): posdis = distance(posmask) normalized_dtm[b][c] = posdis / np.max(posdis) return normalized_dtm
def dis(mask): bg = 1 - mask bg_edt = distance(bg) bg_edt2 = (np.max(bg_edt) - bg_edt) * bg return bg_edt2 / np.max(bg_edt2)