Esempio n. 1
0
def test_marching_cubes_anisotropic():
    # test spacing as numpy array (and not just tuple)
    spacing = np.array([1., 10 / 6., 16 / 6.])
    ellipsoid_anisotropic = ellipsoid(6,
                                      10,
                                      16,
                                      spacing=spacing,
                                      levelset=True)
    _, surf = ellipsoid_stats(6, 10, 16)

    # Classic
    verts, faces = marching_cubes_classic(ellipsoid_anisotropic,
                                          0.,
                                          spacing=spacing)
    surf_calc = mesh_surface_area(verts, faces)
    # Test within 1.5% tolerance for anisotropic. Will always underestimate.
    assert surf > surf_calc and surf_calc > surf * 0.985

    # Lewiner
    verts, faces = marching_cubes_lewiner(ellipsoid_anisotropic,
                                          0.,
                                          spacing=spacing)[:2]
    surf_calc = mesh_surface_area(verts, faces)
    # Test within 1.5% tolerance for anisotropic. Will always underestimate.
    assert surf > surf_calc and surf_calc > surf * 0.985

    # Test spacing together with allow_degenerate=False
    marching_cubes_lewiner(ellipsoid_anisotropic,
                           0,
                           spacing=spacing,
                           allow_degenerate=False)
Esempio n. 2
0
def plot_3d_surface(data, labels, region=3, spacing=(1.0, 1.0, 1.0)):
    """Generates a 3D surface plot for the specified region.

    Parameters
    ----------
    data : array (M, N, P)
        3D interest image.
    labels : array (M, N, P)
        Labels corresponding to data, obtained by measure.label.
    region : int, optional
        The region of interest to be plotted.
    spacing : array (1, 3)
        Spacing information, set distances between pixels.

    Notes
    -----
    The volume is visualized using the mesh vertexes and faces.
    """
    properties = measure.regionprops(labels, intensity_image=data)
    # skimage.measure.marching_cubes expects ordering (row, col, plane).
    # We need to transpose the data:
    volume = (labels == properties[region].label).transpose(1, 2, 0)

    verts_px, faces_px, _, _ = measure.marching_cubes_lewiner(volume,
                                                              level=0,
                                                              spacing=(1.0,
                                                                       1.0,
                                                                       1.0))
    surface_area_pixels = measure.mesh_surface_area(verts_px, faces_px)

    verts_actual, faces_actual, _, _ = measure.marching_cubes_lewiner(
        volume, level=0, spacing=tuple(spacing))
    surface_area_actual = measure.mesh_surface_area(verts_actual, faces_actual)

    print('Surface area\n')
    print(' * Total pixels: {:0.2f}'.format(surface_area_pixels))
    print(' * Actual: {:0.2f}'.format(surface_area_actual))

    fig = plt.figure(figsize=(10, 10))
    ax = fig.add_subplot(111, projection='3d')

    mesh = Poly3DCollection(verts_px[faces_px])
    mesh.set_edgecolor('black')
    ax.add_collection3d(mesh)

    ax.set_xlabel('col')
    ax.set_ylabel('row')
    ax.set_zlabel('plane')

    min_pln, min_row, min_col, max_pln, max_row, max_col = properties[
        region].bbox

    ax.set_xlim(min_row, max_row)
    ax.set_ylim(min_col, max_col)
    ax.set_zlim(min_pln, max_pln)

    plt.tight_layout()
    plt.show()

    return None
def interface_per_time_step(wet,
                            void,
                            smooth_decision=smooth_decision
                            ):  #, watermask, t):
    A_ws = 0
    A_wa = 0
    A_tot = 0
    A_wv = 0
    A_ws_label = 0
    A_wa_corr = 0
    A_wa_corr2 = 0
    if np.any(wet):
        try:
            verts, faces, _, _ = measure.marching_cubes_lewiner(wet)
            if smooth_decision == 'yes':
                verts, faces = surface_smoothing(verts, faces)
            A_tot = measure.mesh_surface_area(verts, faces)
            a = True
        except:
            A_tot = np.nan
            a = False
            # print(str(t)+' A_tot_failed')
        A_wv = np.count_nonzero(wet[:, :, 0]) + np.count_nonzero(wet[:, :, -1])

        if a:
            # wet surface
            try:
                wet_interface = solid_interface_extraction(wet, ~void)
                sverts, sfaces, _, _ = measure.marching_cubes_lewiner(
                    wet_interface)
                if smooth_decision == 'yes':
                    sverts, sfaces = surface_smoothing(sverts, sfaces)
                A_ws = measure.mesh_surface_area(sverts, sfaces) / 2

                fverts = verts
                ffaces = faces
                fvert_int = np.int16(fverts)
                wet_mask = wet_interface[fvert_int[:, 0], fvert_int[:, 1],
                                         fvert_int[:, 2]]
                ffaces_mask = np.all(wet_mask[ffaces], axis=1)
                wffaces = ffaces[ffaces_mask]
                A_ws_label = measure.mesh_surface_area(fverts, wffaces)
            except:
                A_ws = 0
                A_ws_label = 0

            A_wa_corr = A_tot - A_ws - A_wv
            A_wa_corr2 = A_tot - A_ws_label - A_wv
            # print(str(t)+' A_ws_failed')

    return A_ws, A_wa, A_tot, A_wv, A_ws_label, A_wa_corr, A_wa_corr2
