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)
Beispiel #8
0
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
Beispiel #9
0
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]]))
Beispiel #12
0
 def edtfn(labels):
     return edt.edt(
         labels,
         anisotropy=anisotropy,
         black_border=(minlabel == maxlabel),
         order='F',
         parallel=parallel,
     )
Beispiel #13
0
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
Beispiel #14
0
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
Beispiel #15
0
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
Beispiel #16
0
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[:])
Beispiel #18
0
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)
Beispiel #20
0
    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)
Beispiel #22
0
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)
Beispiel #26
0
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)
Beispiel #28
0
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 )
Beispiel #30
0
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