def skel(self, seed, truth): """ resize_z dimension of all inputs """ ### (2) resize seed #seed = batch_x[0][1].cpu().data.numpy() skel = skeletonize_3d(seed) """ Link to center """ center = [15, 39, 30] degrees, coordinates = bw_skel_and_analyze(skel) coord_end = np.transpose(np.vstack(np.where(degrees == 1))) for coord in coord_end: #print(np.linalg.norm(center - coord)) if np.linalg.norm(center - coord) <= 10: line_coords = line_nd(center, coord, endpoint=False) line_coords = np.transpose(line_coords) skel[line_coords[:, 0], line_coords[:, 1], line_coords[:, 2]] = 1 skel[center[0], center[1], center[2]] = 1 seed_skel = skel seed_skel[seed_skel > 0] = 255 ### (3) resize truth #truth = np.asarray(batch_y[0].cpu().data.numpy(), dtype=np.float64) #truth[truth > 0] = 255 skel = skeletonize_3d(truth) """ Link to center """ center = [15, 39, 39] degrees, coordinates = bw_skel_and_analyze(skel) coord_end = np.transpose(np.vstack(np.where(degrees == 1))) for coord in coord_end: #print(np.linalg.norm(center - coord)) if np.linalg.norm(center - coord) <= 10: line_coords = line_nd(center, coord, endpoint=False) line_coords = np.transpose(line_coords) skel[line_coords[:, 0], line_coords[:, 1], line_coords[:, 2]] = 1 skel[center[0], center[1], center[2]] = 1 """ Dilate to ball """ truth_skel = skel return seed_skel, truth_skel
def draw_tube_from_edt(img, vertex0, vertex1, radius): """ Generate a segmentation mask of a tube connecting known vertices. Parameters ------- img : cloudvolume.volumecutout.VolumeCutout The volume to segment. vertex0 : tuple A vertex containing a coordinate within a known segment. vertex1 : tuple A vertex containing a coordinate within a known segment. radius : float The radius of the cylinder. Returns ------- labels : numpy.ndarray An array consisting of the pixelwise segmentation. """ line = draw.line_nd(vertex0, vertex1, endpoint=True) line_array = np.ones(img.shape, dtype=int) line_array[line] = 0 seg = distance_transform_edt(line_array) labels = np.where(seg <= radius, 1, 0) return labels
def draw_tube_from_spheres(img, vertex0, vertex1, radius): """ Generate a segmentation mask of a tube (series of spheres) connecting known vertices. Parameters ------- img : cloudvolume.volumecutout.VolumeCutout The volume to segment. vertex0 : tuple A vertex containing a coordinate within a known segment. vertex1 : tuple A vertex containing a coordinate within a known segment. radius : float The radius of the cylinder. Returns ------- labels : numpy.ndarray An array consisting of the pixelwise segmentation. """ line = draw.line_nd(vertex0, vertex1, endpoint=True) line = np.array(line).T seg = np.zeros(img.shape) for pt in line: s = draw_sphere(img.shape, pt, radius) seg += s labels = np.where(seg >= 1, 1, 0) return labels
def connect_nearby_px(coords): """ must only look at UNIQUE elements """ coords = np.unique(coords, axis=0) clf = NearestNeighbors(n_neighbors=3).fit(coords) distances, indices = clf.kneighbors(coords) ### need to connect FOR 2nd NEAREST NEIGHBOR ind_to_c = np.where(distances[:, 1] >= 2)[0] full_coords = [] full_coords.append(coords) for ind in ind_to_c: start = indices[ind][0] end = indices[ind][1] line_coords = line_nd(coords[start], coords[end], endpoint=False) line_coords = np.transpose(line_coords) full_coords.append(line_coords[1:len(line_coords)]) ### don't reappend the starting coordinate #full_coords = np.vstack(full_coords) #full_coords = order_coords(full_coords) ### NEED TO CONNECT FOR 3rd NEAREST NEIGHBOR ind_to_c = np.where(distances[:, 2] >= 2)[0] #full_coords = [] #full_coords.append(coords) for ind in ind_to_c: start = indices[ind][0] end = indices[ind][2] line_coords = line_nd(coords[start], coords[end], endpoint=False) line_coords = np.transpose(line_coords) full_coords.append(line_coords[1:len(line_coords)]) ### don't reappend the starting coordinate full_coords = np.vstack(full_coords) return full_coords
def tubes_from_paths( size: Tuple[int, int, int], paths: List[List[int]], radius: Optional[Union[float, int]] = None, ): """Constructs tubes from list of paths. Returns densely labeled paths within the shape of the image. Arguments: size: The size of image to consider. paths: The list of paths. Each path is a list of points along the path (non-dense). radius: The radius of the line to draw. Default is None = 1 pixel wide line. """ check_size(size) for path in paths: [check_iterable_type(vert, (int, np.integer)) for vert in path] if radius is not None: check_type(radius, (int, np.integer, float, np.float)) if radius <= 0: raise ValueError(f"Radius {radius} must be positive.") def _within_img(line, size): arrline = np.array(line).astype(int) arrline = arrline[:, arrline[0, :] < size[0]] arrline = arrline[:, arrline[0, :] >= 0] arrline = arrline[:, arrline[1, :] < size[1]] arrline = arrline[:, arrline[1, :] >= 0] arrline = arrline[:, arrline[2, :] < size[2]] arrline = arrline[:, arrline[2, :] >= 0] return (arrline[0, :], arrline[1, :], arrline[2, :]) coords = [[], [], []] for path in tqdm(paths): for i in range(len(path) - 1): line = draw.line_nd(path[i], path[i + 1]) line = _within_img(line, size) if len(line) > 0: coords[0] = np.concatenate((coords[0], line[0])) coords[1] = np.concatenate((coords[1], line[1])) coords[2] = np.concatenate((coords[2], line[2])) try: coords = (coords[0].astype(int), coords[1].astype(int), coords[2].astype(int)) except AttributeError: # if a list was passed coords = (coords[0], coords[1], coords[2]) if radius is not None: line_array = np.ones(size, dtype=int) line_array[coords] = 0 seg = distance_transform_edt(line_array) labels = np.where(seg <= radius, 1, 0) else: labels = np.zeros(size, dtype=int) labels[coords] = 1 return labels
def connect(region1, region2, img): X1 = find_region_boundaries(region1) X2 = find_region_boundaries(region2) Y = cdist(X1, X2) # for each boundary point of region2, find the nearest point on the boundary of # region 1, then connects the two points coords = np.where(Y == np.amin(Y, axis=0)) for n1, n2 in list(zip(coords[0], coords[1])): p1 = X1[n1, :] p2 = X2[n2, :] lin = line_nd(p1, p2) img[lin] = 1
def test_draw_tube_spheres(): """ Test if the function maps all the points within the radius of a segment line (defined by 2 given points) to 1, otherwise 0 The output array should have the same size of input image and binary values Distance between a point and the segment line: <= radius (if the point has value 1) > radius (if the point has value 0) """ ngl_session = NeuroglancerSession(url=url, url_segments=url_seg) img, _, _ = ngl_session.pull_vertex_list(2, [4], expand=True) shape = img.shape vertex0 = [ np.random.randint(shape[0] / 2), np.random.randint(shape[1]), np.random.randint(shape[2]), ] vertex1 = [ np.random.randint(shape[0] / 2, shape[0]), np.random.randint(shape[1]), np.random.randint(shape[2]), ] radius = np.random.randint(1, 4) labels = tube_seg.draw_tube_from_spheres(img, vertex0, vertex1, radius) line = draw.line_nd(vertex0, vertex1, endpoint=True) coords = np.where(labels < 1) d_bg = max(shape) for pt in np.array(coords).T: distance_min = min(np.sum((np.array(line).T - pt)**2, axis=1)) d_bg = min(distance_min, d_bg) coords = np.where(labels > 0) d_tube = 0 for pt in np.array(coords).T: distance_min = min(np.sum((np.array(line).T - pt)**2, axis=1)) d_tube = max(distance_min, d_tube) """ Verify: I. the size of output array II. if the output is binary-valued III. minimum distance between 0-valued points and the segment line is greater than radius IV. maximum distance between 1-valued points and the segment line is less than or equal to radius """ assert labels.shape == shape assert np.unique(labels).all() in [0, 1] assert d_bg > radius**2 assert d_tube <= radius**2
def define_border(img, NL, ROI, size_nhood_variance, Ray_masks): x_max = [] y_max = [] roughborder = np.zeros(np.shape(img)) J = generic_filter(img, np.std, size=size_nhood_variance) for _ in range(0, NL): Jmasked = J * Ray_masks[_] Jmasked = Jmasked * ROI w = np.where(Jmasked == np.max(Jmasked)) y_max.append(w[0][0]) x_max.append(w[1][0]) for _ in range(0, len(x_max) - 1): coords = line_nd((y_max[_], x_max[_]), (y_max[_ + 1], x_max[_ + 1])) roughborder[coords[0], coords[1]] = 1 return roughborder
def test_tubes_seg(): """ Test if the function maps all the points within the radius of polyline (defined by given vertices) to 1, otherwise 0 The output array should have the same size of input image and binary values Distance between a point and the polyline: <= radius (if the point has value 1) > radius (if the point has value 0) """ ngl_session = NeuroglancerSession(url=url, url_segments=url_seg) img, _, _ = ngl_session.pull_vertex_list(2, [4], expand=True) shape = img.shape vertices = np.random.randint(min(shape), size=(4, 3)) radius = np.random.randint(1, 4) labels = tube_seg.tubes_seg(img, vertices, radius) point = np.empty((3, 0), dtype=int) for i in range(3): lines = draw.line_nd(vertices[i], vertices[i + 1], endpoint=True) point = np.concatenate((point, np.array(lines)), axis=1) coords = np.where(labels < 1) d_bg = max(shape) for pt in np.array(coords).T: distance_min = min(np.sum((point.T - pt)**2, axis=1)) d_bg = min(distance_min, d_bg) coords = np.where(labels > 0) d_tube = 0 for pt in np.array(coords).T: distance_min = min(np.sum((point.T - pt)**2, axis=1)) d_tube = max(distance_min, d_tube) """ Verify: I. the size of output array II. if the output is binary-valued III. minimum distance between 0-valued points and the polyline is greater than radius IV. maximum distance between 1-valued points and the polyline is less than or equal to radius """ assert labels.shape == shape assert np.unique(labels).all() in [0, 1] assert d_bg > radius**2 assert d_tube <= radius**2
next_expand = next_seg['expand_be'] if len(next_expand) > 0: for be_ex, idx_inner in zip( next_expand, range(len(next_expand)) ): # loop through each be of next seg if (cur_ex[:, None] == be_ex).all(-1).any(): next_be = next_seg['center_be'][ idx_inner][0] cur_be = cur_seg['center_be'][ idx_outer][0] ### DRAW LINE line_coords = line_nd(cur_be, next_be, endpoint=False) line_coords = np.transpose(line_coords) cur_seg['bridges'].append(line_coords) match = 1 #print('bridge') ### (b) then try to find coords that match ==> ***IF MATCHED, find CLOSEST if not match: next_coords = next_seg['coords'] if len(next_coords) > 0 and ( cur_ex[:, None] == next_coords).all(-1).any(): cur_be = cur_seg['center_be'][idx_outer][0] # find distance to all coords
#FINO A QUI TUTTO OK logging.info('Refining segmentation results.') p_x = [] p_y = [] d = [] rr_arr = np.array([0]) cc_arr = np.array([0]) p = np.zeros(np.shape(im_norm)) mask = image_modify * fill matrix = np.zeros(np.shape(im_norm)) for _ in range(0, len(bordofinale_x)): raggi = np.zeros(np.shape(im_norm)) coords = line_nd((bordofinale_x[_], bordofinale_y[_]), (center[0], center[1])) raggi[coords[1][0:2], coords[0][0:2]] = 1 matrix = mask * raggi #matrix[matrix==0]=10 w = np.where(matrix == np.max(matrix)) p += matrix p_y.append(w[0][0]) p_x.append(w[1][0]) d.append(matrix[w[0][0], w[1][0]]) roughborder_n = np.zeros(np.shape(im_norm)) for _ in range(0, len(bordofinale_x) - 1): coords = line_nd((p_y[_], p_x[_]), (p_y[_ + 1], p_x[_ + 1])) roughborder_n[coords[1], coords[0]] = 1 rr_arr = np.hstack((rr_arr, coords[0])) cc_arr = np.hstack((cc_arr, coords[1]))
def resize_z_func(self, raw, seed, truth): """ resize_z dimension of all inputs """ ### (1) resize raw #raw = batch_x[0][0].cpu().data.numpy() raw_resize = resize(np.asarray(raw, dtype=np.float32), [80, 80, 80], order=1) ### (2) resize seed #seed = batch_x[0][1].cpu().data.numpy() seed = skeletonize_3d(seed) seed_resize = resize(np.asarray(seed, dtype=np.float32), [80, 80, 80], order=1) #seed_resize[seed_resize > 0] = 255 seed_resize[seed_resize <= 100] = 0 seed_resize[seed_resize >= 100] = 1 skel = skeletonize_3d(seed_resize) ### subtract out cube in middle skel[self.cube == 1] = 0 """ Link to center """ center = [39, 39, 39] degrees, coordinates = bw_skel_and_analyze(skel) coord_end = np.transpose(np.vstack(np.where(degrees == 1))) for coord in coord_end: #print(np.linalg.norm(center - coord)) if np.linalg.norm(center - coord) <= 10: line_coords = line_nd(center, coord, endpoint=False) line_coords = np.transpose(line_coords) skel[line_coords[:, 0], line_coords[:, 1], line_coords[:, 2]] = 1 skel[center[0], center[1], center[2]] = 1 seed_resize = dilate_by_ball_to_binary(skel, radius=1) seed_resize[seed_resize > 0] = 255 ### (3) resize truth #truth = np.asarray(batch_y[0].cpu().data.numpy(), dtype=np.float64) truth[truth > 0] = 255 truth = skeletonize_3d(truth) truth_resize = resize(np.asarray(truth, dtype=np.float32), [80, 80, 80], order=1) truth_resize[truth_resize <= 100] = 0 truth_resize[truth_resize >= 100] = 1 skel = skeletonize_3d(truth_resize) ### subtract out cube in middle skel[self.cube == 1] = 0 """ Link to center """ center = [39, 39, 39] degrees, coordinates = bw_skel_and_analyze(skel) coord_end = np.transpose(np.vstack(np.where(degrees == 1))) for coord in coord_end: #print(np.linalg.norm(center - coord)) if np.linalg.norm(center - coord) <= 10: line_coords = line_nd(center, coord, endpoint=False) line_coords = np.transpose(line_coords) skel[line_coords[:, 0], line_coords[:, 1], line_coords[:, 2]] = 1 skel[center[0], center[1], center[2]] = 1 """ Dilate to ball """ truth_resize = dilate_by_ball_to_binary(skel, radius=1) """ If want to load parents as well """
def test_empty_line(): coords = line_nd((1, 1, 1), (1, 1, 1)) assert len(coords) == 3 assert all(len(c) == 0 for c in coords)
def test_zero_line(): coords = line_nd((-1, -1), (2, 2)) assert_equal(coords, [[-1, 0, 1], [-1, 0, 1]])
def __rasterize(self, graph, data_roi, voxel_size, dtype, settings, mask_array=None): '''Rasterize 'graph' into an array with the given 'voxel_size''' mask = mask_array.data if mask_array is not None else None logger.debug("Rasterizing graph in %s", graph.spec.roi) # prepare output array rasterized_graph = np.zeros(data_roi.get_shape(), dtype=dtype) # Fast rasterization currently only implemented for mode ball without # inner radius set use_fast_rasterization = ( settings.mode == "ball" and settings.inner_radius_fraction is None and len(list(graph.edges)) == 0 ) if use_fast_rasterization: dims = len(rasterized_graph.shape) # get structuring element for mode ball ball_kernel = create_ball_kernel(settings.radius, voxel_size) radius_voxel = Coordinate(np.array(ball_kernel.shape)/2) data_roi_base = Roi( offset=Coordinate((0,)*dims), shape=Coordinate(rasterized_graph.shape)) kernel_roi_base = Roi( offset=Coordinate((0,)*dims), shape=Coordinate(ball_kernel.shape)) # Rasterize volume either with single voxel or with defined struct elememt for node in graph.nodes: # get the voxel coordinate, 'Coordinate' ensures integer v = Coordinate(node.location/voxel_size) # get the voxel coordinate relative to output array start v -= data_roi.get_begin() # skip graph outside of mask if mask is not None and not mask[v]: continue logger.debug( "Rasterizing node %s at %s", node.location, node.location/voxel_size - data_roi.get_begin()) if use_fast_rasterization: # Calculate where to crop the kernel mask and the rasterized array shifted_kernel = kernel_roi_base.shift(v - radius_voxel) shifted_data = data_roi_base.shift(-(v - radius_voxel)) arr_crop = data_roi_base.intersect(shifted_kernel) kernel_crop = kernel_roi_base.intersect(shifted_data) arr_crop_ind = arr_crop.get_bounding_box() kernel_crop_ind = kernel_crop.get_bounding_box() rasterized_graph[arr_crop_ind] = np.logical_or( ball_kernel[kernel_crop_ind], rasterized_graph[arr_crop_ind] ) else: if settings.color_attr is not None: c = graph.nodes[node].get(settings.color_attr) if c is None: logger.debug(f"Skipping node: {node}") continue elif np.isclose(c, 1) and not np.isclose(settings.fg_value, 1): logger.warning( f"Node {node} is being colored with color {c} according to " f"attribute {settings.color_attr} " f"but color 1 will be replaced with fg_value: {settings.fg_value}" ) else: c = 1 rasterized_graph[v] = c if settings.edges: for e in graph.edges: if settings.color_attr is not None: c = graph.edges[e].get(settings.color_attr) if c is None: continue elif np.isclose(c, 1) and not np.isclose(settings.fg_value, 1): logger.warning( f"Edge {e} is being colored with color {c} according to " f"attribute {settings.color_attr} " f"but color 1 will be replaced with fg_value: {settings.fg_value}" ) u = graph.node(e.u) v = graph.node(e.v) u_coord = Coordinate(u.location / voxel_size) v_coord = Coordinate(v.location / voxel_size) line = draw.line_nd(u_coord, v_coord, endpoint=True) rasterized_graph[line] = 1 # grow graph if not use_fast_rasterization: if settings.mode == "ball": enlarge_binary_map( rasterized_graph, settings.radius, voxel_size, settings.inner_radius_fraction, in_place=True) else: sigmas = settings.radius/voxel_size gaussian_filter( rasterized_graph, sigmas, output=rasterized_graph, mode="constant" ) # renormalize to have 1 be the highest value max_value = np.max(rasterized_graph) if max_value > 0: rasterized_graph /= max_value if mask_array is not None: # use more efficient bitwise operation when possible if settings.mode == "ball": rasterized_graph &= mask else: rasterized_graph *= mask return rasterized_graph
def bridge_end_points(output_PYTORCH, bridge_radius=2): ### (1) Find cc of every object in the current crop labelled = measure.label(output_PYTORCH) cc = measure.regionprops(labelled) all_seg = [] for cur in cc: cur_seg = { "coords": cur['coords'], "center_be": [], "expand_be": [], "bridges": [], } all_seg.append(cur_seg) ### (2) Find the end points AND(???) branchpoints pixel_graph, degrees, coordinates = bw_skel_and_analyze(output_PYTORCH) be_points = np.copy(degrees); be_points[be_points == 2] = 0; be_points[be_points > 0] = 1; labelled = measure.label(be_points) cc_be = measure.regionprops(labelled) ### (3) get pixel indices of each be AND get the expanded version of the be neighborhood as well # match the end point to the list of coords for seg in all_seg: cur_seg = seg['coords'] for be in cc_be: cur_be = be['coords'] if (cur_seg[:, None] == cur_be).all(-1).any(): seg["center_be"].append(cur_be) ### expand the cur_be neighborhood_be = expand_coord_to_neighborhood(cur_be, lower=bridge_radius, upper=bridge_radius + 1) if len(neighborhood_be) > 0: neighborhood_be = np.vstack(neighborhood_be) seg["expand_be"].append(neighborhood_be) ### (4) loop through each cc and see if be neighborhood hits nearby cc EXCLUDING itself ### if it hits, use line_nd to make connection empty = np.zeros(np.shape(output_PYTORCH)) for cur_seg, cur_idx in zip(all_seg, range(len(all_seg))): cur_expand = cur_seg['expand_be'] if len(cur_expand) > 0: for cur_ex, idx_outer in zip(cur_expand, range(len(cur_expand))): # loop through each be of current seg for next_seg, next_idx in zip(all_seg, range(len(all_seg))): # loop through all other segs if cur_idx == next_idx: continue; ### don't try to match with self ### (a) try to find an expanded neighborhood that matches match = 0 next_expand = next_seg['expand_be'] if len(next_expand) > 0: all_lines = [] dist_lines = [] for be_ex, idx_inner in zip(next_expand, range(len(next_expand))): # loop through each be of next seg if (cur_ex[:, None] == be_ex).all(-1).any(): next_be = next_seg['center_be'][idx_inner][0] cur_be = cur_seg['center_be'][idx_outer][0] ### DRAW LINE line_coords = line_nd(cur_be, next_be, endpoint=False) line_coords = np.transpose(line_coords) all_lines.append(line_coords) dist_lines.append(len(line_coords)) ### ONLY ADD THE SHORTEST LINE: if len(dist_lines) > 0: cur_seg['bridges'].append(all_lines[np.argmin(dist_lines)]) match = 1 #print('bridge') ### (b) then try to find coords that match ==> ***IF MATCHED, find CLOSEST if not match: next_coords = next_seg['coords'] if len(next_coords) > 0 and (cur_ex[:, None] == next_coords).all(-1).any(): cur_be = cur_seg['center_be'][idx_outer][0] # find distance to all coords cur = np.transpose(np.vstack(cur_be)) dist = distance.cdist(cur, next_coords) min_idx = np.argmin(dist) closest_point = next_coords[min_idx] ### DRAW LINE line_coords = line_nd(cur_be, closest_point, endpoint=False) line_coords = np.transpose(line_coords) cur_seg['bridges'].append(line_coords) print('body bridge') print(cur_be) ### debug: ensure proper points inserted """ get output image """ output = np.zeros(np.shape(output_PYTORCH)) for seg, idx in zip(all_seg, range(len(all_seg))): cur_expand = seg['bridges'] if len(cur_expand) > 0: cur_expand = np.vstack(cur_expand) output[cur_expand[:, 0], cur_expand[:, 1], cur_expand[:, 2]] = 5 cur_seg = seg['coords'] if len(cur_seg) > 0: cur_seg = np.vstack(cur_seg) output[cur_seg[:, 0], cur_seg[:, 1], cur_seg[:, 2]] = idx + 1 non_bin_output = np.copy(output) output[output > 0] = 1 return output, non_bin_output
def test_no_round(): coords = line_nd((0.5, 0), (2.5, 0), integer=False, endpoint=True) assert_equal(coords, [[0.5, 1.5, 2.5], [0, 0, 0]])