def test_marching_cubes_isotropic():
    ellipsoid_isotropic = ellipsoid(6, 10, 16, levelset=True)
    _, surf = ellipsoid_stats(6, 10, 16)
    
    # Classic
    verts, faces = marching_cubes_classic(ellipsoid_isotropic, 0.)
    surf_calc = mesh_surface_area(verts, faces)
    # Test within 1% tolerance for isotropic. Will always underestimate.
    assert surf > surf_calc and surf_calc > surf * 0.99
    
    # Lewiner
    verts, faces = marching_cubes_lewiner(ellipsoid_isotropic, 0.)[:2]
    surf_calc = mesh_surface_area(verts, faces)
    # Test within 1% tolerance for isotropic. Will always underestimate.
    assert surf > surf_calc and surf_calc > surf * 0.99
Esempio n. 5
0
def get_contact_area(volume):
    '''
    Get the contact volume surface of the segmentation. The segmentation results should be watershed segmentation results
    with a ***watershed line***.
    :param volume: segmentation result
    :return boundary_elements_uni: pairs of SegCell which contacts with each other
    :return contact_area: the contact surface area corresponding to that in the the boundary_elements.
    '''

    cell_mask = volume != 0
    boundary_mask = (cell_mask == 0) & ndimage.binary_dilation(cell_mask)
    [x_bound, y_bound, z_bound] = np.nonzero(boundary_mask)
    boundary_elements = []
    for (x, y, z) in zip(x_bound, y_bound, z_bound):
        neighbors = volume[np.ix_(range(x - 1, x + 2), range(y - 1, y + 2),
                                  range(z - 1, z + 2))]
        neighbor_labels = list(np.unique(neighbors))
        neighbor_labels.remove(0)
        if len(neighbor_labels) == 2:
            boundary_elements.append(neighbor_labels)
    boundary_elements_uni = list(np.unique(np.array(boundary_elements),
                                           axis=0))
    contact_area = []
    boundary_elements_uni_new = []
    for (label1, label2) in boundary_elements_uni:
        contact_mask = np.logical_and(
            ndimage.binary_dilation(volume == label1),
            ndimage.binary_dilation(volume == label2))
        contact_mask = np.logical_and(contact_mask, boundary_mask)
        if contact_mask.sum() > 4:
            verts, faces, _, _ = marching_cubes_lewiner(contact_mask)
            area = mesh_surface_area(verts, faces) / 2
            contact_area.append(area)
            boundary_elements_uni_new.append((label1, label2))
    return boundary_elements_uni_new, contact_area
def get_basic_properties(nuclei_mask, intensity_image):
    # 3D features
    volume = np.sum(nuclei_mask)
    iso_verts, iso_faces, _, _ = measure.marching_cubes(nuclei_mask)
    surface_area = measure.mesh_surface_area(iso_verts, iso_faces)
    sa_vo = surface_area / volume

    # 2D features
    props = measure.regionprops(
        nuclei_mask.astype(np.uint8).max(axis=0),
        intensity_image.max(axis=0))[0]
    area = props.area
    eccentricity = props.eccentricity
    aspect_ratio = max(props.intensity_image.shape) / min(
        props.intensity_image.shape)
    convexity = area / props.convex_area
    major_axis_length = props.major_axis_length
    minor_axis_length = props.minor_axis_length

    basic_properties = {
        'volume': volume,
        'surface_area': surface_area,
        'sa/vol': sa_vo,
        'area': area,
        'eccentricity': eccentricity,
        'aspect_ratio': aspect_ratio,
        'convexity': convexity,
        'minor_axis_length': minor_axis_length,
        'major_axis_length': major_axis_length
    }

    return basic_properties
Esempio n. 7
0
def calculate_sphericity_array(dataarray):
        # requires numpy array as input
        
        dataarray_copy = copy.deepcopy(dataarray)
        extended_inputmrc = np.zeros((dataarray.shape[0]+10,dataarray.shape[1]+10,dataarray.shape[2]+10),dtype=np.float) ## Had to extend it before Gaussian filter, else you might get edge effects
        extended_inputmrc[6:6+dataarray.shape[0], 6:6+dataarray.shape[1], 6:6+dataarray.shape[2]] = dataarray_copy

        # Gaussian filtering
        # Sigma=1 works well
        blurred = gaussian_filter(extended_inputmrc,sigma=1)

        # Find surfaces using marching cube algorithm
        verts, faces, normals, values = measure.marching_cubes_lewiner(blurred,level=0.5) ## Fixed thresholded due to Gaussian blurring
        
        # Find surface area
        surface_area = measure.mesh_surface_area(verts,faces)

        # Find volume
        blurred[blurred >= 0.5] = 1
        blurred[blurred < 0.5] = 0
        volume = np.sum(blurred)
        
        # Calculate sphericity
        sphericity = (((pi)**(1/3))*((6*volume)**(2/3)))/(surface_area)
        return sphericity
