def segmentation_to_scribble_points(segmentation, points=20): """ return two array filled with positive and negative points coordinate for the given segmentation. """ # convert segmentation into positive and negative distance map dt_pos = edt.edt(segmentation) dt_neg = -edt.edt(1-segmentation) dt_neg = dt_neg - dt_neg.min() dt_neg[segmentation == 1] = 0 # flatten and sort distance map coord = np.arange(segmentation.size).flatten() dt_pos_flat = dt_pos.flatten() argsort = np.argsort(dt_pos_flat) coord_flat_pos_sorted = np.flip(np.take_along_axis(coord, argsort, axis=0)) coord = np.arange(segmentation.size).flatten() dt_neg_flat = dt_neg.flatten() argsort = np.argsort(dt_neg_flat) coord_flat_neg_sorted = np.flip(np.take_along_axis(coord, argsort, axis=0)) # create points pos_points = [] neg_points = [] seg_size = np.sum(segmentation) background_size = segmentation.size - np.sum(segmentation) for _ in range(points): a = abs(int(np.random.normal(loc=0.0, scale=seg_size//4))) pos_points.append(np.unravel_index(coord_flat_pos_sorted[a], segmentation.shape)) a = abs(int(np.random.normal(loc=0.0, scale=background_size//4))) neg_points.append(np.unravel_index(coord_flat_neg_sorted[a], segmentation.shape)) return pos_points, neg_points
def test_2d_even_anisotropy(): labels = np.zeros((15, 15), dtype=bool, order='F') labels[2:12, 2:12] = True img = edt.edt(labels, anisotropy=(1, 1), order='F') for i in range(1, 150): w = float(i) aimg = edt.edt(labels, anisotropy=(w, w)) assert np.all(w * img == aimg)
def test_3d_even_anisotropy(): labels = np.zeros((15, 15, 15), dtype=bool, order='F') labels[2:12, 2:12, 5:10] = True img = edt.edt(labels, anisotropy=(1, 1, 1)) for parallel in (1, 2): for i in range(1, 150): w = float(i) aimg = edt.edt(labels, anisotropy=(w, w, w), parallel=parallel) assert np.all(w * img == aimg)
def test_2d_lopsided_anisotropic(size): def gen(x, y, order): x = np.zeros((x, y), dtype=np.uint32, order=order) x[0:25, 5:50] = 3 x[25:50, 5:50] = 1 x[60:110, 5:50] = 2 return x cres = edt.edt(gen(size[0], size[1], 'C'), anisotropy=(2, 3), order='C') fres = edt.edt(gen(size[0], size[1], 'F'), anisotropy=(2, 3), order='F') assert np.all(np.isclose(cres, fres))
def test_3d_lopsided(size): def gen(x, y, z, order): x = np.zeros((x, y, z), dtype=np.uint32, order=order) x[0:25, 5:50, 0:25] = 3 x[25:50, 5:50, 0:25] = 1 x[60:110, 5:50, 0:25] = 2 return x cres = edt.edt(gen(size[0], size[1], size[2], 'C'), order='C') fres = edt.edt(gen(size[0], size[1], size[2], 'F'), order='F') assert np.all(np.isclose(cres, fres))
def test_voxel_connectivity_graph_2d(): labels = np.array([ [1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1], ]) omni = 0b111111 noxf = 0b111110 noxb = 0b111101 graph = np.array([ [omni, omni, omni, omni, omni, omni], [omni, omni, omni, omni, omni, omni], [omni, omni, omni, omni, omni, omni], [omni, omni, omni, omni, omni, omni], [omni, omni, omni, omni, omni, omni], ], dtype=np.uint8) dt = edt.edt(labels, voxel_graph=graph) assert np.all(dt == np.inf) dt = edt.edt(labels, voxel_graph=graph, black_border=True) assert np.all(dt == np.array( [[0.5, 0.5, 0.5, 0.5, 0.5, 0.5], [0.5, 1.5, 1.5, 1.5, 1.5, 0.5], [0.5, 1.5, 2.5, 2.5, 1.5, 0.5], [0.5, 1.5, 1.5, 1.5, 1.5, 0.5], [0.5, 0.5, 0.5, 0.5, 0.5, 0.5]])) graph = np.array([ [omni, omni, omni, omni, omni, omni], [omni, omni, omni, omni, omni, omni], [omni, omni, noxf, noxb, omni, omni], [omni, omni, omni, omni, omni, omni], [omni, omni, omni, omni, omni, omni], ], dtype=np.uint8, order="C") dt = edt.edt(labels, voxel_graph=graph, black_border=True) ans = np.array([[1, 1, 1, 1, 1, 1], [1, 1.8027756, 1.118034, 1.118034, 1.8027756, 1], [1, 1.5, 0.5, 0.5, 1.5, 1], [1, 1.8027756, 1.118034, 1.118034, 1.8027756, 1], [1, 1, 1, 1, 1, 1]]) assert np.all(np.abs(dt - ans)) < 0.000002 graph = np.asfortranarray(graph) dt = edt.edt(labels, voxel_graph=graph, black_border=True) assert np.all(np.abs(dt - ans)) < 0.000002
def test_3d_scipy_comparison(dtype, parallel, order): for _ in range(5): randos = np.random.randint(0, 2, size=(100, 100, 100), dtype=dtype) labels = np.zeros( (randos.shape[0] + 2, randos.shape[1] + 2, randos.shape[2] + 2), dtype=dtype, order=order) # Scipy requires zero borders labels[1:-1, 1:-1, 1:-1] = randos print("INPUT") print(labels) print("MLAEDT") mlaedt_result = edt.edt(labels, black_border=False, order=order, parallel=parallel) print(mlaedt_result) print("SCIPY") scipy_result = ndimage.distance_transform_edt(labels) print(scipy_result) print("DIFF") print(np.abs(scipy_result == mlaedt_result)) print(np.max(np.abs(scipy_result - mlaedt_result))) assert np.all(np.abs(scipy_result - mlaedt_result) < 0.000001)
def compute_mask_raster(input_raster, vector_mask, output_raster, blend_distance=20, only_max_coords_feature=False): if not os.path.exists(input_raster): log.ODM_WARNING("Cannot mask raster, %s does not exist" % input_raster) return if not os.path.exists(vector_mask): log.ODM_WARNING("Cannot mask raster, %s does not exist" % vector_mask) return log.ODM_INFO("Computing mask raster: %s" % output_raster) with rasterio.open(input_raster, 'r') as rast: with fiona.open(vector_mask) as src: burn_features = src if only_max_coords_feature: max_coords_count = 0 max_coords_feature = None for feature in src: if feature is not None: # No complex shapes if len(feature['geometry']['coordinates'] [0]) > max_coords_count: max_coords_count = len( feature['geometry']['coordinates'][0]) max_coords_feature = feature if max_coords_feature is not None: burn_features = [max_coords_feature] shapes = [feature["geometry"] for feature in burn_features] out_image, out_transform = mask(rast, shapes, nodata=0) if blend_distance > 0: if out_image.shape[0] >= 4: # alpha_band = rast.dataset_mask() alpha_band = out_image[-1] dist_t = edt(alpha_band, black_border=True, parallel=0) dist_t[dist_t <= blend_distance] /= blend_distance dist_t[dist_t > blend_distance] = 1 np.multiply(alpha_band, dist_t, out=alpha_band, casting="unsafe") else: log.ODM_WARNING( "%s does not have an alpha band, cannot blend cutline!" % input_raster) with rasterio.open(output_raster, 'w', BIGTIFF="IF_SAFER", **rast.profile) as dst: dst.colorinterp = rast.colorinterp dst.write(out_image) return output_raster
def feather_raster(input_raster, output_raster, blend_distance=20): if not os.path.exists(input_raster): log.ODM_WARNING("Cannot feather raster, %s does not exist" % input_raster) return log.ODM_INFO("Computing feather raster: %s" % output_raster) with rasterio.open(input_raster, 'r') as rast: out_image = rast.read() if blend_distance > 0: if out_image.shape[0] >= 4: alpha_band = out_image[-1] dist_t = edt(alpha_band, black_border=True, parallel=0) dist_t[dist_t <= blend_distance] /= blend_distance dist_t[dist_t > blend_distance] = 1 np.multiply(alpha_band, dist_t, out=alpha_band, casting="unsafe") else: log.ODM_WARNING( "%s does not have an alpha band, cannot feather raster!" % input_raster) with rasterio.open(output_raster, 'w', **rast.profile) as dst: dst.colorinterp = rast.colorinterp dst.write(out_image) return output_raster
def test_fix_avocados(): labels = np.zeros((256, 256, 256), dtype=np.uint32) # fake clipped avocado labels[:50, :40, :30] = 1 labels[:25, :20, :25] = 2 # double avocado labels[50:100, 40:100, 30:80] = 3 labels[60:90, 50:90, 40:70] = 4 labels[60:70, 51:89, 41:69] = 5 fn = lambda lbls: edt.edt(lbls) dt = fn(labels) labels, dbf, remapping = kimimaro.intake.engage_avocado_protection( labels, dt, { 1: 1, 2: 2, 3: 3, 4: 4, 5: 5 }, soma_detection_threshold=1, edtfn=fn, progress=True) uniq = set(np.unique(labels)) assert uniq == set([0, 1, 2]) # 0,2,5 renumbered assert np.all(labels[:50, :40, :30] == 1) assert np.all(labels[50:100, 40:100, 30:80] == 2)
def test_small_anisotropy(): d = np.array([ [True, True], [True, False], ]) res = edt.edt(d, anisotropy=[0.5, 0.5], black_border=False) assert np.all(np.isclose(res, [[np.sqrt(2) / 2, 0.5], [0.5, 0.0]]))
def edtfn(labels): return edt.edt( labels, anisotropy=anisotropy, black_border=(minlabel == maxlabel), order='F', parallel=parallel, )
def voronoi_edges(shape: List[int], radius: int, ncells: int, flat_faces: bool = True): r""" Create an image of the edges in a Voronoi tessellation Parameters ---------- shape : array_like The size of the image to generate in [Nx, Ny, Nz] where Ni is the number of voxels in each direction. radius : scalar The radius to which Voronoi edges should be dilated in the final image. ncells : scalar The number of Voronoi cells to include in the tesselation. flat_faces : Boolean Whether the Voronoi edges should lie on the boundary of the image (True), or if edges outside the image should be removed (False). Returns ------- image : ND-array A boolean array with ``True`` values denoting the pore space """ print(78*'―') print('voronoi_edges: Generating', ncells, 'cells') shape = np.array(shape) if np.size(shape) == 1: shape = np.full((3, ), int(shape)) im = np.zeros(shape, dtype=bool) base_pts = np.random.rand(ncells, 3)*shape if flat_faces: # Reflect base points Nx, Ny, Nz = shape orig_pts = base_pts base_pts = np.vstack((base_pts, [-1, 1, 1] * orig_pts + [2.0*Nx, 0, 0])) base_pts = np.vstack((base_pts, [1, -1, 1] * orig_pts + [0, 2.0*Ny, 0])) base_pts = np.vstack((base_pts, [1, 1, -1] * orig_pts + [0, 0, 2.0*Nz])) base_pts = np.vstack((base_pts, [-1, 1, 1] * orig_pts)) base_pts = np.vstack((base_pts, [1, -1, 1] * orig_pts)) base_pts = np.vstack((base_pts, [1, 1, -1] * orig_pts)) vor = sptl.Voronoi(points=base_pts) vor.vertices = np.around(vor.vertices) vor.vertices *= (np.array(im.shape)-1) / np.array(im.shape) vor.edges = _get_Voronoi_edges(vor) for row in vor.edges: pts = vor.vertices[row].astype(int) if np.all(pts >= 0) and np.all(pts < im.shape): line_pts = line_segment(pts[0], pts[1]) im[tuple(line_pts)] = True im = edt(~im) > radius return im
def find_outer_region(im, r=0): r""" Finds regions of the image that are outside of the solid matrix. This function uses the rolling ball method to define where the outer region ends and the void space begins. This function is particularly useful for samples that do not fill the entire rectangular image, such as cylindrical cores or samples with non- parallel faces. Parameters ---------- im : ND-array Image of the porous material with 1's for void and 0's for solid r : scalar The radius of the rolling ball to use. If not specified then a value is calculated as twice maximum of the distance transform. The image size is padded by this amount in all directions, so the image can become quite large and unwieldy if too large a value is given. Returns ------- image : ND-array A boolean mask the same shape as ``im``, containing True in all voxels identified as *outside* the sample. """ if r == 0: dt = edt(input=im) r = int(np.amax(dt)) * 2 im_padded = np.pad(array=im, pad_width=r, mode='constant', constant_values=True) dt = edt(input=im_padded) seeds = (dt >= r) + get_border(shape=im_padded.shape) # Remove seeds not connected to edges labels = spim.label(seeds)[0] mask = labels == 1 # Assume label of 1 on edges, assured by adding border dt = edt(~mask) outer_region = dt < r outer_region = extract_subsection(im=outer_region, shape=im.shape) return outer_region
def insert_cylinder(im, xyz0, xyz1, r): r""" Inserts a cylinder of given radius onto a given image Parameters ---------- im : array_like Original voxelated image xyz0, xyz1 : 3-by-1 array_like Voxel coordinates of the two end points of the cylinder r : int Radius of the cylinder Returns ------- im : ND-array Original voxelated image overlayed with the cylinder Notes ----- This function is only implemented for 3D images """ if im.ndim != 3: raise Exception('This function is only implemented for 3D images') # Converting coordinates to numpy array xyz0, xyz1 = [np.array(xyz).astype(int) for xyz in (xyz0, xyz1)] r = int(r) L = np.absolute(xyz0 - xyz1).max() + 1 xyz_line = [np.linspace(xyz0[i], xyz1[i], L).astype(int) for i in range(3)] xyz_min = np.amin(xyz_line, axis=1) - r xyz_max = np.amax(xyz_line, axis=1) + r shape_template = xyz_max - xyz_min + 1 template = np.zeros(shape=shape_template) # Shortcut for orthogonal cylinders if (xyz0 == xyz1).sum() == 2: unique_dim = [xyz0[i] != xyz1[i] for i in range(3)].index(True) shape_template[unique_dim] = 1 template_2D = disk(radius=r).reshape(shape_template) template = np.repeat(template_2D, repeats=L, axis=unique_dim) xyz_min[unique_dim] += r xyz_max[unique_dim] += -r else: xyz_line_in_template_coords = [ xyz_line[i] - xyz_min[i] for i in range(3) ] template[tuple(xyz_line_in_template_coords)] = 1 template = edt(template == 0) <= r im[xyz_min[0]:xyz_max[0] + 1, xyz_min[1]:xyz_max[1] + 1, xyz_min[2]:xyz_max[2] + 1] += template return im
def insert_sphere(im, c, r, v=True, overwrite=True): r""" Inserts a sphere of a specified radius into a given image Parameters ---------- im : array_like Image into which the sphere should be inserted c : array_like The [x, y, z] coordinate indicating the center of the sphere r : int The radius of sphere to insert v : int The value to put into the sphere voxels. The default is ``True`` which corresponds to inserting spheres into a Boolean image. If a numerical value is given, ``im`` is converted to the same type as ``v``. overwrite : boolean If ``True`` (default) then the sphere overwrites whatever values are present in ``im``. If ``False`` then the sphere values are only inserted into locations that are 0 or ``False``. Returns ------- image : ND-array The original image with a sphere inerted at the specified location """ # Convert image to same type os v for eventual insertion if im.dtype != type(v): im = im.astype(type(v)) # Parse the arugments r = int(sp.around(r, decimals=0)) if r == 0: return im c = np.array(c, dtype=int) if c.size != im.ndim: raise Exception('Coordinates do not match dimensionality of image') # Define a bounding box around inserted sphere, minding imaage boundaries bbox = [] [bbox.append(np.clip(c[i] - r, 0, im.shape[i])) for i in range(im.ndim)] [bbox.append(np.clip(c[i] + r, 0, im.shape[i])) for i in range(im.ndim)] bbox = np.ravel(bbox) # Obtain slices into image s = bbox_to_slices(bbox) # Generate sphere template within image boundaries blank = np.ones_like(im[s], dtype=int) blank[tuple(c - bbox[0:im.ndim])] = 0 sph = edt(blank) < r if overwrite: # Clear voxles under sphere to be zero temp = im[s] * sph > 0 im[s][temp] = 0 else: # Clear portions of sphere to prevent overwriting sph *= im[s] == 0 im[s] = im[s] + sph * v return im
def test_2d_lopsided(): def gen(x, y, order): x = np.zeros((x, y), dtype=np.uint32, order=order) x[0:25, 5:50] = 3 x[25:50, 5:50] = 1 x[60:110, 5:50] = 2 return x sizes = [ (150, 150), (150, 75), (75, 150), ] for size in sizes: cres = edt.edt(gen(size[0], size[1], 'C'), order='C') fres = edt.edt(gen(size[0], size[1], 'F'), order='F') print(size) assert np.all(cres[:] == fres[:])
def gen_mask_tract(index_list, subject_name): result = np.zeros((145, 174, 145)) tmp = np.load(subject_name) for index in index_list: result = result + tmp[:, :, :, index] result = 1 * (result == 0) dist_mat = edt.edt(result, anisotropy=(1, 1.2, 1), black_border=False, order='K', parallel=1) return dist_mat
def test_1d_scipy_comparison(): for _ in range(20): randos = np.random.randint(0, 2, size=(100), dtype=np.uint32) labels = np.zeros((randos.shape[0] + 2, ), dtype=np.uint32) # Scipy requires zero borders labels[1:-1] = randos print("INPUT") print(labels) print("MLAEDT") mlaedt_result_bb = edt.edt(labels, black_border=True) mlaedt_result = edt.edt(labels, black_border=True) print(mlaedt_result) print("SCIPY") scipy_result = ndimage.distance_transform_edt(labels) print(scipy_result) assert np.all(np.abs(scipy_result - mlaedt_result) < 0.000001) assert np.all(np.abs(scipy_result - mlaedt_result_bb) < 0.000001)
def __getitem__(self, index): vol_size = self.sample_input_size if self.mode == 'train': pos, skel_id1, skel_id2, match, sample = self.get_pos_seed() elif self.mode == 'test': pos, skel_id1, skel_id2, match, sample = self.get_pos_test(index) out_image = crop_volume(self.image[pos[0]], vol_size, pos[1:]).astype(np.float32, copy=True) out_skeleton = crop_volume(self.skeleton[pos[0]], vol_size, pos[1:]).astype(np.float32, copy=True) out_flux = crop_volume_mul(self.flux[pos[0]], vol_size, pos[1:]).astype(np.float32, copy=True) out_skeleton_probability = crop_volume(self.skeleton_probability[pos[0]], vol_size, pos[1:]).astype(np.float32, copy=True) if self.mode == 'train' and self.augmentor is not None: # augmentation data = {'image': out_image, 'flux': out_flux, 'skeleton': out_skeleton, 'skeleton_probability': out_skeleton_probability} augmented = self.augmentor(data, random_state=None) out_image, out_flux = augmented['image'], augmented['flux'] out_skeleton = augmented['skeleton'] out_skeleton_probability = augmented['skeleton_probability'] skeleton_distance_tx = edt.edt(out_skeleton == 0, anisotropy=self.dataset_resolution[::-1], black_border=False, order='C', parallel=1) weight_distance_th = 1.75 * self.dataset_resolution[0] out_weight = (skeleton_distance_tx <= weight_distance_th).astype(np.float32, copy=False) out_weight += 0.1 out_weight /= 1.1 out_weight = torch.from_numpy(out_weight).unsqueeze(0) # keep the two selected skeletons into 2 separate volumes, erase rest out_skeleton_1 = np.zeros_like(out_image) out_skeleton_1[out_skeleton == skel_id1] = 1 out_skeleton_1 = torch.from_numpy(out_skeleton_1.astype(np.float32, copy=False)) out_skeleton_1 = out_skeleton_1.unsqueeze(0) out_skeleton_2 = np.zeros_like(out_image) out_skeleton_2[out_skeleton == skel_id2] = 1 out_skeleton_2 = torch.from_numpy(out_skeleton_2.astype(np.float32, copy=False)) out_skeleton_2 = out_skeleton_2.unsqueeze(0) out_image = torch.from_numpy(out_image.astype(np.float32, copy=True)).unsqueeze(0) out_flux = torch.from_numpy(out_flux.astype(np.float32, copy=True)) out_skeleton_probability = torch.from_numpy(out_skeleton_probability.astype(np.float32, copy=True)).unsqueeze(0) match = np.array([match]).astype(np.float32) match = torch.from_numpy(match) if self.mode == 'train': return sample, out_image, out_skeleton_1, out_skeleton_2, out_flux, out_skeleton_probability, out_weight, match else: return sample, out_image, out_skeleton_1, out_skeleton_2, out_flux, out_skeleton_probability, match
def test_find_border_targets(): labels = np.zeros((257, 257), dtype=np.uint8) labels[1:-1, 1:-1] = 1 dt = edt.edt(labels) targets = kimimaro.skeletontricks.find_border_targets(dt, labels.astype( np.uint32), wx=100, wy=100) assert len(targets) == 1 assert targets[1] == (128, 128)
def get_phi(binary, lmbda, N, scale, offset, dim=2): """ Generate a phase field from a binary representation of a complex geometry. The phase field diffuses from 1 inside of the complex geometry to 0 outside of the complex geometry. Args: binary (numpy array): Array containing binary representation of complex geometry. lmbda (float): Measure of the diffuseness of the phase field boundary. N (list): Number of mesh elements in each direction (N+1 nodes). scale (list): Extent of the meshed domain in each direction ([-2,2] square -> scale=[4,4]). offset (list): Centers the meshed domain in each direction ([-2,2] square -> offset=[2,2]). dim (int): Dimension of the domain (must be 2 or 3). Returns: phi (numpy array): Array containing phase field. """ if dim == 2: kernel = np.ones((3, 3)) elif dim == 3: kernel = np.ones((3, 3, 3)) else: raise ValueError('Only works with 2D or 3D meshes.') # Use the difference between binary and an eroded binary to get the border # of binary, then get the distance transform relative to that border. The # distance transform is a Euclidean distance transform that takes into # account the entire array, not just a local neighbourhood. erosion = spimg.morphology.binary_erosion(binary, kernel, 1).astype(np.float64) border = 1.0 - (binary - erosion) dt = edt.edt(border) dt *= min(scale) / min(N) # The phase field should run from -1 to 1, take the value 0 at binary's # border, and follow the error function's distribution away from binary's # border (either towards -1 or 1). phi_in = spec.erf(dt / lmbda) * binary phi_out = spec.erf(dt / lmbda) * (binary - 1.0) phi = phi_in + phi_out # Modify the phase field to run from 0 to 1. phi = (phi + 1.0) / 2.0 return phi
def test_anisotropy_range(weight): img = np.ones((100, 97, 99), dtype=np.uint8) img[0, 0, 0] = 0 res = edt.edt(img, anisotropy=(weight, weight, weight), black_border=False) sx = weight * (img.shape[0] - 1) sy = weight * (img.shape[1] - 1) sz = weight * (img.shape[2] - 1) max_val = res[99, 96, 98] expected = math.sqrt(sx * sx + sy * sy + sz * sz) assert math.isclose(expected, max_val, rel_tol=0.000001)
def test_2d_scipy_comparison(): for _ in range(20): for dtype in (np.uint32, np.bool): randos = np.random.randint(0, 2, size=(5, 5), dtype=dtype) labels = np.zeros( (randos.shape[0] + 2, randos.shape[1] + 2), dtype=dtype) print("INPUT") print(labels) print("MLAEDT") mlaedt_result = edt.edt(labels, black_border=False) print(mlaedt_result) print("SCIPY") scipy_result = ndimage.distance_transform_edt(labels) print(scipy_result) assert np.all( np.abs(scipy_result - mlaedt_result) < 0.000001 )
def compare_scipy_edt(labels): print("INPUT", labels.shape) print(labels) print("MLAEDT") mlaedt_result = edt.edt(labels, black_border=False) print(mlaedt_result) print("SCIPY") scipy_result = ndimage.distance_transform_edt(labels) print(scipy_result) print("DIFF") print(np.abs(scipy_result - mlaedt_result) < 0.000001) print("MAX Diff") print(np.max(np.abs(scipy_result - mlaedt_result))) assert np.all(np.abs(scipy_result - mlaedt_result) < 0.000001)
def ps_ball(radius): r""" Creates spherical ball structuring element for morphological operations Parameters ---------- radius : float or int The desired radius of the structuring element Returns ------- strel : 3D-array A 3D numpy array of the structuring element """ rad = int(np.ceil(radius)) other = np.ones((2 * rad + 1, 2 * rad + 1, 2 * rad + 1), dtype=bool) other[rad, rad, rad] = False ball = edt(other) < radius return ball
def test_3d_high_anisotropy(): shape = (256, 256, 256) anisotropy = (1000000, 1200000, 40) labels = np.ones(shape, dtype=np.uint8) labels[0, 0, 0] = 0 labels[-1, -1, -1] = 0 resedt = edt.edt(labels, anisotropy=anisotropy, black_border=False) mx = np.max(resedt) assert np.isfinite(mx) assert mx <= (1e6 * 256)**2 + (1e6 * 256)**2 + (666 * 256)**2 resscipy = ndimage.distance_transform_edt(labels, sampling=anisotropy) resscipy[resscipy == 0] = 1 resedt[resedt == 0] = 1 ratio = np.abs(resscipy / resedt) assert np.all(ratio < 1.000001) and np.all(ratio > 0.999999)
def ps_disk(radius): r""" Creates circular disk structuring element for morphological operations Parameters ---------- radius : float or int The desired radius of the structuring element Returns ------- strel : 2D-array A 2D numpy bool array of the structring element """ rad = int(np.ceil(radius)) other = np.ones((2 * rad + 1, 2 * rad + 1), dtype=bool) other[rad, rad] = False disk = edt(other) < radius return disk
def test_2d_scipy_comparison_black_border(): for dtype in INTEGER_TYPES: print(dtype) randos = np.random.randint(0, 2, size=(3, 3), dtype=dtype) labels = np.zeros( (randos.shape[0] + 2, randos.shape[1] + 2), dtype=dtype) # Scipy requires zero borders labels[1:-1,1:-1] = randos print("INPUT") print(labels) print("MLAEDT") mlaedt_result = edt.edt(labels, black_border=False) # mlaedt_result_bb = edt.edt(labels, black_border=True) print(mlaedt_result) print("SCIPY") scipy_result = ndimage.distance_transform_edt(labels) print(scipy_result) assert np.all( np.abs(scipy_result - mlaedt_result) < 0.000001 )
def compute_border_targets(cc_labels, anisotropy): sx, sy, sz = cc_labels.shape planes = ( (cc_labels[:, :, 0], (0, 1), lambda x, y: (x, y, 0)), # top xy (cc_labels[:, :, -1], (0, 1), lambda x, y: (x, y, sz - 1)), # bottom xy (cc_labels[:, 0, :], (0, 2), lambda x, z: (x, 0, z)), # left xz (cc_labels[:, -1, :], (0, 2), lambda x, z: (x, sy - 1, z)), # right xz (cc_labels[0, :, :], (1, 2), lambda y, z: (0, y, z)), # front yz (cc_labels[-1, :, :], (1, 2), lambda y, z: (sx - 1, y, z)) # back yz ) target_list = defaultdict(set) for plane, dims, rotatefn in planes: wx, wy = anisotropy[dims[0]], anisotropy[dims[1]] plane = np.copy(plane, order='F') cc_plane = cc3d.connected_components(np.ascontiguousarray(plane)) dt_plane = edt.edt(cc_plane, black_border=True, anisotropy=(wx, wy)) plane_targets = kimimaro.skeletontricks.find_border_targets( dt_plane, cc_plane, wx, wy) plane = plane[..., np.newaxis] cc_plane = cc_plane[..., np.newaxis] remapping = kimimaro.skeletontricks.get_mapping(plane, cc_plane) for label, pt in plane_targets.items(): label = remapping[label] target_list[label].add(rotatefn(int(pt[0]), int(pt[1]))) target_list.default_factory = lambda: np.array([], np.uint32) for label, pts in target_list.items(): target_list[label] = np.array(list(pts), dtype=np.uint32) return target_list