def test_structure_tensor_eigenvalues_3d(): image = np.pad(cube(9), 5) * 1000 boundary = (np.pad(cube(9), 5) - np.pad(cube(7), 6)).astype(bool) A_elems = structure_tensor(image, sigma=0.1) e0, e1, e2 = structure_tensor_eigenvalues(A_elems) # e0 should detect facets assert np.all(e0[boundary] != 0)
def water_interface_extraction2(wet, nwet, void): wet = ndimage.binary_dilation(input=wet, structure=cube(4).astype(np.bool)) nwet = ndimage.binary_dilation(input=nwet, structure=cube(4).astype(np.bool)) interface = np.bitwise_and(wet, nwet) interface = np.bitwise_and(interface, void) return interface
def region_growing_from_input(self, color, bone_from_scan=None): """ This function runs the region growing algorithm :param color: the color you want for the bone :param bone_from_scan: If the same scan were used inside another bone """ collect() # initilize if not bone_from_scan: self.load_original_data() else: self.copy_original_from_bone(bone_from_scan) checked = zeros(self._original_img_data.shape) seg = zeros(self._original_img_data.shape) need_to_check = [] # Color the seeds and check for neighbors for seed in self._seeds_points: seg[seed] = color checked[seed] = 1 neighbors = self._get_neighbors(seed, checked, self._original_img_data.shape) for neighbor in neighbors: if self._get_threshold(self._original_img_data[neighbor], BONE_BOUND_VALUES[0], BONE_BOUND_VALUES[1]): need_to_check.append(neighbor) # Region Growing - while there's a neighbor, color it and keep going while need_to_check: pt = need_to_check.pop() if checked[pt] == 1: continue else: checked[pt] = 1 neighbors = self._get_neighbors(pt, checked, self._original_img_data.shape) for neighbor in neighbors: if self._get_threshold(self._original_img_data[neighbor], BONE_BOUND_VALUES[0], BONE_BOUND_VALUES[1]): need_to_check.append(neighbor) seg[pt] = color # Closing holes del need_to_check, checked for i in range(self._dilation): seg = dilation(seg, cube(3, uint8)) for i in range(self._dilation - 1): seg = erosion(seg, cube(3, uint8)) self._segmentation_data = seg del seg collect()
def local_thickness(im): r""" For each voxel, this functions calculates the radius of the largest sphere that both engulfs the voxel and fits entirely within the foreground. This is not the same as a simple distance transform, which finds the largest sphere that could be *centered* on each voxel. Parameters ---------- im : array_like A binary image with the phase of interest set to True Returns ------- An image with the pore size values in each voxel Notes ----- The term *foreground* is used since this function can be applied to both pore space or the solid, whichever is set to True. """ from skimage.morphology import cube if im.ndim == 2: from skimage.morphology import square as cube dt = spim.distance_transform_edt(im) sizes = sp.unique(sp.around(dt, decimals=0)) im_new = sp.zeros_like(im, dtype=float) for r in tqdm(sizes): im_temp = dt >= r im_temp = spim.distance_transform_edt(~im_temp) <= r im_new[im_temp] = r # Trim outer edge of features to remove noise im_new = spim.binary_erosion(input=im, structure=cube(1))*im_new return im_new
def _get_axial_shifts(ndim=2, include_diagonals=False): r''' Helper function to generate the axial shifts that will be performed on the image to identify bordering pixels/voxels ''' if ndim == 2: if include_diagonals: neighbors = square(3) else: neighbors = diamond(1) neighbors[1, 1] = 0 x, y = np.where(neighbors) x -= 1 y -= 1 return np.vstack((x, y)).T else: if include_diagonals: neighbors = cube(3) else: neighbors = octahedron(1) neighbors[1, 1, 1] = 0 x, y, z = np.where(neighbors) x -= 1 y -= 1 z -= 1 return np.vstack((x, y, z)).T
def create_boundary(labels, regions, width=10): """Return new label numpy array including labels for boundary zone Args: labels: 3d numpy array labels information regions: list list containing the label of region(tissue) that we are interested to do dilation on In my case, labels could be either 0, 1, and 0 represents background, so regions=[1] width: int how much distance would you like to dilate Find the boundary region for each region by using dilation method. Take the union of all separate boundary regions as target region and assign it with new label with label of given region + len(regions). """ labels_dilated = labels.copy() nb_region = len(regions) id_protected = np.in1d(labels.ravel(), regions).reshape(labels.shape) kernel = cube(2 * width + 1) for region in regions: labels_binary = np.zeros(labels.shape, dtype=labels.dtype) labels_binary[np.where(labels == region)] = 1 lab_boundary = dilation(labels_binary, kernel) - labels_binary idx_boundary = (lab_boundary == 1) labels_dilated[idx_boundary & ~id_protected] = region + nb_region return labels_dilated
def removeBackground_(data, selem=cube(6)): img = data for i in range(img.shape[0]): img[i, :, :] = median(img[i, :, :]) background = opening(img) sansBackground = img - background return sansBackground
def surface_distance_abs(seg, ref): import skimage.morphology as skm if (np.sum(seg) == 0): return 0 se = skm.cube(3) seg_surface = seg - skm.erosion(seg, se) ref_surface = ref - skm.erosion(ref, se) dist = [] ix, iy, iz = np.where(ref_surface) p_ref = np.array([ix, iy, iz]) indx, indy, indz = np.where(seg_surface) for i in range(len(indx)): p = np.array([[indx[i]], [indy[i]], [indz[i]]]) dists = np.sqrt(np.sum((np.repeat(p, p_ref.shape[1], 1) - p_ref)**2, 0)) dist.append(np.min(dists)) ix, iy, iz = np.where(seg_surface) p_seg = np.array([ix, iy, iz]) indx, indy, indz = np.where(ref_surface) for i in range(len(indx)): p = np.array([[indx[i]], [indy[i]], [indz[i]]]) dists = np.sqrt(np.sum((np.repeat(p, p_seg.shape[1], 1) - p_seg)**2, 0)) dist.append(np.min(dists)) return round(sum(dist) / len(dist), 3)
def plot_tbss(img, mean_FA_skeleton, start, end, row_l=6, step=1, title='', axis='z', pngfile=None): ''' Inspired from plot_two_maps. Plots a TBSS contrast map over the skeleton of a mean FA map''' # Dilate tbss map import numpy as np from skimage.morphology import cube, dilation from nilearn import image d = np.array(image.load_img(img).dataobj) dil_tbss = dilation(d, cube(2)) dil_tbss_img = image.new_img_like(img, dil_tbss) slice_nb = int(abs(((end - start) / float(step)))) images = [] for line in range(int(slice_nb/float(row_l) + 1)): opt = {'title':{True:title, False:None}[line==0], 'colorbar':False, 'black_bg':True, 'display_mode':axis, 'threshold':0.2, 'cmap': cm.Greens, 'cut_coords':range(start + line * row_l * step, start + (line+1) * row_l * step, step)} method = 'plot_stat_map' opt.update({'stat_map_img': mean_FA_skeleton}) t = getattr(plotting, method).__call__(**opt) try: # Add overlay t.add_overlay(dil_tbss_img, cmap=cm.hot, threshold=0.95, colorbar=True) except TypeError: print img, 'probably empty tbss map' pass # Converting to PIL and appending it to the list buf = io.BytesIO() t.savefig(buf) buf.seek(0) im = Image.open(buf) images.append(im) # Joining the images imsize = images[0].size out = Image.new('RGBA', size=(imsize[0], len(images)*imsize[1])) for i, im in enumerate(images): box = (0, i * imsize[1], imsize[0], (i+1) * imsize[1]) out.paste(im, box) if pngfile is None: import tempfile pngfile = tempfile.mkstemp(suffix='.png')[1] print 'Saving to...', pngfile, '(%s)'%title out.save(pngfile)
def getfovLabel(coord, mask, seg, seed_mask, fov_shape): """ 由patch的当前分割结果, 结合整体分割map, 得到下一个 未分割实例的seed点 """ mask_fov = fromSeed2Data(coord, mask, fov_shape) seg_fov = fromSeed2Data(coord, seg, fov_shape) seed_mask_fov = fromSeed2Data(coord, seed_mask, fov_shape) mask1 = np.zeros(mask_fov.shape) mask1[seg_fov > 0] = mask_fov[seg_fov > 0] # mask_fov[seg_fov==0] = 0 mask1 = dilation(mask1, cube(3)) seed_map = ((mask1 == 0) & (seg_fov > 0)).astype(int) # 剩下未分割的区域 seed_mask_fov_a = np.zeros(seed_mask_fov.shape) seed_mask_fov_a[seed_mask_fov > 0] = seed_mask_fov[seed_mask_fov > 0] # seed_mask_fov_a = dilation(seed_mask_fov_a, cube(5)) # prev_seed = ((seed_map>0) & (seed_mask_fov>0)).astype(int) prev_seed = ((seed_map > 0) & (seed_mask_fov_a > 0)).astype(int) if np.sum(prev_seed) > 0: coords = np.stack(np.where(prev_seed > 0), axis=0).T else: coords = np.stack(np.where(seed_map > 0), axis=0).T sel = np.random.choice(len(coords), size=1)[0] coord = coords[sel] label = seed_mask_fov_a[..., coord[0], coord[1], coord[2]] return label, coord
def assign_pore(pore_object, fiber_object, label): mask = pore_object == label mask = ndimage.binary_dilation(input=mask, structure=cube(5)) fiber_contacts = np.unique(fiber_object[mask]) result = np.zeros(5, dtype=np.uint16) result[fiber_contacts + 1] = 1 result[0] = label return result
def solid_interface_extraction(wet, solid): # swap dilation for v13b # wet = ndimage.binary_dilation(input = wet, structure = cube(3).astype(np.bool)) solid = ndimage.binary_dilation(input=solid, structure=cube(4).astype(np.bool)) interface = np.bitwise_and(wet, solid) # interface = np.bitwise_and(interface, void) return interface
def multipleSeedsRG(CT_scan, ROI): """ this function preforms a liver segmentataion by using the following algorithm: 1. Extract N seeds points inside the ROI. 2. Perform Seeded Region Growing with N initial points :param CT_scan: a CT_scan :param ROI: ROI :return: Liver segmentation """ img = nib.load(CT_scan) img_data = img.get_data() visit = np.zeros_like(img_data) # entering first neighbor: findSeeds(CT_scan, ROI) seeds_name = CT_scan.split(".")[0] + "_Seeds.nii.gz" seeds = nib.load(seeds_name) seeds_data = seeds.get_data() visit[seeds_data == 1] = 1 cube = morphology.dilation(seeds_data, morphology.cube(3, np.uint8)) neighbors = np.subtract(cube, seeds_data) neighbors[visit == 1] = 0 visit[neighbors == 1] = 1 num_of_iterations = 0 while np.sum(neighbors) > 0 and num_of_iterations < 40: num_of_iterations += 1 neighbors_only_values = np.zeros_like(img_data) neighbors_only_values[neighbors == 1] = img_data[ neighbors == 1] # the value of each # neighbor where there is one and 0 where there is no neighbor mean = np.mean(img_data[ seeds_data == 1]) # the mean value of all values of all the seed # in the image seeds_data[ np.absolute(np.subtract(mean, neighbors_only_values)) < 40] = 1 # update neighbors: cube = morphology.dilation(seeds_data, morphology.cube(3, np.uint8)) neighbors = np.subtract(cube, seeds_data) neighbors[visit == 1] = 0 visit[neighbors == 1] = 1 scan_name = CT_scan.split(".")[0] nib.save(seeds, scan_name + "_LiverSegBeforeClean.nii.gz") cleanLiver(CT_scan)
def trim_nearby_peaks(peaks, dt): r""" Finds pairs of peaks that are nearer to each other than to the solid phase, and removes the peak that is closer to the solid. Parameters ---------- peaks : ND-array A boolean image containing True values to mark peaks in the distance transform (``dt``) dt : ND-array The distance transform of the pore space for which the true peaks are sought. Returns ------- image : ND-array An array the same size as ``peaks`` containing a subset of the peaks in the original image. Notes ----- Each pair of peaks is considered simultaneously, so for a triplet of peaks each pair is considered. This ensures that only the single peak that is furthest from the solid is kept. No iteration is required. """ peaks = sp.copy(peaks) if dt.ndim == 2: from skimage.morphology import square as cube else: from skimage.morphology import cube peaks, N = spim.label(peaks, structure=cube(3)) crds = spim.measurements.center_of_mass(peaks, labels=peaks, index=sp.arange(1, N + 1)) crds = sp.vstack(crds).astype(int) # Convert to numpy array of ints # Get distance between each peak as a distance map tree = sptl.cKDTree(data=crds) temp = tree.query(x=crds, k=2) nearest_neighbor = temp[1][:, 1] dist_to_neighbor = temp[0][:, 1] del temp, tree # Free-up memory dist_to_solid = dt[tuple(crds.T)] # Get distance to solid for each peak hits = sp.where(dist_to_neighbor < dist_to_solid)[0] # Drop peak that is closer to the solid than it's neighbor drop_peaks = [] for peak in hits: if dist_to_solid[peak] < dist_to_solid[nearest_neighbor[peak]]: drop_peaks.append(peak) else: drop_peaks.append(nearest_neighbor[peak]) drop_peaks = sp.unique(drop_peaks) # Remove peaks from image slices = spim.find_objects(input=peaks) for s in drop_peaks: peaks[slices[s]] = 0 return (peaks > 0)
def trim_saddle_points(peaks, dt, max_iters=10): r""" Removes peaks that were mistakenly identified because they lied on a saddle or ridge in the distance transform that was not actually a true local peak. Parameters ---------- peaks : ND-array A boolean image containing True values to mark peaks in the distance transform (``dt``) dt : ND-array The distance transform of the pore space for which the true peaks are sought. max_iters : int The maximum number of iterations to run while eroding the saddle points. The default is 10, which is usually not reached; however, a warning is issued if the loop ends prior to removing all saddle points. Returns ------- image : ND-array An image with fewer peaks than the input image """ peaks = sp.copy(peaks) if dt.ndim == 2: from skimage.morphology import square as cube else: from skimage.morphology import cube labels, N = spim.label(peaks) slices = spim.find_objects(labels) for i in range(N): s = extend_slice(s=slices[i], shape=peaks.shape, pad=10) peaks_i = labels[s] == i + 1 dt_i = dt[s] im_i = dt_i > 0 iters = 0 peaks_dil = sp.copy(peaks_i) while iters < max_iters: iters += 1 peaks_dil = spim.binary_dilation(input=peaks_dil, structure=cube(3)) peaks_max = peaks_dil * sp.amax(dt_i * peaks_dil) peaks_extended = (peaks_max == dt_i) * im_i if sp.all(peaks_extended == peaks_i): break # Found a true peak elif sp.sum(peaks_extended * peaks_i) == 0: peaks_i = False break # Found a saddle point peaks[s] = peaks_i if iters >= max_iters: print('Maximum number of iterations reached, consider' + 'running again with a larger value of max_iters') return peaks
def simple_otsu(im, trim_solid=True): r""" Uses Otsu's method to find a threshold, then uses binary opening and closing to remove noise. Parameters ---------- im : ND-image The greyscale image of the porous medium to be binarized. trim_solid : Boolean If True (default) then all solid voxels not connected to an image boundary are trimmed. Returns ------- An ND-image the same size as ``im`` but with True and False values indicating the binarized or segmented phases. Examples -------- >>> im = ps.generators.blobs([300, 300], porosity=0.5) >>> im = ps.generators.add_noise(im) >>> im = spim.gaussian_filter(im, sigma=1) >>> im = simple_otsu(im) """ if im.ndim == 2: ball = disk cube = square im = sp.pad(array=im, pad_width=1, mode='constant', constant_values=1) val = filters.threshold_otsu(im) im = im >= val # Remove speckled noise from void and solid phases im = spim.binary_closing(input=im, structure=ball(1)) im = spim.binary_opening(input=im, structure=ball(1)) # Clean up edges im = spim.binary_closing(input=im, structure=cube(3)) im = spim.binary_opening(input=im, structure=cube(3)) im = im[[slice(1, im.shape[d] - 1) for d in range(im.ndim)]] temp = clear_border(~im) im = im + temp return im
def interface_extraction(wet, void): dry = np.bitwise_and(void, ~wet) # wet = ndimage.binary_dilation(input = wet, structure = cube(3).astype(np.bool)) dry = ndimage.binary_dilation(input=dry, structure=cube(4).astype(np.bool)) interface = np.bitwise_and(wet, dry) interface = np.bitwise_and(interface, void) # interface = skimage.morphology.binary_erosion(interface, selem=ball(1).astype(np.bool)) # interface = skimage.morphology.binary_dilation(interface, selem=ball(1).astype(np.bool)) return interface
def __postProcessing(mask): pred_mask = binary_closing(np.squeeze(mask), cube(2)) try: labels = label(pred_mask) pred_mask = (labels == np.argmax(np.bincount(labels.flat)[1:]) + 1).astype(np.uint16) except: pred_mask = pred_mask return pred_mask
def condition(patch_mask, seg_pad, coord, fov_shape): fov_mask = fromSeed2Data(coord, patch_mask, fov_shape) fov_seg = fromSeed2Data(coord, seg_pad, fov_shape) mask1 = np.zeros(fov_mask.shape) mask1[fov_seg > 0] = fov_mask[fov_seg > 0] # fov_mask[fov_seg==0] = 0 # c = (not np.all((mask1>0) & (fov_seg>0))) and (np.any(fov_seg>0)) mask1 = dilation(mask1, cube(3)) c = ((mask1 == 0) & (fov_seg > 0)).astype(int) return np.sum(c)
def nuclearDetectionList(selem=6, min_sigma=0.25, max_sigma=5, threshold=0.04, writeOut=False, directoryPath=None, name=None, cube_selem=True): """ FUNCTION: write a list of function dictionaries (as per Chain; ChainMethod) ARGUMENTS: selem: structure element for morphological opening. If cube_selem, skimage.morphology.cube() will be applied to this arg. In this case, selem should be int. min_sigma: standard deviation of the smallest Gaussian kernel to be used in the scale-space representation in Laplacian of Gaussian blob detection (skimage.feature.blob_log()). Determines the smallest blobs that can be detected. max_sigma: standard deviation of the largest Gaussian kernel to be used in the scale-space representation in Laplacian of Gaussian blob detection (skimage.feature.blob_log()). Determines the largest blobs that can be detected. threshold: lower threshold of LoG local minima that will be detected as a blob. Determines the lowest intensity of blob that can be detected writeOut: should images labeling blob coordinates be written out? (bool) directoryPath: if writeOut, this is the directory to which the blob images will be saved. name: if writeOut, this name will be saved as part of the blob image file names. Will probably reflect the parameters chosen. If writeOut and name is None, this will be automatically defined. cube_selem: should skimage.morphology.cube() be applied to the inputted selem? (bool) DEPENDENCIES: numpy as np (when implemented in chain) Native: (1)medianFilter3D()/skimage.filters.median(); (2)removeBackground()/skimage.morphology.opening(); getBlobs_LoG()/(3)skimage.feature.blob_log(); (4)writeBlobsImg()/skimage.draw.circle_perimeter 1) Median filter sequentially applied to x-y planes along z 2) Applies morphological opening to generate a background image. This is subtracted from the original and the result is returned. 3) A Laplacian of Gaussian kernel returns an image whereby local minima reflect blobs (~ spherical areas of highest local intensity) similar to the size of the kernels sigma. When applied across a scale space, blobs of a variety of sizes can be identified. In order to define blobs, some absolute threshold for magnitude of minima is chosen. Returns np.ndarray with shape (n, 4) where columns are z, y, x, sigma. 4) Writes images demarcating the locations of blobs by writing a series of x-y images in which the centre of a blob in that plane is encircled by a circle of radius equal to the sigma recorded for the blob. RETURNS: list ([{...}, {...}, {...}]) """ _selem = selem if cube_selem: selem = cube(selem) med = {'function': medianFilter3D, 'suffix': 'MF', 'writeOut': None} remBack = {'function': removeBackground, 'suffix': 'sansBk', 'writeOut': None, 'args': [selem]} if writeOut: if name is None: name = 'o{}_min{}_max{}_thr{}'.format(_selem, min_sigma, max_sigma, threshold) blobs = {'function': getBlobs_LoG, 'suffix': 'blob_log', 'kwargs': {'min_sigma': min_sigma, 'max_sigma': max_sigma, 'threshold': threshold}, 'writeOut': {'function': writeBlobsImg, 'priorLink_kw': 'img', 'newLink_kw': 'blobs', 'writeInfo_kw': 'file_', 'kwargs': {'outDir': directoryPath, 'name': name}}} else: blobs = {'function': getBlobs_LoG, 'suffix': 'blob_log', 'kwargs': {'min_sigma': min_sigma, 'max_sigma': max_sigma, 'threshold': threshold}, 'writeOut': None} funcList = [med, remBack, blobs] return funcList
def mkIsoStruct(dilate_factor, aspect): ''' Builds isotropic structuring element for dilation. Args: dilate_factor (int): number of px for isotropic 2D dilation. aspect (tuple(float)): voxel side ratios. Returns: np.ndarray: structureing element for 3D anisotropic dilation. ''' # XY dilation factor df_xy = int(dilate_factor * 2 + 1) if 0 == aspect[0]: se = cube(df_xy) se = se[0] new_shape = [1] [new_shape.append(d) for d in se.shape] se = se.reshape(new_shape) return (se) # Z dilation factor df_z = int(dilate_factor * aspect[1] / aspect[0] * 2 + 1) if df_z == df_xy: # Isotropic return (cube(df_z)) elif df_z > df_xy: # Larger Z side se = cube(df_z) se = se[:, 0:df_xy, 0:df_xy] else: # Larger XY side se = cube(df_xy) se = se[0:df_z] # Output return (se)
def morph_process(mask, elem_len=1, radius=10, save_labeled=None): # 1. closing and opening process of vesicle mask. 2. label the vesicles. # 3. exclude fasle vesicles by counting their volumes and thresholding, return only vesicle binary mask # 4. extract boundaries and labels them # 5. extract labeled individual vesicle boundary, convert into points vectors and output them. from skimage.morphology import opening, closing, erosion, cube from skimage.measure import label with mrcfile.open(mask) as f: tomo_mask = f.data # transform mask into uint8 bimask = np.round(tomo_mask).astype(np.uint8) #closing_opening = closing(opening(bimask,cube(elem_len)),cube(elem_len+2)) #closing_opening = opening(closing(bimask, cube(elem_len+2)),cube(elem_len)) closing_opening = closing(opening(bimask, cube(elem_len)), cube(elem_len)) # label all connected regions labeled = label(closing_opening) idx = get_indices_sparse(labeled) num = np.max(labeled) for i in range(1, num + 1): if idx[i][0].shape[0] < radius**3 * 4: labeled[idx[i][0], idx[i][1], idx[i][2]] = 0 filtered = (labeled >= 1).astype(np.uint8) print('complete filtering') boundaries = filtered - erosion(filtered, cube(3)) # label the boundaries of vesicles bd_labeled = label(boundaries) #the number of labeled vesicle num = np.max(bd_labeled) #vesicle list elements: np.where return point cloud positions whose shape is (3,N) idx = get_indices_sparse(bd_labeled) vesicle_list = [ np.swapaxes(np.array(idx[i]), 0, 1) for i in range(1, num + 1) ] # for i in range(1,num+1): # cloud = np.array(np.where(bd_labeled == i)) # cloud = np.swapaxes(cloud,0,1) # vesicle_list.append(cloud) return vesicle_list
def FillHoles(InputTensor,FillingStructure = cube(4)): # Convert tensor to numpy array InputNumpyArray = InputTensor.numpy() # The closing of an input image by erosion and dilation of the image by a structuring element. # Closing fills holes smaller than the structuring element ClosedArray = binary_closing(InputNumpyArray,structure=FillingStructure ).astype(int) # Fill the holes in binary objects OutputArray = binary_fill_holes(ClosedArray, structure=FillingStructure ).astype(int) # convert numpy array to tensor TensorOutput = torch.from_numpy(OutputArray) return TensorOutput
def selective_seeds_generate(centerline, junc, num_seeds=20): junc_mask = dilation(junc, cube(8)) p = centerline mask = np.logical_and(junc, centerline) p[mask] = 20 p = p.astype(float) / np.sum(p) seeds = np.zeros_like(centerline) inds = np.arange(seeds.size)[(centerline > 0).flatten()] inds_sel = np.random.choice(inds, num_seeds, replace=False, p.flatten()) #xs,ys,zs = np.unravel_index(inds_sel,seeds.shape) #seeds[xs,ys,zs] = 1 #return seeds return inds_sel
def fun_generar_vol_random(tipo_forma, tam_limite, transp_forma): # Base volume generation if (tipo_forma == 1): # Random size tam_aux = rnd.randint(tam_limite[0], tam_limite[1]) # Shape creation vol_forma = morphology.cube(tam_aux) elif (tipo_forma == 2): # Random size tam_aux = rnd.randint(tam_limite[0], tam_limite[1]) # Shape creation vol_forma = morphology.ball(tam_aux) elif (tipo_forma == 3): # Random size tam_forma = (rnd.randint(tam_limite[0], tam_limite[1]), rnd.randint(tam_limite[0], tam_limite[1]), rnd.randint(tam_limite[0] * 2, tam_limite[1] * 2)) # Shape creation vol_forma = fun_cilindro(tam_forma) elif (tipo_forma == 4): # Random size tam_aux = rnd.randint(tam_limite[0], tam_limite[1]) # Shape creation vol_forma = morphology.octahedron(tam_aux) elif (tipo_forma == 5): # Random size tam_forma = (rnd.randint(tam_limite[0], tam_limite[1]), rnd.randint(tam_limite[0], tam_limite[1]), rnd.randint(tam_limite[0] * 2, tam_limite[1] * 2)) # Shape creation vol_forma = fun_estrella_cil_vol(tam_forma) elif (tipo_forma == 6): # Random size tam_aux = rnd.randint(tam_limite[0], tam_limite[1]) # Shape creation vol_forma = fun_estrella_rot_vol(tam_aux) # Shape label population label_forma = np.multiply(vol_forma, tipo_forma) # Transparency vol_forma = np.multiply(vol_forma, transp_forma) tam_forma = vol_forma.shape return (tam_forma, vol_forma, label_forma)
def find_disconnected_voxels(im, conn=None): r""" This identifies all pore (or solid) voxels that are not connected to the edge of the image. This can be used to find blind pores, or remove artifacts such as solid phase voxels that are floating in space. Parameters ---------- im : ND-image A Boolean image, with True values indicating the phase for which disconnected voxels are sought. conn : int For 2D the options are 4 and 8 for square and diagonal neighbors, while for the 3D the options are 6 and 26, similarily for square and diagonal neighbors. The default is max Returns ------- image : ND-array An ND-image the same size as ``im``, with True values indicating voxels of the phase of interest (i.e. True values in the original image) that are not connected to the outer edges. Notes ----- image : ND-array The returned array (e.g. ``holes``) be used to trim blind pores from ``im`` using: ``im[holes] = False`` """ if im.ndim != im.squeeze().ndim: warnings.warn('Input image conains a singleton axis:' + str(im.shape) + ' Reduce dimensionality with np.squeeze(im) to avoid' + ' unexpected behavior.') if im.ndim == 2: if conn == 4: strel = disk(1) elif conn in [None, 8]: strel = square(3) elif im.ndim == 3: if conn == 6: strel = ball(1) elif conn in [None, 26]: strel = cube(3) labels, N = spim.label(input=im, structure=strel) holes = clear_border(labels=labels) > 0 return holes
def binarize(i, thr): """Binarize an image using the provided threshold. Args: i (np.array): image. thr (float or int): intensity threshold. Returns: np.array: thresholded image. """ if 2 == len(i.shape): i = closing(i > thr, square(3)) elif 3 == len(i.shape): i = closing(i > thr, cube(3)) return (i)
def threshold_adaptive(i, block_size, doClosing=True, method=None, mode=None, param=None): """Adaptive threshold. Args: i (np.array): image. block_size (int): neighbourhood, if even then it is incremented by 1. doClosing (bool): to trigger closing operation after local thresholding. method, mode, param: additional parameters for threshold_local. Returns: np.array: thresholded image. """ # Increment neighbourhood size if 0 == block_size % 2: block_size += 1 # Local threshold mask lmask = i.copy() # Apply threshold per slice if 3 == len(i.shape): for slice_id in range(i.shape[0]): lmask[slice_id, :, :] = filters.threshold_local(i[slice_id, :, :], block_size, method=method, mode=mode, param=param) lmask = i >= lmask if doClosing: lmask = closing(lmask, cube(3)) else: lmask = filters.threshold_local(i, block_size, method=method, mode=mode, param=param) lmask = i >= lmask if doClosing: lmask = closing(lmask, square(3)) # Output return (lmask)
def class_map_zero_pixel_closing(class_map, zero_pixel_pos_list, range): increase_range = True if len(zero_pixel_pos_list) == 0: return class_map else: clean_map = class_map class_map = closing(class_map, cube(range)).astype(np.int8) for (x, y, z) in zero_pixel_pos_list: clean_map[x, y, z] = class_map[x, y, z] if class_map[x, y, z] != 0: zero_pixel_pos_list.remove((x, y, z)) increase_range = False if increase_range: range += 1 return class_map_zero_pixel_closing(clean_map, zero_pixel_pos_list, range)
def objects(): from skimage.morphology import (square, rectangle, diamond, disk, cube, octahedron, ball, octagon, star) from mpl_toolkits.mplot3d import Axes3D # Generate 2D and 3D structuring elements. struc_2d = { "square(15)": square(15), "rectangle(15, 10)": rectangle(15, 10), "diamond(7)": diamond(7), "disk(7)": disk(7), "octagon(7, 4)": octagon(7, 4), "star(5)": star(5) } struc_3d = { "cube(11)": cube(11), "octahedron(5)": octahedron(5), "ball(5)": ball(5) } # Visualize the elements. fig = plt.figure(figsize=(8, 8)) idx = 1 for title, struc in struc_2d.items(): ax = fig.add_subplot(3, 3, idx) ax.imshow(struc, cmap="Paired", vmin=0, vmax=12) for i in range(struc.shape[0]): for j in range(struc.shape[1]): ax.text(j, i, struc[i, j], ha="center", va="center", color="w") ax.set_axis_off() ax.set_title(title) idx += 1 for title, struc in struc_3d.items(): ax = fig.add_subplot(3, 3, idx, projection=Axes3D.name) ax.voxels(struc) ax.set_title(title) idx += 1 fig.tight_layout() plt.show()
def vesicle_rendering(vesicle_file, tomo_dims): # vesicle file can be json or a info list from tomoSgmt.utils import make_ellipsoid as mk from skimage.morphology import closing, cube if type(vesicle_file) is str: with open(vesicle_file) as f: ves = json.load(f) vesicle_info = ves['vesicles'] else: vesicle_info = vesicle_file vesicle_tomo = np.zeros(np.array(tomo_dims) + np.array([30, 30, 30]), dtype=np.uint8) for i, vesicle in enumerate(vesicle_info): print(i) ellip_i = mk.ellipsoid_point(np.array(vesicle['radii']), np.array(vesicle['center']), np.array(vesicle['evecs'])) #ellip_i is an array (N,3) of points of a filled ellipsoid vesicle_tomo[ellip_i[:, 0], ellip_i[:, 1], ellip_i[:, 2]] = 1 vesicle_tomo = closing(vesicle_tomo, cube(3)) return vesicle_tomo[0:tomo_dims[0], 0:tomo_dims[1], 0:tomo_dims[2]]
def neigh_br(arr_nodes, arr_br): """ Creates a graph with nodes from the array of nodes and branches by considering the branches that adjoin to the current node. The labels of these branches are set as an attribute of the node. Parameters --------- arr_nodes : array of labeled regions that are considered as nodes arr_br : array of labeled regions considered as branches Returns ------ G : a graph of nodes with the attribute 'neigh' Examples ------- >>>arr_nodes = label_nodes(num) >>>arr_nodes array([[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], [[0, 0, 0], [1, 1, 0], [1, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]], dtype=uint8) >>>arr_br = label_br(num) >>>arr_br array([[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], [[1, 0, 0], [0, 0, 2], [0, 0, 0], [3, 0, 0]], [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]], dtype=uint8) >>>G = neigh_br(arr_nodes, arr_br) >>>G.nodes() [1] >>>nx.get_node_attributes(G, 'neigh') {1: array([1, 2, 3])} """ G = nx.MultiGraph() nodes = np.unique(arr_nodes)[1:] find_nodes = ndimage.find_objects(arr_nodes) start = np.zeros(3) stop = np.zeros(3) cube = morphology.cube(3) for j in nodes: sl = find_nodes[j-1] for i in range(3): start[i] = sl[i].start - 1 stop[i] = sl[i].stop + 1 #volume with node vol_c_n = arr_nodes[start[0]:stop[0], start[1]:stop[1], start[2]:stop[2]] #dilation to find the joint branches dil = ndimage.binary_dilation(vol_c_n, cube) dil = dil.astype('uint8') vol_c = arr_br[start[0]:stop[0], start[1]:stop[1], start[2]:stop[2]] #find the labels of connected branches n = np.unique(vol_c * dil)[1:] G.add_node(j, neigh=n) return G
def neigh_br(arr_nodes, arr_br): """ Creates a graph with nodes from the array of nodes and branches by considering the branches that adjoin to the current node. The labels of these branches are set as an attribute of the node. Parameters --------- arr_nodes : array of labeled regions that are considered as nodes arr_br : array of labeled regions considered as branches Returns ------ G : a graph of nodes with the attribute 'neigh' Examples ------- >>>arr_nodes = label_nodes(num) >>>arr_nodes array([[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], [[0, 0, 0], [1, 1, 0], [1, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]], dtype=uint8) >>>arr_br = label_br(num) >>>arr_br array([[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], [[1, 0, 0], [0, 0, 2], [0, 0, 0], [3, 0, 0]], [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]], dtype=uint8) >>>G = neigh_br(arr_nodes, arr_br) >>>G.nodes() [1] >>>nx.get_node_attributes(G, 'neigh') {1: array([1, 2, 3])} """ G = nx.Graph() el = morphology.cube(3) nodes = np.unique(arr_nodes)[1:] for j in nodes: mask = arr_nodes==j dil = morphology.binary_dilation(mask, el) vol_c = arr_br * (dil.astype('uint8')) G.add_node(j, neigh=np.unique(vol_c)[1:]) return G