Esempio n. 8
0
def calc_surface(mask, spacing=None):
    """Calculate the surface of a binary mask.
    The provided mask is expected to have only one object.

    Args:
      mask (np.array): thresholded image.
      spacing (tuple[float]): pixel/voxel side sizes.

    Returns:
      float: mesh surface area of the provided object.
    """

    # Aspect ratio for 3d surface calculation
    if None == spacing:
        spacing = [1.0 for d in mask.shape]

    # Force binary type
    mask = mask.astype('bool')

    # Check number of objects
    if 1 != label(mask).max():
        return (0)

    # Add top/bottom slices
    shape = list(mask.shape)
    shape[0] = 1
    mask = np.vstack((np.zeros(shape), mask, np.zeros(shape)))

    # Calculate sphericity
    verts, faces, ns, vs = marching_cubes_lewiner(mask, 0.0, spacing)
    return (mesh_surface_area(verts, faces))
    def test_sphere_surface_area(self):
        if USE_SCIKIT:
            from skimage.measure import marching_cubes_classic, mesh_surface_area

            # (B, W, H, D)
            volume = torch.Tensor([[[(x - 10)**2 + (y - 10)**2 + (z - 10)**2
                                     for z in range(20)] for y in range(20)]
                                   for x in range(20)]).unsqueeze(0)
            volume = volume.permute(0, 3, 2, 1)  # (B, D, H, W)
            verts, faces = marching_cubes_naive(volume, isolevel=64)
            verts_sci, faces_sci = marching_cubes_classic(volume[0], level=64)

            surf = mesh_surface_area(verts[0], faces[0])
            surf_sci = mesh_surface_area(verts_sci, faces_sci)

            self.assertClose(surf, surf_sci)
Esempio n. 10
0
def surface_area_3d(img_np, level=0.0, spacing=None):
    """Measure the surface area for a 3D volume.
    
    Wrapper for :func:`measure.marching_cubes_lewiner` and 
    :func:`measure.mesh_surface_area`.
    
    Args:
        img_np (:obj:`np.ndarray`): 3D image array, which can be a mask.
        level (float): Contour value for :func:`measure.marching_cubes_lewiner`;
            defaults to 0.0.
        spacing (List[float]): Sequence of voxel spacing in same order 
            as for ``img_np``; defaults to None, which will use a value of 
            ``np.ones(3)``.

    Returns:
        Surface area in the coordinate units squared.

    """
    if spacing is None:
        spacing = np.ones(3)
    try:
        verts, faces, normals, vals = measure.marching_cubes_lewiner(
            img_np, level=level, spacing=spacing)
        return measure.mesh_surface_area(verts, faces)
    except RuntimeError as e:
        print(e)
    return np.nan
Esempio n. 11
0
    def marching_cubes(self, spc=0.02):
        """Set initial vertices on metaballs using marching cubes

        Set initial vertices on metaballs using marching cubes

        Args:
            spc:

        """

        mb_meshgrid, xyz_spc = self.get_mb_meshgrid(spc)

        verts, faces, normals, values = measure.marching_cubes(
            mb_meshgrid,
            level=0.0,
            spacing=xyz_spc,
            gradient_direction='ascent',
            step_size=1)

        verts += np.c_[self.xmin, self.ymin, self.zmin]

        self.verts = verts
        self.faces = faces
        self.normals = normals
        self.values = values
        self.sa = measure.mesh_surface_area(verts, faces)
