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)
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
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
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
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)
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
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)
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)
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
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!'
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
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)
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
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
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.
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)
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)
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
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')
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)
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
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)