Esempio n. 12
0
def mesh_surface_area(mesh=None, verts=None, faces=None):
    r"""
    Calculates the surface area of a meshed region

    Parameters
    ----------
    mesh : tuple
        The tuple returned from the ``mesh_region`` function
    verts : array
        An N-by-ND array containing the coordinates of each mesh vertex
    faces : array
        An N-by-ND array indicating which elements in ``verts`` form a mesh
        element.

    Returns
    -------
    surface_area : float
        The surface area of the mesh, calculated by
        ``skimage.measure.mesh_surface_area``

    Notes
    -----
    This function simply calls ``scikit-image.measure.mesh_surface_area``, but
    it allows for the passing of the ``mesh`` tuple returned by the
    ``mesh_region`` function, entirely for convenience.
    """
    if mesh:
        verts = mesh.verts
        faces = mesh.faces
    else:
        if (verts is None) or (faces is None):
            raise Exception('Either mesh or verts and faces must be given')
    surface_area = measure.mesh_surface_area(verts, faces)
    return surface_area
    def test_double_ellipsoid_surface_area(self):
        if USE_SCIKIT:
            import numpy as np
            from skimage.draw import ellipsoid
            from skimage.measure import marching_cubes_classic, mesh_surface_area

            ellip_base = ellipsoid(6, 10, 16, levelset=True)
            ellip_double = np.concatenate(
                (ellip_base[:-1, ...], ellip_base[2:, ...]), axis=0)
            volume = torch.Tensor(ellip_double).unsqueeze(0)
            volume = volume.permute(0, 3, 2, 1)  # (B, D, H, W)
            verts, faces = marching_cubes_naive(volume, isolevel=0)
            verts_sci, faces_sci = marching_cubes_classic(volume[0], level=0)

            surf = mesh_surface_area(verts[0], faces[0])
            surf_sci = mesh_surface_area(verts_sci, faces_sci)

            self.assertClose(surf, surf_sci)
Esempio n. 14
0
def test_marching_cubes_anisotropic():
    spacing = (1., 10 / 6., 16 / 6.)
    ellipsoid_anisotropic = ellipsoid(6, 10, 16, spacing=spacing,
                                      levelset=True)
    _, surf = ellipsoid_stats(6, 10, 16)
    
    # Classic
    verts, faces = marching_cubes_classic(ellipsoid_anisotropic, 0.,
                                          spacing=spacing)
    surf_calc = mesh_surface_area(verts, faces)
    # Test within 1.5% tolerance for anisotropic. Will always underestimate.
    assert surf > surf_calc and surf_calc > surf * 0.985
    
    # Lewiner
    verts, faces = marching_cubes_lewiner(ellipsoid_anisotropic, 0., spacing=spacing)[:2]
    surf_calc = mesh_surface_area(verts, faces)
    # Test within 1.5% tolerance for anisotropic. Will always underestimate.
    assert surf > surf_calc and surf_calc > surf * 0.985
Esempio n. 15
0
    def _compute_shape_size_features(self):
        """ compute volume under a given mask"""

        # mask volume
        vox_size_SRC = 0.1 * np.array(self._IG.samplingSRC)  # unit: cm
        vox_vol = np.prod(vox_size_SRC)  # unit: cm^
        vol_cm3 = np.sum(self._maskImage_ndarray) * vox_vol  # unit: ml or cm^3
        self._df_feature_output['ShapeSize_vol_cm3'] = vol_cm3

        # mask surface area
        the_tumor_vol = np.zeros(self._inputImage_ndarray.shape)
        the_tumor_vol[self._maskImage_ndarray] = self._inputImage_ndarray[
            self._maskImage_ndarray]
        verts, faces = skm.marching_cubes(the_tumor_vol, 0.0,
                                          tuple(vox_size_SRC))
        surf_area_cm2 = skm.mesh_surface_area(verts, faces)  # unit: cm^2

        self._df_feature_output['ShapeSize_surf_area_cm2'] = surf_area_cm2
        self._df_feature_output['ShapeSize_compactness1'] = vol_cm3 / (
            np.sqrt(np.pi) * surf_area_cm2**(2. / 3.))
        self._df_feature_output['ShapeSize_compactness2'] = (
            36. * np.pi * vol_cm3**2) / (surf_area_cm2**3)
        self._df_feature_output['ShapeSize_sphericity'] = (np.pi**(
            1. / 3.)) * (6 * vol_cm3)**(2. / 3.) / surf_area_cm2
        self._df_feature_output[
            'ShapeSize_surface2volratio'] = surf_area_cm2 / vol_cm3

        # maximum 3D euclidean distance (or diameter?)
        kk, ii, jj = np.where(self._maskImage_ndarray == True)

        # skip computing max euclidean distance if the mask is too big such as including skin and etc.
        if len(kk) > 300000 and vol_cm3 > 70.:
            print '::ImageFeature:: compute_shape_size_feature, tumor mask is TOO big (vol: {} ml)! will not compute euc max distance :/'.format(
                vol_cm3)
        else:
            print '::ImageFeature:: max euc distance # of voxels to go through: {}, vol = {} cm3'.format(
                len(kk), vol_cm3)
            eucdis_tmp = np.zeros((len(kk), 1))
            for n in range(len(kk) - 1):
                px1 = np.column_stack((kk[n + 1:], ii[n + 1:], jj[n + 1:]))
                px2 = np.tile(np.array([kk[n], ii[n], jj[n]]),
                              (px1.shape[0], 1))
                vox_size_tile = np.tile(vox_size_SRC, (px1.shape[0], 1))
                eucdis = np.sqrt(
                    np.sum(((px1 - px2) * vox_size_tile)**2, axis=1))
                eucdis_tmp[n] = np.amax(eucdis)
            max_euc_dis = np.amax(eucdis_tmp)
            self._df_feature_output['ShapeSize_max_euc_dis'] = max_euc_dis

        # R is the radius of the sphere with the same volume as the tumor
        tumor_sphere_R = (3 * vol_cm3 / (4 * np.pi))**(1. / 3)
        self._df_feature_output[
            'ShapeSize_spherical_disproportion'] = surf_area_cm2 / (
                4 * np.pi * tumor_sphere_R**2)

        print '::ImageFeature:: complete compute_shape_size_features!'
Esempio n. 16
0
def mesh_area_calc(mesh):
    """

    Returns
    -------
    float
        Mesh area in um^2
    """
    return mesh_surface_area(mesh[1].reshape(-1, 3), mesh[0].reshape(-1,
                                                                     3)) / 1e6
Esempio n. 17
0
    def mesh_area(self):
        """

        Returns
        -------
        float
            Mesh area in um^2
        """
        return mesh_surface_area(self.mesh[1].reshape(-1, 3),
                                 self.mesh[0].reshape(-1, 3)) / 1e6
def test_masked_marching_cubes():

    ellipsoid_scalar = ellipsoid(6, 10, 16, levelset=True)
    mask = np.ones_like(ellipsoid_scalar, dtype=bool)
    mask[:10, :, :] = False
    mask[:, :, 20:] = False
    ver, faces, _, _ = marching_cubes(ellipsoid_scalar, 0, mask=mask)
    area = mesh_surface_area(ver, faces)

    assert_allclose(area, 299.56878662109375, rtol=.01)
Esempio n. 19
0
def find_isosurfaces(frame, params):
    radius, factor, scale, sel = params['radius'], params['factor'], params[
        'scale'], params['sel']
    level = params['level']
    params['box'] = frame.unitcell_lengths[0]
    params['grid'], params['spacing'], params['grid_shape'] = make_grid(
        params['box'], params['mesh'])
    grid, spacing, grid_shape, box = params['grid'], params['spacing'], params[
        'grid_shape'], params['box']

    pos = frame.atom_slice(atom_indices=frame.top.select(sel)).xyz[0]

    tree = cKDTree(grid, boxsize=box)

    # indeces of grid points within a radial distance from the particles
    indlist = tree.query_ball_point(pos, radius)

    # unwrapped list of lists
    indarray = np.asarray(list(itertools.chain.from_iterable(indlist)),
                          dtype=int)

    # lenghts of the sublists in indlist
    lenarray = np.asarray([len(ind) for ind in indlist], dtype=int)

    # vector distance between particles and grid points
    dr = grid[indarray, :] - np.repeat(pos, lenarray, axis=0)

    # periodic boundary conditions in xy-plane
    cond = np.where(np.abs(dr) > box / 2.)
    dr[cond] -= np.sign(dr[cond]) * box[cond[1]]
    # coarse grained density field
    dens = factor * np.exp(-np.linalg.norm(dr, ord=2, axis=1)**2 / scale)
    # densities at the same grid point are summed up
    field = pd.DataFrame(data={
        'index': indarray,
        'dens': dens
    }).groupby('index').sum()
    # grid points with zero density are included in the dataframe
    new_index = pd.Index(range(grid.shape[0]), name="index")
    field = field.reindex(new_index, fill_value=0).values.reshape(grid_shape)

    verts, faces, normals, values = measure.marching_cubes_lewiner(
        field, level, spacing=tuple(spacing))
    verts[:, :2] = verts[:, :2] + spacing[:2] / 2.
    cond_upper = verts[:, 2] > box[2] / 2
    upper = verts[cond_upper], normals[cond_upper]
    lower = verts[~cond_upper], normals[~cond_upper]
    params['surface_area'] = np.append(
        params['surface_area'],
        measure.mesh_surface_area(verts, faces) * 0.5)
    params['surface_zstd'] = np.append(params['surface_zstd'],
                                       upper[0][:, 2].std())
    params['surface_zstd'] = np.append(params['surface_zstd'],
                                       lower[0][:, 2].std())
    return upper, lower
def test_marching_cubes_anisotropic():
    sampling = (1., 10 / 6., 16 / 6.)
    ellipsoid_anisotropic = ellipsoid(6, 10, 16, sampling=sampling,
                                      levelset=True)
    _, surf = ellipsoid_stats(6, 10, 16, sampling=sampling)
    verts, faces = marching_cubes(ellipsoid_anisotropic, 0.,
                                  sampling=sampling)
    surf_calc = mesh_surface_area(verts, faces)

    # Test within 1.5% tolerance for anisotropic. Will always underestimate.
    assert surf > surf_calc and surf_calc > surf * 0.985
Esempio n. 21
0
def calculate_burning_area_3d(regression_map, regression_depth, mc_mask,
                              volume_ratio):
    regression_depth = regression_depth / volume_ratio
    verts, faces, norm, line = measure.marching_cubes(
        regression_map,
        level=regression_depth,
        mask=mc_mask,
        spacing=(volume_ratio, volume_ratio,
                 volume_ratio))  # (hide false values)
    burning_area = measure.mesh_surface_area(verts, faces)
    return burning_area
Esempio n. 22
0
    def _assign_layers(self):
        """ There are no layers in the Willard-Chandler method.

            This function identifies the dividing surface and stores the
            triangulated isosurface, the density and the particles.

        """
        self.label_group(
            self.universe.atoms, beta=0.0, layer=-1, cluster=-1, side=-1)
        # we assign an empty group for consistency
        self._layers = self.universe.atoms[:0]

        self.normal = None

        # this can be used later to shift back to the original shift
        self.original_positions = np.copy(self.universe.atoms.positions[:])
        self.universe.atoms.pack_into_box()

        self._define_cluster_group()

        self.centered_positions = None
        if self.do_center is True:
            self.center()

        pos = self.cluster_group.positions
        box = self.universe.dimensions[:3]

        ngrid, spacing = utilities.compute_compatible_mesh_params(
            self.mesh, box)
        self.spacing = spacing
        self.ngrid = ngrid
        grid = utilities.generate_grid_in_box(box, ngrid, order='zyx')
        kernel, _ = utilities.density_map(pos, grid, self.alpha, box)

        if self.fast is True:
            kernel.pos = pos.copy()
            self.density_field = kernel.evaluate_pbc_fast(grid)
        else:
            self.density_field = kernel.evaluate_pbc(grid)

        # Thomas Lewiner, Helio Lopes, Antonio Wilson Vieira and Geovan
        # Tavares. Efficient implementation of Marching Cubes’ cases with
        # topological guarantees. Journal of Graphics Tools 8(2) pp. 1-15
        # (december 2003). DOI: 10.1080/10867651.2003.10487582
        volume = self.density_field.reshape(
            tuple(np.array(ngrid[::-1]).astype(int)))
        verts, faces, normals, values = measure.marching_cubes(
            volume, None, spacing=tuple(spacing))
        # note that len(normals) == len(verts): they are normals
        # at the vertices, and not normals of the faces
        self.triangulated_surface = [verts, faces, normals]
        self.surface_area = measure.mesh_surface_area(verts, faces)
        verts += spacing[::-1] / 2.
Esempio n. 23
0
    def run(self, ips, imgs, para=None):
        k = ips.unit[0]

        titles = ['ID']
        if para['center']: titles.extend(['Center-X', 'Center-Y', 'Center-Z'])
        if para['surf']: titles.append('Surface')
        if para['vol']: titles.append('Volume')
        if para['extent']:
            titles.extend(
                ['Min-Z', 'Min-Y', 'Min-X', 'Max-Z', 'Max-Y', 'Max-X'])
        if para['ed']: titles.extend(['Diameter'])
        if para['fa']: titles.extend(['FilledArea'])
        if para['cov']: titles.extend(['Axis1', 'Axis2', 'Axis3'])

        strc = generate_binary_structure(
            3, 1 if para['con'] == '4-connect' else 2)
        buf, n = label(imgs, strc, output=np.uint32)
        ls = regionprops(buf)

        dt = [range(len(ls))]

        centroids = [i.centroid for i in ls]
        if para['center']:
            dt.append([round(i.centroid[1] * k, 1) for i in ls])
            dt.append([round(i.centroid[0] * k, 1) for i in ls])
            dt.append([round(i.centroid[2] * k, 1) for i in ls])
        if para['surf']:
            buf[find_boundaries(buf, mode='outer')] = 0
            vts, fs, ns, cs = marching_cubes_lewiner(buf, level=0)
            lst = [[] for i in range(n + 1)]
            for i in fs:
                lst[int(cs[i[0]])].append(i)
            dt.append([
                0 if len(i) == 0 else mesh_surface_area(vts, np.array(i)) *
                k**2 for i in lst
            ][1:])
        if para['vol']:
            dt.append([i.area * k**3 for i in ls])
        if para['extent']:
            for j in (0, 1, 2, 3, 4, 5):
                dt.append([i.bbox[j] * k for i in ls])
        if para['ed']:
            dt.append([round(i.equivalent_diameter * k, 1) for i in ls])
        if para['fa']:
            dt.append([i.filled_area * k**3 for i in ls])
        if para['cov']:
            ites = np.array([i.inertia_tensor_eigvals for i in ls])
            rst = np.sqrt(np.clip(ites.sum(axis=1) // 2 - ites.T, 0, 1e10)) * 4
            for i in rst[::-1]:
                dt.append(np.abs(i))
        IPy.show_table(pd.DataFrame(list(zip(*dt)), columns=titles),
                       ips.title + '-region')
    def compute_isoArea(self, c_iso):
        print('Computing the surface for c_iso: ', c_iso)
        # print('Currently in timing test mode!')

        half_filter = int(self.filter_width / 2)

        # reference area of planar flame
        A_planar = (self.filter_width - 1)**2

        iterpoints = (self.Nx)**3
        # progress bar
        bar = ChargingBar('Processing', max=iterpoints)

        isoArea_coefficient = np.zeros((self.Nx, self.Nx, self.Nx))

        for l in range(half_filter, self.Nx - half_filter, self.every_nth):
            for m in range(half_filter, self.Nx - half_filter, self.every_nth):
                for n in range(half_filter, self.Nx - half_filter,
                               self.every_nth):

                    this_LES_box = (self.c_data_np[l - half_filter:l +
                                                   half_filter, m -
                                                   half_filter:m + half_filter,
                                                   n - half_filter:n +
                                                   half_filter])

                    # this works only if the c_iso value is contained in my array
                    # -> check if array contains values above AND below iso value
                    if np.any(np.where(this_LES_box < c_iso)) and np.any(
                            np.any(np.where(this_LES_box > c_iso))):
                        #start = time.time()
                        verts, faces = measure.marching_cubes_classic(
                            this_LES_box, c_iso)
                        iso_area = measure.mesh_surface_area(verts=verts,
                                                             faces=faces)
                        #end=time.time()
                        #print(' Time marching cube: ', end-start)
                        #iso_area = 0
                    else:
                        iso_area = 0

                    if iso_area / A_planar < 1:
                        isoArea_coefficient[l, m, n] = 0
                    else:
                        isoArea_coefficient[l, m, n] = iso_area / A_planar

                    # iterbar
                    bar.next()

        bar.finish()

        return isoArea_coefficient
    def test_cube_surface_area(self):
        if USE_SCIKIT:
            from skimage.measure import marching_cubes_classic, mesh_surface_area

            volume_data = torch.zeros(1, 5, 5, 5)
            volume_data[0, 1, 1, 1] = 1
            volume_data[0, 1, 1, 2] = 1
            volume_data[0, 2, 1, 1] = 1
            volume_data[0, 2, 1, 2] = 1
            volume_data[0, 1, 2, 1] = 1
            volume_data[0, 1, 2, 2] = 1
            volume_data[0, 2, 2, 1] = 1
            volume_data[0, 2, 2, 2] = 1
            volume_data = volume_data.permute(0, 3, 2, 1)  # (B, D, H, W)
            verts, faces = marching_cubes_naive(volume_data,
                                                return_local_coords=False)
            verts_sci, faces_sci = marching_cubes_classic(volume_data[0])

            surf = mesh_surface_area(verts[0], faces[0])
            surf_sci = mesh_surface_area(verts_sci, faces_sci)

            self.assertClose(surf, surf_sci)
Esempio n. 26
0
    def estimate_surface_area(self):
        """
        Estimate the surface area by summing the areas of a trianglation
        of the nodules surface in 3d. Returned units are mm^2.
        """
        mask = self.get_boolean_mask()
        mask = np.pad(mask, [(1,1), (1,1), (1,1)], 'constant') # Cap the ends.
        dist = dtrans(mask) - dtrans(~mask)

        rxy  = self.scan.pixel_spacing
        rz   = self.scan.slice_thickness
        verts, faces, _, _ = marching_cubes(dist, 0, spacing=(rxy, rxy, rz))
        return mesh_surface_area(verts, faces)
Esempio n. 27
0
def get_surface_area(cell_mask):
    '''
    get cell surface area
    :param cell_mask: single cell mask
    :return surface_are: cell's surface are
    '''
    # ball_structure = morphology.cube(3) # TODO
    # erased_mask = ndimage.binary_erosion(cell_mask, ball_structure, iterations=1)
    # surface_area = np.logical_and(~erased_mask, cell_mask).sum()
    verts, faces, _, _ = marching_cubes_lewiner(cell_mask)
    surface = mesh_surface_area(verts, faces)

    return surface
Esempio n. 28
0
 def run(self, ips, snap, img, para = None):
     ips.lut = self.buflut
     k, unit = ips.unit
     lev, ds, step = para['thr'], para['ds'], para['step']
     scube = np.cumprod(ips.imgs.shape)[-1] * k**3
     sfront = (ips.imgs[::ds,::ds,::ds]>lev).sum() * ds ** 3 * k**3
     sback = scube - sfront
     print(scube, sfront, sback)
     vts, fs, ns, cs =  marching_cubes_lewiner(ips.imgs[::ds,::ds,::ds], lev, step_size=step)
     area = mesh_surface_area(vts, fs) * (ds**2 * k **2)
     rst = [round(i,3) for i in [scube, sfront, sback, sfront/scube, area, area/sfront]]
     titles = ['Cube Volume', 'Volume', 'Blank', 'Volume/Cube', 'Surface', 'Volume/Surface']
     IPy.show_table(pd.DataFrame([rst], columns=titles), ips.title+'-Volume Measure')
Esempio n. 29
0
def test_marching_cubes_anisotropic():
    # test spacing as numpy array (and not just tuple)
    spacing = np.array([1., 10 / 6., 16 / 6.])
    ellipsoid_anisotropic = ellipsoid(6, 10, 16, spacing=spacing,
                                      levelset=True)
    _, surf = ellipsoid_stats(6, 10, 16)
    
    # Classic
    verts, faces = marching_cubes_classic(ellipsoid_anisotropic, 0.,
                                          spacing=spacing)
    surf_calc = mesh_surface_area(verts, faces)
    # Test within 1.5% tolerance for anisotropic. Will always underestimate.
    assert surf > surf_calc and surf_calc > surf * 0.985
    
    # Lewiner
    verts, faces = marching_cubes_lewiner(
        ellipsoid_anisotropic, 0., spacing=spacing)[:2]
    surf_calc = mesh_surface_area(verts, faces)
    # Test within 1.5% tolerance for anisotropic. Will always underestimate.
    assert surf > surf_calc and surf_calc > surf * 0.985

    # Test spacing together with allow_degenerate=False
    marching_cubes_lewiner(ellipsoid_anisotropic, 0, spacing=spacing,
                           allow_degenerate=False)
Esempio n. 30
0
def calc_surface_area(input_data,
                      pixdim=None,
                      affine=None,
                      mask_value=0,
                      mode='edges'):
    """ Reminder: Verify on real-world data.
        Also, some of the binarization feels clumsy/ineffecient.
        Also, this will over-estimate surface area, because
        it is counting cubes instead of, say, triangular
        surfaces
    """

    input_data, input_affine = read_image_files(input_data, return_affine=True)

    input_data = input_data[..., -1]

    pixdim = _get_pixdim(pixdim, affine, input_affine)

    if mode == 'mesh':
        verts, faces = marching_cubes(input_data, 0, pixdim)
        surface_area = mesh_surface_area(verts, faces)

    elif mode == 'edges':
        edges_kernel = np.zeros((3, 3, 3), dtype=float)
        edges_kernel[1, 1, 0] = -1 * pixdim[0] * pixdim[1]
        edges_kernel[0, 1, 1] = -1 * pixdim[1] * pixdim[2]
        edges_kernel[1, 0, 1] = -1 * pixdim[0] * pixdim[2]
        edges_kernel[1, 2, 1] = -1 * pixdim[0] * pixdim[2]
        edges_kernel[2, 1, 1] = -1 * pixdim[1] * pixdim[2]
        edges_kernel[1, 1, 2] = -1 * pixdim[0] * pixdim[1]
        edges_kernel[1, 1,
                     1] = 1 * (2 * pixdim[0] * pixdim[1] + 2 * pixdim[0] *
                               pixdim[2] + 2 * pixdim[1] * pixdim[2])

        label_numpy = np.copy(input_data)
        label_numpy[label_numpy != mask_value] = 1
        label_numpy[label_numpy == mask_value] = 0

        edge_image = signal.convolve(label_numpy, edges_kernel, mode='same')
        edge_image[edge_image < 0] = 0

        surface_area = np.sum(edge_image)

    else:
        print 'Warning, mode parameter', mode, 'not available. Returning None.'
        surface_area = None

    return surface_area
    def compute_this_LES_box(self, l, m, n, half_filter, c_iso,
                             isoArea_coefficient):

        this_LES_box = (self.c_data_np[l - half_filter:l + half_filter,
                                       m - half_filter:m + half_filter,
                                       n - half_filter:n + half_filter])

        # this works only if the c_iso value is contained in my array
        # -> check if array contains values above AND below iso value
        try:  #if np.any(np.where(this_LES_box < c_iso)) and np.any(np.any(np.where(this_LES_box > c_iso))):
            verts, faces = measure.marching_cubes_classic(this_LES_box, c_iso)
            iso_area = measure.mesh_surface_area(verts=verts, faces=faces)
        except ValueError:  #else:
            iso_area = 0

        #isoArea_coefficient[l, m, n] = iso_area / (self.filter_width - 1) ** 2

        return iso_area / (self.filter_width - 1)**2
Esempio n. 32
0
    def surface_area(self):
        """
        Estimate the surface area by summing the areas of a trianglation
        of the nodules surface in 3d. Returned units are mm^2.

        Return
        ------
        sa: float
            The estimated surface area in squared millimeters.
        """
        mask = self.boolean_mask()
        mask = np.pad(mask, [(1,1), (1,1), (1,1)], 'constant') # Cap the ends.
        mask = mask.astype(np.float)

        rij  = self.scan.pixel_spacing
        rk   = self.scan.slice_thickness
        verts, faces, _, _ = marching_cubes(mask, 0.5, spacing=(rij, rij, rk))
        return mesh_surface_area(verts, faces)