def holes_filling(input, _): struct_1 = np.array([ [1], [1], [1] ]) struct_2 = np.array([ [1, 1, 1] ]) structure = np.array([ [0, 1, 0], [1, 1, 1], [0, 1, 0] ]) mask = complementary(input) #iter = 0 X0 = np.zeros(input.shape) while True: d1 = ndimage.binary_dilation(X0, struct_1, border_value=1, mask=mask) d2 = ndimage.binary_dilation(X0, struct_2, border_value=1, mask=mask) X1 = union(d1, d2) if np.array_equal(X0, X1): break X0 = X1 #iter += 1 return complementary(X0)
def lnlike(theta , p): cx, cy , F, B = theta Kp = sampler.imatrix_new(25, H, cx, cy) #Kp = sampler_new.imatrix_new(25, H , cx[p] , cy[p]) model = F*np.dot(fl+X,Kp) + B resi = (data[p,:] - model).reshape(25,25) res = (data[p,:] - model)*100/data[p,:] res = res.reshape(25,25) chisq = (data[p,:] - model)**2./(f+g*np.abs(model)) + np.log(f+g*np.abs(model)) chi= (data[p,:] - model)/(f+g*np.abs(model)+q*(model)**2.)**0.5 maskp = mask[p,:] chisq = chisq.reshape(25,25) chi = chi.reshape(25,25) maskp = maskp.reshape(25,25) mast = np.abs(chi)**2. > 3 mast = ndimage.binary_dilation(mast) mast = ndimage.binary_dilation(mast) bad = maskp != 0 unhealthy = bad * mast chisq = chisq[unhealthy == False] return np.sum(chisq)
def find_initial_worm(small_image, well_mask): # plan here is to find known good worm edges with Canny using a stringent threshold, then # relax the threshold in the vicinity of the good edges. # back off another pixel from the well edge to avoid gradient from the edge shrunk_mask = ndimage.binary_erosion(well_mask, structure=S) smoothed, gradient, sobel = canny.prepare_canny(small_image, 2, shrunk_mask) local_maxima = canny.canny_local_maxima(gradient, sobel) # Calculate stringent and medium-stringent thresholds. The stringent threshold # is the 200th-brightest edge pixel, and the medium is the 450th-brightest pixel highp = 100 * (1-200/local_maxima.sum()) highp = max(highp, 94) mediump = 100 * (1-450/local_maxima.sum()) mediump = max(mediump, 94) low_worm, medium_worm, high_worm = numpy.percentile(gradient[local_maxima], [94, mediump, highp]) stringent_worm = canny.canny_hysteresis(local_maxima, gradient, low_worm, high_worm) # Expand out 20 pixels from the stringent worm edges to make our search space stringent_area = ndimage.binary_dilation(stringent_worm, mask=well_mask, iterations=20) # now use the relaxed threshold but only in the stringent area relaxed_worm = canny.canny_hysteresis(local_maxima, gradient, low_worm, medium_worm) & stringent_area # join very close-by objects, and remove remaining small objects candidate_worm = ndimage.binary_dilation(relaxed_worm, structure=S) candidate_worm = ndimage.binary_erosion(candidate_worm) candidate_worm = mask.remove_small_area_objects(candidate_worm, 30, structure=S) # Now figure out the biggest blob of nearby edges, and call that the worm region glommed_candidate = ndimage.binary_dilation(candidate_worm, structure=S, iterations=2) glommed_candidate = ndimage.binary_erosion(glommed_candidate, iterations=2) # get just outline, not any regions filled-in due to closing glommed_candidate ^= ndimage.binary_erosion(glommed_candidate) glommed_candidate = mask.get_largest_object(glommed_candidate, structure=S) worm_area = ndimage.binary_dilation(glommed_candidate, mask=well_mask, structure=S, iterations=12) worm_area = mask.fill_small_radius_holes(worm_area, max_radius=15) candidate_edges = relaxed_worm & candidate_worm & worm_area return candidate_edges, worm_area
def binary_dilation(image, selem=None, out=None): """Return fast binary morphological dilation of an image. This function returns the same result as greyscale dilation but performs faster for binary images. Morphological dilation sets a pixel at ``(i,j)`` to the maximum over all pixels in the neighborhood centered at ``(i,j)``. Dilation enlarges bright regions and shrinks dark regions. Parameters ---------- image : ndarray Binary input image. selem : ndarray, optional The neighborhood expressed as a 2-D array of 1's and 0's. If None, use cross-shaped structuring element (connectivity=1). out : ndarray of bool, optional The array to store the result of the morphology. If None, is passed, a new array will be allocated. Returns ------- dilated : ndarray of bool or uint The result of the morphological dilation with values in ``[False, True]``. """ if out is None: out = np.empty(image.shape, dtype=np.bool) ndi.binary_dilation(image, structure=selem, output=out) return out
def filterImage(image): """ Filters the given image and returns a binary representation of it. """ # otsu to bring out edges t_loc_otsu = otsu(image[:, :, 1]) loc_otsu = np.zeros_like(image, dtype=np.bool) loc_otsu[:, :, 1] = image[:, :, 1] <= t_loc_otsu + 5 image[loc_otsu] = 0 # bring out single particles and smooth the rest foot = circarea(8) green = rank_filter(image[:,:,1], foot, rank=44) nonzero = green > 10 weak = (green > 20) & (green < green[nonzero].mean()) green[weak] += 40 # remove pollution gray = cv2.medianBlur(green, ksize=13) # black and white representation of particles and surroundings binary = gray < 25 # dilatation and erosion dilated1 = ndimage.binary_dilation(binary, iterations=6) erosed = ndimage.binary_erosion(dilated1, iterations=_EROSIONFACTOR+3) dilated = ndimage.binary_dilation(erosed, iterations=_EROSIONFACTOR) return dilated
def clean_classified_image(self): """ clean the binary image resulting from pixel classification using morphological operators """ if self.class_image is None: self.classify_image() bim = self.class_image feature_mask = self.features_object.mask_image if feature_mask is not None: bim = bim & feature_mask bim = ni.binary_fill_holes(bim) min_gap = 0 for n in range(min_gap): bim = ni.binary_dilation(bim) #bim = ni.binary_closing(bim) #bim = ni.binary_fill_holes(bim) min_radius = 8 for n in range(min_radius): bim = ni.binary_erosion(bim) #bim = ni.binary_opening(bim) for n in range(min_radius): bim = ni.binary_dilation(bim) #bim = ni.binary_dilation(bim) #bim = ni.binary_erosion(bim) self.clean_class_image = bim.astype(np.uint8) * 255
def dilation(): image_list = get_one_imagefrom_mnist() image_array =np.asarray(image_list) image =image_array.reshape(28, 28) ndimage.binary_dilation(image).astype(int) plt.imshow(image, cmap=cm.binary) plt.show()
def split_up_binary_regions(seq_block, opening_param = 3, mshape = ((0,1,0),(1,1,1),(0,1,0)), min_size = 20): # for each region: # dilate, relabel, create stack with eroded single regions # delete overlapping pixels, recombine, assigning unique labels zdim, xdim, ydim = seq_block.shape splitblock = np.zeros_like(seq_block) for sidx in range(zdim): num_labels = int(np.max(seq_block[sidx])) if num_labels <= 1: continue max_label = num_labels for lidx in range(num_labels): comp = extract_region(seq_block[sidx], lidx+1) if np.sum(comp) < min_size: continue component = ndi.binary_erosion(comp,structure = mshape,iterations = opening_param) component, numnewcomponents = ndi.label(component) if numnewcomponents == 1: # component is not split up by erosion #component[component>0] = 1 #component = ndi.binary_dilation(component,structure = mshape,iterations = opening_param) splitblock[sidx][comp > 0] = int(lidx+1) else: compstack = np.zeros((numnewcomponents,xdim,ydim)) for cidx in range(numnewcomponents): compstack[cidx] = ndi.binary_dilation(extract_region(component, cidx+1),structure = mshape,iterations = opening_param) compstack[cidx] = ndi.binary_dilation(compstack[cidx],structure = ((0,1,0),(1,1,1),(0,1,0)),iterations = 2) overlapmask = np.sum(compstack, axis = 0) comp[overlapmask > 1] = 0 comp, numnewcomponents2 = ndi.label(comp) #if numnewcomponents != numnewcomponents2: #print "Lost something on the way: C1 = " + str(numnewcomponents) + ", C2 = " + str(numnewcomponents2) for cidx in range(numnewcomponents2): component = extract_region(comp, cidx+1) if np.sum(component) < min_size: continue #compstack[cidx] = compstack[cidx] * overlapmask #if np.sum(compstack[cidx]) > 0: max_label = max_label + 1 #compstack[cidx] = compstack[cidx] * max_label splitblock[sidx] = splitblock[sidx] + component.astype(int) * int(max_label) #splitblock[sidx] = splitblock[sidx] + np.sum(compstack,axis = 0).astype(int) # relabel components to unique labels all_labels = np.sort(np.unique(splitblock[sidx])) dum = np.zeros_like(splitblock[sidx]) l_idx = 1 for label in all_labels: if label >0: dum[splitblock[sidx]==label] = l_idx l_idx = l_idx + 1 return splitblock
def refine_worm(image, initial_area, candidate_edges): # find strong worm edges (roughly equivalent to the edges found by find_initial_worm, # which are in candidate_edges): smooth the image, do canny edge-finding, and # then keep only those edges near candidate_edges smooth_image = restoration.denoise_tv_bregman(image, 140).astype(numpy.float32) smoothed, gradient, sobel = canny.prepare_canny(smooth_image, 8, initial_area) local_maxima = canny.canny_local_maxima(gradient, sobel) candidate_edge_region = ndimage.binary_dilation(candidate_edges, iterations=4) strong_edges = local_maxima & candidate_edge_region # Now threshold the image to find dark blobs as our initial worm region # First, find areas in the initial region unlikely to be worm pixels mean, std = mcd.robust_mean_std(smooth_image[initial_area][::4], 0.85) non_worm = (smooth_image > mean - std) & initial_area # now fit a smoothly varying polynomial to the non-worm pixels in the initial # region of interest, and subtract that from the actual image to generate # an image with a flat illumination field background = polyfit.fit_polynomial(smooth_image, mask=non_worm, degree=2) minus_bg = smooth_image - background # now recalculate a threshold from the background-subtracted pixels mean, std = mcd.robust_mean_std(minus_bg[initial_area][::4], 0.85) initial_worm = (minus_bg < mean - std) & initial_area # Add any pixels near the strong edges to our candidate worm position initial_worm |= ndimage.binary_dilation(strong_edges, iterations=3) initial_worm = mask.fill_small_radius_holes(initial_worm, 5) # Now grow/shrink the initial_worm region so that as many of the strong # edges from the canny filter are in contact with the region edges as possible. ac = active_contour.EdgeClaimingAdvection(initial_worm, strong_edges, max_region_mask=initial_area) stopper = active_contour.StoppingCondition(ac, max_iterations=100) while stopper.should_continue(): ac.advect(iters=1) ac.smooth(iters=1, depth=2) worm_mask = mask.fill_small_radius_holes(ac.mask, 7) # Now, get edges from the image at a finer scale smoothed, gradient, sobel = canny.prepare_canny(smooth_image, 0.3, initial_area) local_maxima = canny.canny_local_maxima(gradient, sobel) strong_sum = strong_edges.sum() highp = 100 * (1 - 1.5*strong_sum/local_maxima.sum()) lowp = max(100 * (1 - 3*strong_sum/local_maxima.sum()), 0) low_worm, high_worm = numpy.percentile(gradient[local_maxima], [lowp, highp]) fine_edges = canny.canny_hysteresis(local_maxima, gradient, low_worm, high_worm) # Expand out the identified worm area to include any of these finer edges closed_edges = ndimage.binary_closing(fine_edges, structure=S) worm = ndimage.binary_propagation(worm_mask, mask=worm_mask|closed_edges, structure=S) worm = ndimage.binary_closing(worm, structure=S, iterations=2) worm = mask.fill_small_radius_holes(worm, 5) worm = ndimage.binary_opening(worm) worm = mask.get_largest_object(worm) # Last, smooth the shape a bit to reduce sharp corners, but not too much to # sand off the tail ac = active_contour.CurvatureMorphology(worm, max_region_mask=initial_area) ac.smooth(depth=2, iters=2) return strong_edges, ac.mask
def removeGrid(self,cs,removeGrid): """ Detect the grid of the phantom and remove it from the image """ shift = int(1./self.pixDim(cs)+.5) # try to find a threshold on pixelvalue to define a value representing the grid maskval = 0.75*cs.pixeldataIn.mean() # make a mask of grid-like values mask = (cs.pixeldataIn < maskval) # hole closing of the mask mask = scind.binary_closing(mask,structure=np.ones((5,5))) mask = scind.binary_opening(mask,structure=np.ones((5,5))) mask = scind.binary_dilation(mask) # new since 20150211 mask = scind.binary_dilation(mask) # fill the gridlines with the median values of the pixels around it medimage = np.roll(cs.pixeldataIn,shift,axis=0).astype(float) dest = cs.pixeldataIn+mask*(medimage-cs.pixeldataIn) # repeat to remove propagated mask # new since 20150211 medimage = np.roll(dest,shift,axis=0).astype(float) dest = cs.pixeldataIn+mask*(medimage-cs.pixeldataIn) medimage = None cs.gridimage = mask.astype(float) mask = None # find gridobject gridobject = scind.binary_fill_holes(cs.gridimage) label_im,nb_labels = scind.label(gridobject) sizes = scind.sum(gridobject, label_im, range(nb_labels + 1)) gridobject = None #Clean up small connect components: mask_size = sizes < max(sizes) #(100/self.pixDim())**2 remove_pixel = mask_size[label_im] label_im[remove_pixel] = 0 # Now reassign labels with np.searchsorted: labels = np.unique(label_im) label_im = np.searchsorted(labels, label_im) medimage = np.roll(dest,shift,axis=0).astype(float) dest += cs.gridimage*(medimage-dest) medimage = None cs.gridimage *= label_im if -1>0: # remove everything outside grid wid = dest.shape[0] hei = dest.shape[1] mv = np.mean(dest[wid/4:3*wid/4,hei/4:3*hei/4]) dest = label_im*(dest-mv)+mv if removeGrid: cs.pixeldataIn = dest
def is_adjacent(array1, array2, borderWidth=2): """ decide if two patches are adjacent within border width """ p1d = ni.binary_dilation(array1, iterations=borderWidth - 1).astype(np.int8) p2d = ni.binary_dilation(array2, iterations=borderWidth - 1).astype(np.int8) if np.amax(p1d + p2d) > 1: return True else: return False
def sg_filter(s1, winsize1=15, winsize2=11): s1m = ni.median_filter(s1, 11) #s1m = s1 #winsize1 = 15 #winsize2 = 11 f1 = savgol_filter(s1m, winsize1, 3) f1_std = np.nanstd(s1-f1) if 0: # calculate weight f1_mask = np.abs(s1-f1) > 2.*f1_std f1_mask2 = ni.binary_opening(f1_mask, iterations=int(winsize2*0.2)) f1_mask3 = ni.binary_closing(f1_mask2, iterations=int(winsize2*0.2)) f1_mask4 = ni.binary_dilation(f1_mask3, iterations=int(winsize2)) weight = ni.gaussian_filter(f1_mask4.astype("d"), winsize2) else: fd2 = savgol_filter(s1m, winsize1, 3, deriv=2) fd2_std = np.std(fd2) f1_mask = np.abs(fd2) > 2.*fd2_std f1_mask = f1_mask | (s1m < s1m.max()*0.4) f1_mask4 = ni.binary_dilation(f1_mask, iterations=int(winsize2)) #f1_mask4[:300] = True #f1_mask4[-300:] = True weight = ni.gaussian_filter(f1_mask4.astype("d"), winsize2*.5) # find a region where deviation is significant if np.any(weight): weight/=weight.max() f2 = savgol_filter(s1m, winsize2, 5) f12 = f1*(1.-weight) + f2*weight else: f12 = f1 weight = np.zeros(f12.shape) if 0: ax1.cla() ax2.cla() ax1.plot(f12) ax2.plot(s1 - f1, color="0.5") ax2.plot(s1 - f12) ax2.plot(weight * f1_std*2) ax2.set_ylim(-0.02, 0.02) return f12, f1_std
def process_blob(cim): #cim = ndimage.binary_erosion(cim>0) for i in range(4): cim = ndimage.binary_erosion(cim>0) cim=ndimage.binary_dilation(cim>0) filterk = np.ones(Param.process_conv_size); cim = ndimage.convolve(cim, filterk, mode='constant', cval=0.0) for i in range(Param.num_dilation): cim = ndimage.binary_dilation(cim>0) for i in range(Param.num_erosion): cim = ndimage.binary_erosion(cim>0) return cim
def ClusterObjects(farn, struct_elem): magn_img = farn.magnitude_image dir_img = farn.direction_image bin_img = np.zeros(shape=(magn_img.shape[0], magn_img.shape[1]), dtype=np.uint8) bin_img[magn_img < 25] = 0 bin_img[magn_img >= 25] = 1 bin_img = ndimage.binary_dilation(bin_img, structure=struct_elem, iterations=3).astype(bin_img.dtype) labels, nb_labels = Morphology.ConnenctedComponents(bin_img) filt_labels, areas, nb_new_labels = Morphology.FilterArea(bin_img, labels, nb_labels, 480) temp_magn = ndimage.mean(magn_img, filt_labels, range(nb_new_labels + 1)) temp_dir = ndimage.mean(dir_img, filt_labels, range(nb_new_labels + 1)) data = np.concatenate((np.reshape(temp_magn, (-1,1)), np.reshape(temp_dir, (-1,1))), axis=1) clusters = -1 if nb_new_labels >= 1: Y = pdist(data, 'euclidean') agglo = AgglomerativeClustering.Agglomerative(Y, 50.) agglo.AggloClustering(criterion = 'distance', method = 'single', metric = 'euclidean', normalized = False) clusters = agglo.clusters bin_img[filt_labels == 0] = 0 bin_img[filt_labels >= 1] = 1 return bin_img, nb_new_labels, temp_magn, temp_dir, data, clusters
def bg_mask(in_file, in_mask, out_file=None): """ Rough mask of background from brain masks """ import nibabel as nb import numpy as np from scipy.ndimage import binary_dilation import scipy.ndimage as nd import os.path as op if out_file is None: fname, ext = op.splitext(op.basename(in_file)) if ext == ".gz": fname, ext2 = op.splitext(fname) ext = ext2 + ext out_file = op.abspath("%s_bgmask%s" % (fname, ext)) im = nb.load(in_file) hdr = im.get_header().copy() hdr.set_data_dtype(np.uint8) hdr.set_xyzt_units('mm') imdata = im.get_data() msk = nb.load(in_mask).get_data() msk = 1 - binary_dilation(msk, structure=np.ones((20, 20, 20))) nb.Nifti1Image(msk.astype(np.uint8), im.get_affine(), hdr).to_filename(out_file) return out_file
def remove_unresponsive_and_fluctuating_stripe(self, sinogram, snr, size): """ Algorithm 6 in the paper. Remove unresponsive or fluctuating stripes. --------- Parameters: - sinogram: 2D array. - snr: ratio used to discriminate between useful information and noise - size: window size of the median filter. --------- Return: - stripe-removed sinogram. """ (nrow, _) = sinogram.shape sinosmoothed = np.apply_along_axis(uniform_filter1d, 0, sinogram, 10) listdiff = np.sum(np.abs(sinogram - sinosmoothed), axis=0) nmean = np.mean(listdiff) listdiffbck = median_filter(listdiff, size) listdiffbck[listdiffbck == 0.0] = nmean listfact = listdiff / listdiffbck listmask = self.detect_stripe(listfact, snr) listmask = binary_dilation(listmask, iterations=1).astype(listmask.dtype) listmask[0:2] = 0.0 listmask[-2:] = 0.0 listx = np.where(listmask < 1.0)[0] listy = np.arange(nrow) matz = sinogram[:, listx] finter = interpolate.interp2d(listx, listy, matz, kind='linear') listxmiss = np.where(listmask > 0.0)[0] if len(listxmiss) > 0: matzmiss = finter(listxmiss, listy) sinogram[:, listxmiss] = matzmiss return sinogram
def remove_large_stripe(self, matindex, sinogram, snr, size): """ Algorithm 5 in the paper. Use to remove large stripes --------- Parameters: - sinogram: 2D array. - snr: ratio used to discriminate between useful information and noise. - size: window size of the median filter. --------- Return: - stripe-removed sinogram. """ badpixelratio = 0.05 (nrow, ncol) = sinogram.shape ndrop = np.int16(badpixelratio * nrow) sinosorted = np.sort(sinogram, axis=0) sinosmoothed = median_filter(sinosorted, (1, size)) list1 = np.mean(sinosorted[ndrop:nrow - ndrop], axis=0) list2 = np.mean(sinosmoothed[ndrop:nrow - ndrop], axis=0) listfact = list1 / list2 listmask = self.detect_stripe(listfact, snr) listmask = binary_dilation(listmask, iterations=1).astype(listmask.dtype) matfact = np.tile(listfact,(nrow,1)) sinogram = sinogram / matfact sinogram1 = np.transpose(sinogram) matcombine = np.asarray(np.dstack((matindex, sinogram1))) matsort = np.asarray( [row[row[:, 1].argsort()] for row in matcombine]) matsort[:, :, 1] = np.transpose(sinosmoothed) matsortback = np.asarray( [row[row[:, 0].argsort()] for row in matsort]) sino_corrected = np.transpose(matsortback[:, :, 1]) listxmiss = np.where(listmask > 0.0)[0] sinogram[:, listxmiss] = sino_corrected[:, listxmiss] return sinogram
def _rs_dead(sinogram, snr, size, matindex): """ Remove unresponsive and fluctuating stripes. """ sinogram = np.copy(sinogram) # Make it mutable (nrow, _) = sinogram.shape sinosmoothed = np.apply_along_axis(uniform_filter1d, 0, sinogram, 10) listdiff = np.sum(np.abs(sinogram - sinosmoothed), axis=0) nmean = np.mean(listdiff) listdiffbck = median_filter(listdiff, size) listdiffbck[listdiffbck == 0.0] = nmean listfact = listdiff / listdiffbck listmask = _detect_stripe(listfact, snr) listmask = binary_dilation(listmask, iterations=1).astype(listmask.dtype) listmask[0:2] = 0.0 listmask[-2:] = 0.0 listx = np.where(listmask < 1.0)[0] listy = np.arange(nrow) matz = sinogram[:, listx] finter = interpolate.interp2d(listx, listy, matz, kind='linear') listxmiss = np.where(listmask > 0.0)[0] if len(listxmiss) > 0: matzmiss = finter(listxmiss, listy) sinogram[:, listxmiss] = matzmiss # Use algorithm 5 to remove residual stripes sinogram = _rs_large(sinogram, snr, size, matindex) return sinogram
def step(self): """Perform a single step of the morphological snake evolution.""" # Assign attributes to local variables for convenience. u = self._u gI = self._data dgI = self._ddata theta = self._theta v = self._v if u is None: raise ValueError, "the levelset is not set (use set_levelset)" res = np.copy(u) # Balloon. if v > 0: aux = binary_dilation(u, self.structure) elif v < 0: aux = binary_erosion(u, self.structure) if v!= 0: res[self._threshold_mask_v] = aux[self._threshold_mask_v] # Image attachment. aux = np.zeros_like(res) dres = np.gradient(res) for el1, el2 in zip(dgI, dres): aux += el1*el2 res[aux > 0] = 1 res[aux < 0] = 0 # Smoothing. for i in xrange(self.smoothing): res = curvop(res) self._u = res
def InDecPatch(self,which,amount): s = ndimage.generate_binary_structure(2,1) # taxi-cab struct if which == 0: ras = ndimage.binary_dilation(self.cl_array,s,iterations=amount,border_value=0) else: ras = ndimage.binary_erosion(self.cl_array,s,iterations=amount,border_value=0) return(ras)
def _rs_large(sinogram, snr, size, matindex): """ Remove large stripes by: locating stripes, normalizing to remove full stripes, using the sorting technique to remove partial stripes. """ badpixelratio = 0.05 (nrow, ncol) = sinogram.shape ndrop = np.int16(badpixelratio * nrow) sinosorted = np.sort(sinogram, axis=0) sinosmoothed = median_filter(sinosorted, (1, size)) list1 = np.mean(sinosorted[ndrop:nrow - ndrop], axis=0) list2 = np.mean(sinosmoothed[ndrop:nrow - ndrop], axis=0) listfact = list1 / list2 # Locate stripes listmask = _detect_stripe(listfact, snr) listmask = binary_dilation(listmask, iterations=1).astype(listmask.dtype) matfact = np.tile(listfact, (nrow, 1)) # Normalize sinogram = sinogram / matfact sinogram1 = np.transpose(sinogram) matcombine = np.asarray(np.dstack((matindex, sinogram1))) matsort = np.asarray( [row[row[:, 1].argsort()] for row in matcombine]) matsort[:, :, 1] = np.transpose(sinosmoothed) matsortback = np.asarray( [row[row[:, 0].argsort()] for row in matsort]) sino_corrected = np.transpose(matsortback[:, :, 1]) listxmiss = np.where(listmask > 0.0)[0] sinogram[:, listxmiss] = sino_corrected[:, listxmiss] return sinogram
def get_objects_edges(objects): m = objects[0].get_mask() m[::] = False for o in objects: m += o.get_mask() md = ndimage.binary_dilation(m) return md - m
def multi_label_edge_detection(data): f = nd.generate_binary_structure(len(data.shape), 1) bound = (nd.grey_erosion(data,footprint=f) != nd.grey_dilation(data,footprint=f)) - \ (nd.binary_dilation(data.astype(np.bool)) - data.astype(np.bool)) # the unwanted thick bounds data=bound.astype(data.dtype) return data
def test_mask(): vol = np.zeros((30, 30, 30)) vol[15, 15, 15] = 1 struct = generate_binary_structure(3, 1) voln = binary_dilation(vol, structure=struct, iterations=4).astype('f4') initial = np.sum(voln > 0) mask = voln.copy() thresh = otsu(mask) mask = mask > thresh initial_otsu = np.sum(mask > 0) assert_equal(initial_otsu, initial) mins, maxs = bounding_box(mask) voln_crop = crop(mask, mins, maxs) initial_crop = np.sum(voln_crop > 0) assert_equal(initial_crop, initial) applymask(voln, mask) final = np.sum(voln > 0) assert_equal(final, initial) # Test multi_median. median_test = np.arange(25).reshape(5, 5) median_control = median_test.copy() medianradius = 3 median_test = multi_median(median_test, medianradius, 3) medarr = np.ones_like(median_control.shape) * ((medianradius * 2) + 1) median_filter(median_control, medarr, output=median_control) median_filter(median_control, medarr, output=median_control) median_filter(median_control, medarr, output=median_control) assert_equal(median_test, median_control)
def dilated_region(self, region, indpsf): '''Specify a optimization region that extends around the ``region``. This requires that the fitter has an attribute ``dilatation_region``, which can be - an int: In this case a square matrix of size 2 * n + 1 is used. - a matrix (see `scipy.ndimage.binary_dilation` for details. Examples -------- >>> from psfsubtraction.fitpsf import fitters >>> from psfsubtraction.fitpsf import optregion >>> region = np.array([[True, False, False], \ [False, False, False], \ [False, False, False]]) >>> class DilationFitter(fitters.SimpleSubtraction): ... optregion = optregion.dilated_region ... dilation_region = 1 >>> dummy_image = np.ones((3, 3)) # boring image, but good enough for the example >>> dummy_psfs = np.ones((3,3,4)) # even more boring psf array. >>> myfitter = DilationFitter(dummy_psfs, dummy_image) >>> myfitter.optregion(region, [0]).reshape((3, 3)) array([[ True, True, False], [ True, True, False], [False, False, False]]) ''' if not hasattr(self, 'dilation_region'): raise OptionalAttributeError('Fitter must speficy the `self.dilation_region`\n' + 'which is either and int or a square matrix.') if np.isscalar(self.dilation_region): selem = np.ones((2 * self.dilation_region + 1, 2 * self.dilation_region + 1)) else: selem = self.dilation_region return self.dim2to1(binary_dilation(self.dim1to2(region), selem))
def generate_scatter_volume(roi, prf, threshold, iterations): scatter = np.zeros_like(roi,dtype='double') # get indices xi,yi,zi = np.nonzero((roi>0) & (prf[...,-3]>threshold)) for voxel in range(len(xi)): # get index xind = xi[voxel] yind = yi[voxel] zind = zi[voxel] # set target to 1 mask = np.zeros_like(roi) mask[xind,yind,zind] = 1 # get neighborhood hood = ndimage.binary_dilation(mask,iterations=iterations) hood[xind,yind,zind] = 0 fit = prf[mask==1][0] fits = prf[hood==1] scatter[xind,yind,zind] = np.mean(np.sqrt((fits[:,0]-fit[np.newaxis,0])**2 + (fits[:,1]-fit[np.newaxis,1])**2))/2 return scatter
def process_frames(self, data): """ Algorithm 5 in the paper. Remove large stripes by: locating stripes, normalizing to remove full stripes, using the sorting technique to remove partial stripes. """ sinogram = np.copy(data[0]) badpixelratio = 0.05 # To avoid false detection ndrop = np.int16(badpixelratio * self.height1) sinosorted = np.sort(sinogram, axis=0) sinosmoothed = median_filter(sinosorted, (1, self.size)) list1 = np.mean(sinosorted[ndrop:self.height1 - ndrop], axis=0) list2 = np.mean(sinosmoothed[ndrop:self.height1 - ndrop], axis=0) listfact = list1 / list2 listmask = self.detect_stripe(listfact, self.snr) listmask = binary_dilation(listmask, iterations=1).astype(listmask.dtype) matfact = np.tile(listfact,(self.height1,1)) sinogram = sinogram / matfact sinogram1 = np.transpose(sinogram) matcombine = np.asarray(np.dstack((self.matindex, sinogram1))) matsort = np.asarray( [row[row[:, 1].argsort()] for row in matcombine]) matsort[:, :, 1] = np.transpose(sinosmoothed) matsortback = np.asarray( [row[row[:, 0].argsort()] for row in matsort]) sino_corrected = np.transpose(matsortback[:, :, 1]) listxmiss = np.where(listmask > 0.0)[0] sinogram[:, listxmiss] = sino_corrected[:, listxmiss] return sinogram
def connectedCompFeat(conn, firstBinClosing, secondDilatation, threshHold, max_size, RF_on_off, rawUint8): print "clearedImGauss.. " # Threshold rawBinary = np.where(rawUint8 > threshHold, 1, 0) # Remove small black particles binClosingVigra = vigra.filters.discClosing(rawBinary.astype(np.uint8), firstBinClosing) # change background tempColorRotate = (binClosingVigra.astype(np.uint8) -1) # remove connected Components smaller 90 000 pixels labels, count = ndimage.label(tempColorRotate.astype(np.bool)) sizes = np.bincount(labels.ravel()) mask_sizes = sizes > max_size mask_sizes[0]=0 print "---------------" clearedImage = mask_sizes[labels] # regrow areas clearedImage = ndimage.binary_dilation(clearedImage, iterations=secondDilatation) # clearedImageEr = vigra.filters.discErosion(clearedImage.astype(np.uint8), 2) # expand areas smoothely clearedImageGauss = vigra.filters.gaussianSmoothing(clearedImage.astype(np.float32), 7) if RF_on_off == True: clearedImageGauss = np.ravel(clearedImageGauss.T) clearedImageGauss = clearedImageGauss.reshape(clearedImageGauss.shape[0], 1) print "cleared Image Gauss fct: ", clearedImageGauss.shape conn.send(clearedImageGauss) conn.close()
def dilate(self, n_pixels=1): r""" Returns a copy of this :map:`MaskedImage` in which its mask has been expanded by n pixels along its boundary. Parameters ---------- n_pixels : int, optional The number of pixels by which we want to expand the mask along its own boundary. Returns ------- : :map:`MaskedImage` The copy of the masked image in which the mask has been expanded by n pixels along its boundary. """ global binary_dilation if binary_dilation is None: from scipy.ndimage import binary_dilation # expensive # Erode the edge of the mask in by one pixel dilated_mask = binary_dilation(self.mask.mask, iterations=n_pixels) image = self.copy() image.mask = BooleanImage(dilated_mask) return image
def _extrapolate_out_mask(data, mask, iterations=1): """ Extrapolate values outside of the mask. """ if iterations > 1: data, mask = _extrapolate_out_mask(data, mask, iterations=iterations - 1) new_mask = ndimage.binary_dilation(mask) larger_mask = np.zeros(np.array(mask.shape) + 2, dtype=np.bool) larger_mask[1:-1, 1:-1, 1:-1] = mask # Use nans as missing value: ugly masked_data = np.zeros(larger_mask.shape + data.shape[3:]) masked_data[1:-1, 1:-1, 1:-1] = data.copy() masked_data[np.logical_not(larger_mask)] = np.nan outer_shell = larger_mask.copy() outer_shell[1:-1, 1:-1, 1:-1] = np.logical_xor(new_mask, mask) outer_shell_x, outer_shell_y, outer_shell_z = np.where(outer_shell) extrapolation = list() for i, j, k in [(1, 0, 0), (-1, 0, 0), (0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1)]: this_x = outer_shell_x + i this_y = outer_shell_y + j this_z = outer_shell_z + k extrapolation.append(masked_data[this_x, this_y, this_z]) extrapolation = np.array(extrapolation) extrapolation = (np.nansum(extrapolation, axis=0) / np.sum(np.isfinite(extrapolation), axis=0)) extrapolation[np.logical_not(np.isfinite(extrapolation))] = 0 new_data = np.zeros_like(masked_data) new_data[outer_shell] = extrapolation new_data[larger_mask] = masked_data[larger_mask] return new_data[1:-1, 1:-1, 1:-1], new_mask
def dilatacion(Im, estructura): #est = np.zeros((3,3), dtype = np.int) #est[:,1] = 1 #est[1,:] = 1 Im = ndimage.binary_dilation(Im, estructura).astype('uint8') return Im
def calc_shift_direct(ft_1, ft_2, origin=0, debug_cross_cor=None): """ Does the actual fft cross correlation. Clean up - including cropping, thresholding, mask dilation. Performs n dimension gaussian fit and returns center. """ ft_1 = ndimage.fourier_gaussian(ft_1, 0.5) ft_2 = ndimage.fourier_gaussian(ft_2, 0.5) # module level for multiprocessing tmp = ft_1 * np.conj(ft_2) del ft_1, ft_2 cross_corr = np.abs(np.fft.ifftshift(np.fft.irfftn(tmp))) flat_dims = np.where(np.asarray(cross_corr.shape) == 1)[0] if len(flat_dims) > 0: cross_corr = cross_corr.squeeze() if cross_corr.sum() == 0: return origin * np.nan # threshold = np.ptp(cross_corr) * 0.5 + np.min(cross_corr) # cheat and striaght up crop out 3/4 of the image if it's large # i.e. drift not allow to span 1/4 the image width cropping = [slice(dim*6//16, -dim*6//16) if dim >= 16 else slice(None, None) for dim in cross_corr.shape] cross_corr_mask = np.zeros(cross_corr.shape) cross_corr_mask[cropping] = True # threshold = np.percentile(cross_corr[cropping], 95) threshold = np.ptp(cross_corr[cross_corr_mask.astype(bool)]) * 0.75 + np.min(cross_corr[cross_corr_mask.astype(bool)]) cross_corr_mask *= cross_corr > threshold # difficult to adjust for complete despeckling. slow? # cross_corr_mask = ndimage.binary_erosion(cross_corr_mask, structure=np.ones((1,)*cross_corr_mask.ndim), iterations=1, border_value=1, ) # cross_corr_mask = ndimage.binary_dilation(cross_corr_mask, structure=np.ones((3,)*cross_corr_mask.ndim), iterations=1, border_value=0, ) # print("mask {}".format(cross_corr_mask.sum())) labeled_image, labeled_counts = ndimage.label(cross_corr_mask) if labeled_counts > 1: max_index = np.argmax(ndimage.mean(cross_corr_mask, labeled_image, range(1, labeled_counts+1))) + 1 cross_corr_mask = labeled_image == max_index cross_corr_mask = ndimage.binary_dilation(cross_corr_mask, structure=np.ones((5,)*cross_corr_mask.ndim), iterations=1, border_value=0, ) cross_corr_thresholded = cross_corr * cross_corr_mask # if not debug_cross_cor is None: i, (path, dtype, shape) = debug_cross_cor cc_images = np.memmap(path, mode="r+", dtype=dtype, shape=shape) cross_corr_thresholded_repadded = cross_corr_thresholded.view() for d in flat_dims: cross_corr_thresholded_repadded = np.expand_dims(cross_corr_thresholded_repadded, d) short_axis = np.argmin(cross_corr_thresholded_repadded.shape) cc_images[i] = cross_corr_thresholded_repadded.mean(axis=short_axis) del cc_images dims = range(len(cross_corr.shape)) # crop out masked area bounds = np.zeros((len(dims), 2), dtype=np.int) for d in dims: dims_tmp = list(dims) dims_tmp.remove(d) mask_1d = np.any(cross_corr_mask, axis=tuple(dims_tmp)) bounds[d] = np.where(mask_1d)[0][[0, -1]] + [0, 1] cross_corr_thresholded = cross_corr_thresholded.take(np.arange(*bounds[d]), axis=d) # offset = np.zeros(len(dims)) # for d, length in enumerate(cross_corr_thresholded.shape): # dims_tmp = list(dims) # dims_tmp.remove(d) # offset[d] = np.sum(np.arange(length) * cross_corr_thresholded.sum(axis=tuple(dims_tmp))) # offset /= cross_corr_thresholded.sum() # offset += bounds[:, 0] cross_corr_thresholded[cross_corr_thresholded==0] = np.nan # cross_corr_thresholded -= np.nanmin(cross_corr_thresholded) cross_corr_thresholded /= np.nanmax(cross_corr_thresholded) # ### Gaussian fit ## p0 = [np.nanmax(cross_corr_thresholded), np.nanmin(cross_corr_thresholded)] # p0 = [1, 0] # grids = list() # for i, d in enumerate(cross_corr_thresholded.shape): # grids.append(np.arange(d)) # p0.extend([(d-1)*0.5, 0.5*d]) ## print grids ## print p0 # res = optimize.least_squares(guassian_nd_error, p0, args=(grids, cross_corr_thresholded)) ## print res.x ### Rbf peak finding p0 = [] grids = list() for i, d in enumerate(cross_corr_thresholded.shape): grids.append(np.arange(d)) p0.append(0.5*d) rbf_interpolator = build_rbf(grids, cross_corr_thresholded) res = optimize.minimize(rbf_nd_error, p0, args=rbf_interpolator) offset = list() for i in np.arange(len(cross_corr_thresholded.shape)): # offset.append(res.x[2*i+2]) offset.append(res.x[i]) offset += bounds[:, 0] # print offset if len(flat_dims) > 0: offset = np.insert(offset, flat_dims, 0) return offset - origin
def main(pred_file, result_folder='.', **kwargs): assert os.path.exists(pred_file), \ 'Prediction file {} does not exists. Please check!'.format(pred_file) sample = os.path.basename(pred_file).split('.')[0] if sample == 'GMR_38F04_AE_01-20181005_63_G5': return if sample == 'BJD_127B01_AE_01-20171124_64_H5': return result_file = os.path.join(result_folder, sample + '.zarr') aff_key = kwargs['aff_key'] fg_key = kwargs.get('fg_key') # read input shape if pred_file.endswith('.zarr'): in_f = zarr.open(pred_file, mode='r') else: raise NotImplementedError aff_shape = in_f[aff_key].shape channel_order = [slice(0, aff_shape[0])] pred_keys = [aff_key] if kwargs['overlapping_inst']: numinst_shape = in_f[fg_key].shape channel_order.append(slice(0, numinst_shape[0])) pred_keys += [fg_key] assert aff_shape[1:] == numinst_shape[1:], \ 'Please check: affinity and numinst shape do not match!' input_shape = aff_shape[1:] # without first channel dimension # check if blocks should only be within bounding box if kwargs.get('only_bb'): mid = np.prod(kwargs['patchshape']) // 2 mask = np.array(in_f[aff_key][mid]) mask = mask > kwargs['patch_threshold'] if np.sum(mask) == 0: logger.warning("bb empty") return if kwargs.get('ignore_small_comps', 0) > 0: labeled = ndimage.label(mask, np.ones([3] * len(input_shape)))[0] labels, counts = np.unique(labeled, return_counts=True) labels = labels[counts <= kwargs.get('ignore_small_comps')] labeled = replace(labeled, np.array(labels), np.array([0] * len(labels))) print('num small comps: ', len(labels)) # for label in labels: # mask[labeled == label] = 0 mask = labeled > 0 min = np.min(np.transpose(np.nonzero(mask)), axis=0) max = np.max(np.transpose(np.nonzero(mask)), axis=0) # TODO for l1 data # min = np.array([31, 31, 0]) # max = np.array([input_shape[0]-30, input_shape[1]-30, input_shape[2]]) shape = max - min + 1 bb_offset = min else: shape = input_shape bb_offset = [0] * len(shape) if len(shape) == 2: shape = (1, ) + tuple(shape) bb_offset = [0] * len(shape) logger.info("input shape: %s, bb cropped shape: %s, offset: %s", input_shape, shape, bb_offset) if pred_file.endswith('.hdf'): in_f.close() # create offset lists offsets = get_chessboard_offsets(shape, kwargs['chunksize']) #offsets = [offset + bb_offset for offset in offsets] logger.info('num blocks: %s', len(offsets)) logger.info("%s", offsets) # create temporary zarr dataset for blocks (2^dim x shape) tmp_key = 'volumes/tmp_worker' skip_tmp_worker = False if not os.path.exists(result_file): compressor = Blosc(cname='zstd', clevel=3, shuffle=Blosc.BITSHUFFLE) f = zarr.open(result_file, mode='w') f.create_dataset(tmp_key, shape=(2**len(shape), ) + tuple(shape), compressor=compressor, dtype=np.uint32, chunks=(1, ) + tuple(kwargs['chunksize'])) else: f = zarr.open(result_file, mode='r') if tmp_key in f: skip_tmp_worker = True def init(l): global mutex mutex = l if not skip_tmp_worker: mutex = Lock() if kwargs['num_parallel_blocks'] > 1: pool = Pool(processes=kwargs['num_parallel_blocks'], initializer=init, initargs=(mutex, )) pool.map( functools.partial(blockwise_vote_instances, pred_file, pred_keys, result_file, tmp_key, shape, channel_order, bb_offset, kwargs), offsets) pool.close() pool.join() else: kwargs['mutex'] = mutex for idx, offset in enumerate(offsets): # if idx < 7 or idx > 8: # continue logger.info("start block idx: %s/%s (file %s)", idx, len(offsets), sample) blockwise_vote_instances(pred_file, pred_keys, result_file, tmp_key, shape, channel_order, bb_offset, kwargs, offset) else: logger.info("skipping tmp_worker (blocks already exist?)") # stitch blocks res_key = kwargs.get('res_key', 'vote_instances') logger.info("%s", kwargs) instances = stitch_vote_instances(result_file, tmp_key, res_key, input_shape, bb_offset, **kwargs) # save mip save_mip = kwargs.get('save_mip', False) if save_mip: colored = color(np.max(instances, axis=0)) io.imsave(os.path.join(result_folder, sample + '.png'), colored.astype(np.uint8)) # remove small components remove_small_comps = kwargs.get('remove_small_comps', 0) if remove_small_comps > 0: instances = remove_small_components(instances, remove_small_comps) instances = relabel(instances) io.imsave(os.path.join(result_folder, sample + '.tif'), instances.astype(np.uint16), plugin='tifffile') if save_mip: colored = color(np.max(instances, axis=0)) io.imsave(os.path.join(result_folder, sample + '_cleaned.png'), colored.astype(np.uint8)) if kwargs['output_format'] == 'hdf': hf = h5py.File(os.path.join(result_folder, sample + '.hdf'), 'w') hf.create_dataset(res_key, data=instances.astype(np.uint16), dtype=np.uint16, compression='gzip') if kwargs.get("dilate_instances", False): logger.info("dilating") instdil = np.copy(instances) for lbl in np.unique(instances): if lbl == 0: continue label_mask = instdil == lbl dilated_label_mask = ndimage.binary_dilation(label_mask, iterations=1) instdil[dilated_label_mask] = lbl hf.create_dataset(res_key + "_dil_1", data=instdil.astype(np.uint16), dtype=np.uint16, compression='gzip')
str(power_array[-1])) #Calculate flexure difference using cross-correlation chisq_flexure = np.zeros( n_flexure) #Define array to store the chisq for the flexure for order in range( skip_first_orders, n_orders - skip_last_orders ): #Loop through all orders in specified order range sci_order = data_sci[ order, :] #Grab subset of science and A0V data for this order a0v_flattened_order = data_a0v_flattened[order, :] smoothed_sci_order = normalize_continuum( sci_order, size=75 ) #Contiunuum normalize the science data to place the telluric lines in it on the same flux scale as the flattened A0V data cuts = np.isfinite(a0v_flattened_order) & np.isfinite( sci_order) & binary_dilation( (a0v_flattened_order > min_a0v) & (a0v_flattened_order < max_a0v) ) & x_cuts #Set cuts for which pixels to use if np.any( cuts == True ): #Error catch to ignore orders with no useful telluric lines for i in range( n_flexure ): #Loop to check all flexure values for this order and iteration chisq_flexure[i] += bn.nansum( (srebin(x, x + flexure_array[i], a0v_flattened_order)[cuts]**best_fit_power - smoothed_sci_order[cuts])**2 ) #Calculate chisq for flexure using the difference between the A0V and a continuum normalized science spectrum best_fit_flexure = flexure_array[chisq_flexure == bn.nanmin( chisq_flexure)][0] #Calculate airmass (powerlaw) difference using cross-correlation
def prepareImage(img, cimg, returnMaxDims=False): BrightImg = cimg NormalizedImg = rgb2gray(img) seed = np.copy(NormalizedImg) seed[1:-1, 1:-1] = NormalizedImg.min() mask = NormalizedImg fill = -skimage.morphology.reconstruction(seed, mask, method='dilation') #fig = plt.figure() #plt.imshow(fill,cmap = plt.get_cmap('gray')) seed2 = np.copy(fill) seed2[1:-1, 1:-1] = fill.min() mask = fill dilated = skimage.morphology.reconstruction(seed2, mask, method='dilation') fill = fill - dilated thresh = skimage.filters.threshold_otsu(fill) fill = fill > thresh #fill = skimage.feature.canny(fill,sigma = 2.5, low_threshold=10, high_threshold=50) fill = image.binary_dilation(fill, structure=np.ones((3, 3))) fill = image.binary_fill_holes(fill) fill = image.binary_erosion(fill, structure=np.ones((7, 7))) #fig = plt.figure() #plt.imshow(fill,cmap = plt.get_cmap('gray')) distance = image.distance_transform_edt(fill) distance = image.gaussian_filter(distance, 1.1) local_maxi = peak_local_max(distance, indices=False, footprint=np.ones((7, 7)), labels=fill) markers = image.label(local_maxi)[0] labels = watershed(-distance, markers, mask=fill) label_image = skimage.measure.label(labels) #print(np.unique(labels)) TempCoins = np.zeros( (len(np.unique(labels)), 100, 100, 3)).astype(np.uint8) counter = 0 for region in skimage.measure.regionprops(label_image): xLen = np.abs(-min(region.coords[:, 0]) + max(region.coords[:, 0])) yLen = np.abs(-min(region.coords[:, 1]) + max(region.coords[:, 1])) if (region.area >= 900 and xLen <= 100 and yLen <= 100 and xLen / yLen < 2 and xLen / yLen > 1 / 2): #mySlice = (slice(min(region.coords[:,0]) , max(region.coords[:,0]) ), # slice(min(region.coords[:,1]) , max(region.coords[:,1]) ) ) #TempCoins[counter, int((100-xLen)/2) : int((100-xLen)/2) + xLen # , int((100-yLen)/2): int((100-yLen)/2) + yLen,:] = BrightImg[mySlice] if (xLen > yLen): currentImg = BrightImg[ min(region.coords[:, 0]):max(region.coords[:, 0]), min(region.coords[:, 1]):(min(region.coords[:, 1]) + xLen)] else: currentImg = BrightImg[ min(region.coords[:, 0]):(min(region.coords[:, 0]) + yLen), min(region.coords[:, 1]):max(region.coords[:, 1])] TempCoins[counter] = imresize(currentImg, size=(100, 100), interp='bicubic') #fig= plt.figure() #plt.imshow(TempCoins[counter]) counter = counter + 1 Coins = TempCoins[:counter, :, :, :] #Debugging - Midway Images ''' fig = plt.figure() plt.imshow(cimg) fig = plt.figure() plt.imshow(fill,cmap = plt.get_cmap('gray')) fig = plt.figure() plt.imshow(labels,cmap = plt.get_cmap('jet')) ''' #if returnMaxDims: # return maxDims return Coins
def windowed_radon(img, radon_matrix, theta=None, method='h_maxima', threshold_rel=2, threshold_rel_global=False, threshold_mean=1.5, return_lines=False, debug=False): """ Get coarse-grained anisotropy tensor for img via windowed Radon transform. Basic version with no support for curved surfaces, and only one option for maxima detection, to make code easier to read. See https://doi.org/10.7554/eLife.27454. This function first calculates a windowed Radon transform, then detects maxima of the Radon transform and finally computes a coarse-grained anisotropy tensor for each Radon patch according to tensorify_radon. Alternatively, the function can also return the centroid, orientation and intensity of each detected edge segment (for verification purposes). This information is returned in a 2d-array like nested list format, each entry corresponding to the list of detected line segments from one particular Radon window. By default, the Radon transform is weighted so a to calculate the line density along each ray (this is done approximately). The crucial step in anisotropy detection is finding the maxima of the Radon transform. This is implemented using the h-maxima transform. It is necessary to tune the "h"-parameter of the h-maxima transform and check that your choice correctly detects all edge segment of interest. As an example of why maxima detection is so important, consider the Radon transform of an image containing a single line, and average over the projection distance delta, (so as to obtain a function of angle only). The result is _independent_ of the original line orientation! For performance reasons, this function uses a sparse matrix representation of the Radon transform, computed in advance using get_radon_tf_matrix. Parameters ---------- img : np.array Input image. radon_matrix : sparse.csc_matrix Sparse matrix representing the Radon transform (remember, it's linear). Computed by get_radon_tf_matrix. Determines the size of the radon window. theta : np.array, optional Angles at which radon transform is computed. Defaults to np.linspace(0, np.pi, edge_length, endpoint=False) where edge_length is the window size (defined by the shape of radon_matrix). method : str, optional Either 'h_maxima' or 'global_maximum' (faster). threshold_rel : float, optional Threshold for maxima detection in radon transform. For "h_maxima", this is the minimal height of maxima in units of the Radon transform's standard deviation. For "peak_local_max", it is the minimal height of maxima in multiples of the global maximum. threshold_mean : float, optional 2nd auxilliary threshold. All Radon transform maxima must be at least threshold_mean * mean(Radon transform). This removes e.g. the global maximum always returned by the h maxima transform if it is not sufficiently pronounced. threshold_rel_global : float, optional Global threshold for h-maxima computation, in units of image std deviation. If non-zero, overrules the local h-maxima thresholds. Use when large regions of image contain no anisotropy. return_lines : bool, optional Whether to also return a list of centroids, orientations & intensities of all detected edges instead of just the coarse-grained anisotropy tensor. Returns ------- lines : list of lists of np.arrays of shape (..., 4) Returned only if return_lines is True. Lines detected in each window, formatted as follows (x_coord, y_coord, m : np.array of shape (...,..., 2, 2) Coarse grained anisotropy tensor. The first two axes are "spatial" indices and have the following extent: ceil(2*(img_shape - edge_length+1)/(edge_length+1)), where edge_length is the radon window size. """ edge_length = np.sqrt(radon_matrix.shape[1]).astype(int) e_len = int((edge_length - 1) / 2) if theta is None: theta = np.linspace(0, np.pi, int(radon_matrix.shape[0] / (edge_length - 4)), endpoint=False) e_len_cut = int((radon_matrix.shape[0] / theta.size - 1) / 2) delta = np.arange(-e_len_cut, e_len_cut + 1) # needed because line_density radon transform returns cut-off # director matrix q_matrix = np.array([[np.sin(theta)**2, -np.sin(theta) * np.cos(theta)], [-np.sin(theta) * np.cos(theta), np.cos(theta)**2]]) global_h = threshold_rel_global * img.std() # iterate over sub-arrays m = [] lines = [] for r in np.arange(e_len, img.shape[0] - e_len, e_len + 1): m_row = [] lines_row = [] for c in np.arange(e_len, img.shape[1] - e_len, e_len + 1): patch = img[r - e_len:r + e_len + 1, c - e_len:c + e_len + 1] radon_window = radon_matrix.dot(patch.flatten()) radon_window = radon_window.reshape(2 * e_len_cut + 1, theta.size) # compute mean based threshold for maxima thr_mean = (radon_window.min() + threshold_mean * (np.median(radon_window) - radon_window.min())) if method == 'h_maxima': h = (global_h if global_h else radon_window.std() * threshold_rel + 1e-2) max_mask = h_maxima(radon_window, h) max_mask = binary_dilation(max_mask, iterations=1) max_mask *= (radon_window > thr_mean) if method == 'global_maximum': max_mask = np.zeros(radon_window.shape) ind = np.unravel_index(np.argmax(radon_window, axis=None), radon_window.shape) max_mask[ind] = radon_window[ind] > thr_mean if method == 'thr_mean': max_mask = radon_window > thr_mean if return_lines and max_mask.any(): max_mask = shrink_to_centroid(max_mask) # now, get centers of lines for each maximum max_intensity = radon_window[max_mask.astype(bool)] max_delta = delta[np.where(max_mask)[0]] max_theta = theta[np.where(max_mask)[1]] # get distance of line segment centroids to image center, # component orthogonal to line orientation max_center = max_delta * np.stack( [np.cos(max_theta), -np.sin(max_theta)]) # component parallel to line orientation par_com = np.array([ get_segment_com(patch, ang, off, pad=2) for ang, off in zip(max_theta, max_delta) ]) # add to coordinates of radon window center max_center += par_com * np.stack( [np.sin(max_theta), np.cos(max_theta)]) max_loc = max_center.T + np.array([c, r]) lines_row.append(list(zip(max_intensity, max_theta, max_loc))) else: lines_row.append([]) if debug: print(r, c) for l in lines_row[-1]: print('theta', 180 / np.pi * l[1]) print('loc', l[2]) a, b = np.sin(l[1]), np.cos(l[1]) plt.plot(5 * np.array([-a, 0, a]) + l[2][0] + e_len - c, 5 * np.array([-b, 0, b]) + l[2][1] + e_len - r, color='red', lw=1) plt.imshow(patch * disk(e_len), vmin=0, vmax=1) plt.show() radon_window = (radon_window * max_mask).sum(axis=0) m_row.append((q_matrix * radon_window).sum(axis=2)) lines.append(lines_row) m.append(m_row) m = np.stack(m) if return_lines: return lines, m return m
bin_p_values = (log_p_values != 0) vt = nibabel.load(haxby_files.mask_vt[0]).get_data().astype(bool) bin_p_values_and_vt = np.logical_and(bin_p_values, vt) fig_id = plt.subplot(3, 1, 2) plot_roi(nibabel.Nifti1Image(bin_p_values_and_vt.astype(np.int), fmri_img.get_affine()), mean_img, title='Intersection with ventral temporal mask', cut_coords=(coronal, sagittal, axial), axes=fig_id) # Dilation fig_id = plt.subplot(3, 1, 3) from scipy import ndimage dil_bin_p_values_and_vt = ndimage.binary_dilation(bin_p_values_and_vt) plot_roi(nibabel.Nifti1Image(dil_bin_p_values_and_vt.astype(np.int), fmri_img.get_affine()), mean_img, title='Dilated mask', cut_coords=(coronal, sagittal, axial), axes=fig_id, annotate=False) plt.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0) # Identification of connected components plt.figure() labels, n_labels = ndimage.label(dil_bin_p_values_and_vt) first_roi_data = (labels == 1).astype(np.int) second_roi_data = (labels == 2).astype(np.int) fig_id = plt.subplot(2, 1, 1)
def generateTestCatalog(config, numSourcesPerTile, amplitudeColumnName = 'fixed_y_c', amplitudeRange = [0.001, 1], amplitudeDistribution = 'linear', selFn = None, avoidanceRadiusArcmin = 20.0, maskDilationPix = 0): """Generate a catalog of objects with random positions and amplitudes. This is for testing purposes - see, e.g., :meth:`nemo.maps.sourceInjectionTest`. Args: config (:obj:`nemo.startup.NemoConfig`): Nemo configuration object. numSourcesPerTile (:obj:`int`): The maximum number of sources to insert into each tile. The number of sources actually inserted may be less than this depending on the value of `avoidanceRadiusArcmin`. amplitudeColumnName (:obj:`str`): Name of the column in the output catalog in which source (or cluster) amplitudes will be stored. Typically this should be "deltaT_c" for sources, and "fixed_y_c" for clusters. amplitudeRange (:obj:`list`): Range for the random amplitudes, in the form [minimum, maximum]. amplitudeDistribution (:obj:`str`): Either 'linear' or 'log'. selFn (:obj:`nemo.completeness.SelFn`, optional): Nemo selection function object, used to access area masks and coordinates info. If not given, a selFn object will be created using the info in the config. Providing this saves time, as the area mask files don't have to be read from disk. avoidanceRadiusArcmin (:obj:`float`): Minimum separation between two objects in the output catalog. This should be set large enough to avoid crowding and spurious cross-matching problems. maskDilationPix (:obj:`int`): Avoid placing objects within this many pixels of the area mask, by dilating the holes in the mask by this number of pixels. Avoids problems with e.g., objects split across the mask boundary. If not set, gives a small number of objects with larger than expected offsets in recovered position in :meth:`nemo.maps.sourceInjectionTest`). Returns: An :obj:`astropy.table.Table` object containing the catalog. """ if selFn is None: selFn=completeness.SelFn(config.selFnDir, 4.0, configFileName = config.configFileName, enableCompletenessCalc = False, setUpAreaMask = True) RAs=[] decs=[] amps=[] for tileName in config.tileNames: mapData=selFn.areaMaskDict[tileName] if mapData.sum() == 0: # Skip any empty/blank tile continue wcs=selFn.WCSDict[tileName] # Dilate mask to avoid putting objects close to borders or holes for i in range(maskDilationPix): mapData=1-ndimage.binary_dilation(1-mapData) ys, xs=np.where(mapData != 0) ys=ys+np.random.uniform(0, 1, len(ys)) xs=xs+np.random.uniform(0, 1, len(xs)) indices=np.random.randint(0, len(ys), len(ys)) coords=wcs.pix2wcs(xs[indices], ys[indices]) coords=np.array(coords) tileRAs=[] tileDecs=[] keepIndices=[] for i in indices: rai=coords[i, 0] deci=coords[i, 1] rDeg=astCoords.calcAngSepDeg(rai, deci, tileRAs, tileDecs) keepObj=False if len(rDeg) > 0: if rDeg.min()*60 > avoidanceRadiusArcmin: keepObj=True else: keepObj=True if keepObj == True: tileRAs.append(rai) tileDecs.append(deci) keepIndices.append(i) if len(keepIndices) == numSourcesPerTile: break RAs=RAs+coords[keepIndices, 0].tolist() decs=decs+coords[keepIndices, 1].tolist() if amplitudeDistribution == 'linear': amp=np.random.uniform(amplitudeRange[0], amplitudeRange[1], len(keepIndices)) elif amplitudeDistribution == 'log': amp=np.power(10, np.random.uniform(np.log10(amplitudeRange)[0], np.log10(amplitudeRange)[1], len(keepIndices))) else: raise Exception("Must be either 'linear' or 'log'.") amps=amps+amp.tolist() tab=atpy.Table() tab.add_column(atpy.Column(np.arange(0, len(amps))+1, "name")) tab.add_column(atpy.Column(RAs, "RADeg")) tab.add_column(atpy.Column(decs, "decDeg")) tab.add_column(atpy.Column(amps, amplitudeColumnName)) return tab
def preprocess_asegs(aseg_dir, lesion_gt_dir, list_incorrect, list_correct, lesion_label_in_gt=77, dilate=2, recompute=False): # align asegs to gt dir (cropping to same dimension) cropped_dir = aseg_dir + '_cropped' edit_volumes.mri_convert_images_in_dir(aseg_dir, cropped_dir, interpolation='nearest', reference_dir=lesion_gt_dir, recompute=recompute) # correct for aseg labels corrected_dir = cropped_dir + '_corrected' edit_volumes.correct_labels_in_dir(cropped_dir, list_incorrect, list_correct, corrected_dir, smooth=False, recompute=recompute) # list gt and aseg, and create result dir list_lesion_labels = utils.list_images_in_folder(lesion_gt_dir) list_aseg_labels = utils.list_images_in_folder(corrected_dir) inpainted_dir = corrected_dir + '_lesion_inpainted' utils.mkdir(inpainted_dir) # loop over subjects for path_lesion_label, path_aseg_label in zip(list_lesion_labels, list_aseg_labels): path_result = os.path.join(inpainted_dir, os.path.basename(path_aseg_label)) if (not os.path.isfile(path_result)) | recompute: # paste lesion label lesions = utils.load_volume(path_lesion_label) aseg_label, aff, h = utils.load_volume(path_aseg_label, im_only=False) lesion_mask = lesions == lesion_label_in_gt aseg_label[lesion_mask] = 77 utils.save_volume(aseg_label, aff, h, path_result) # dilate lesion and ventricle dilated_dir = inpainted_dir + '_dilated' utils.mkdir(dilated_dir) list_inpainted_aseg = utils.list_images_in_folder(inpainted_dir) for path_aseg in list_inpainted_aseg: path_result = os.path.join(dilated_dir, os.path.basename(path_aseg)) if (not os.path.isfile(path_result)) | recompute: # define lesion, WM, and LV masks aseg, aff, h = utils.load_volume(path_aseg, im_only=False) WM = aseg == 2 LV = aseg == 4 lesion = aseg == 77 # morphological operations to bridge the gaps between lesions and LV morph_struct = utils.build_binary_structure( dilate, len(aseg.shape)) dilated_LV_or_lesion = binary_dilation(LV | lesion, morph_struct) filled_LV_or_lesion = binary_erosion(dilated_LV_or_lesion, morph_struct) LV = LV | (filled_LV_or_lesion & WM) aseg[LV] = 4 # save map utils.save_volume(aseg, aff, h, path_result)
def test_02_06_binary_dilate(self): self.binary_tteesstt( 'dilate', lambda x, footprint: scind.binary_dilation(x, footprint), scale=7)
def median_otsu(input_volume, median_radius=4, numpass=4, autocrop=False, vol_idx=None, dilate=None): """ Simple brain extraction tool method for images from DWI data It uses a median filter smoothing of the input_volumes `vol_idx` and an automatic histogram Otsu thresholding technique, hence the name *median_otsu*. This function is inspired from Mrtrix's bet which has default values ``median_radius=3``, ``numpass=2``. However, from tests on multiple 1.5T and 3T data from GE, Philips, Siemens, the most robust choice is ``median_radius=4``, ``numpass=4``. Parameters ---------- input_volume : ndarray ndarray of the brain volume median_radius : int Radius (in voxels) of the applied median filter(default 4) numpass: int Number of pass of the median filter (default 4) autocrop: bool, optional if True, the masked input_volume will also be cropped using the bounding box defined by the masked data. Should be on if DWI is upsampled to 1x1x1 resolution. (default False) vol_idx : None or array, optional 1D array representing indices of ``axis=3`` of a 4D `input_volume` None (the default) corresponds to ``(0,)`` (assumes first volume in 4D array) dilate : None or int, optional number of iterations for binary dilation Returns ------- maskedvolume : ndarray Masked input_volume mask : 3D ndarray The binary brain mask """ if len(input_volume.shape) == 4: if vol_idx is not None: b0vol = np.mean(input_volume[..., tuple(vol_idx)], axis=3) else: b0vol = input_volume[..., 0].copy() else: b0vol = input_volume.copy() # Make a mask using a multiple pass median filter and histogram thresholding. mask = multi_median(b0vol, median_radius, numpass) thresh = otsu(mask) mask = mask > thresh if dilate is not None: cross = generate_binary_structure(3, 1) mask = binary_dilation(mask, cross, iterations=dilate) # Auto crop the volumes using the mask as input_volume for bounding box computing. if autocrop: mins, maxs = bounding_box(mask) mask = crop(mask, mins, maxs) croppedvolume = crop(input_volume, mins, maxs) maskedvolume = applymask(croppedvolume, mask) else: maskedvolume = applymask(input_volume, mask) return maskedvolume, mask
method_id = [m for m in mlist for s in slist for rdx in range(nrun)] # Resample images to geometry of template and rescale TSNR value with SQRT(NVol) imgs = [] for i, e in enumerate(method_id): if 'spm' in e: img = resample_to_img(filelist[i], template) else: img = load_img(filelist[i]) imgs.append(math_img('img * np.sqrt(%d)' % nvol, img=img)) # Create mask (containing only voxels with values in at least half of the images) img_concat = concat_imgs(imgs) mask = np.sum(img_concat.get_data()!=0, axis=-1)>=(img_concat.shape[-1] * 0.8) mask = binary_fill_holes( binary_dilation(binary_erosion(mask, iterations=2), iterations=2)) group_mask = new_img_like(img_concat, mask.astype('int'), copy_header=True) # Create 2nd-level model design_matrix = design_matrix = pd.get_dummies(method_id) second_level_model = SecondLevelModel(n_jobs=-1, mask=group_mask) second_level_model = second_level_model.fit(imgs, design_matrix=design_matrix) # Compute contrasts, save nifti and plot glass brain weights = [ [1, 0, 0, 0, 0], [1,-1, 0, 0, 0], [1, 0,-1, 0, 0], [1, 0, 0,-1, 0], [1, 0, 0, 0,-1], [-1, 1, 0, 0, 0], [0, 1, 0, 0, 0], [0, 1,-1, 0, 0], [0, 1, 0,-1, 0], [0, 1, 0, 0,-1], [-1, 0, 1, 0, 0], [0,-1, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1,-1, 0], [0, 0, 1, 0,-1], [-1, 0, 0, 1, 0], [0,-1, 0, 1, 0], [0, 0,-1, 1, 0], [0, 0, 0, 1, 0], [0, 0, 0, 1,-1], [-1, 0, 0, 0, 1], [0,-1, 0, 0, 1], [0, 0,-1, 0, 1], [0, 0, 0,-1, 1], [0, 0, 0, 0, 1]] for i, w in enumerate(weights):
def maskCapsomer( capsomer, correction_mask, vector, capsomer_centers ): capsomer_mask = np.ones_like( capsomer ) #equidistance_mask = np.zeros_like( capsomer, dtype=np.bool ) nhalf = len(capsomer) // 2 x0, y0, z0 = np.meshgrid(*[np.arange(-nhalf - 0.5, nhalf - 0.5)] * 3, indexing="ij") x = x0 y = -1*y0 z = -1*z0 print("Preparing to create mask for current subvolume") gold_distance = np.sqrt((x - vector[0])**2 + (y-vector[1])**2 + (z-vector[2])**2) for index, center in enumerate(capsomer_centers): """ Don't compare this subvolume center against itself """ if not np.array_equal(vector,center.astype(int)): """ This makes the evaluator """ x0, y0, z0 = np.meshgrid(*[np.arange(-nhalf - 0.5, nhalf - 0.5)] * 3, indexing="ij") x = x0 y = (-1*y0) z = (-1*z0) """ A volume containing distances to the currently evaluated center """ dist_from_center = np.sqrt((x - center[0])**2 + (y-center[1])**2 + (z-center[2])**2) """ Update overall mask """ mask = gold_distance <= dist_from_center #mask_edge = gold_distance == dist_from_center #equidistance_mask = equidistance_mask + mask_edge capsomer_mask = capsomer_mask * mask.astype(np.int) #print( np.unique(equidistance_mask) ) """ At this point, capsomer_mask contains all voxels belonging to the given capsomer """ """ In the case of equidistance, those voxels are overrepresented """ """ We now want to dilate and soften that mask to reduce nyquist artifacts """ soft_1 = ndimage.binary_dilation(capsomer_mask, iterations=1) - capsomer_mask soft_2 = ndimage.binary_dilation(capsomer_mask, iterations=2) - (capsomer_mask + soft_1) soft_3 = ndimage.binary_dilation(capsomer_mask, iterations=3) - (capsomer_mask + soft_1 + soft_2) soft_1 = np.cos((1*np.pi)/8) * soft_1.astype(np.float32) soft_2 = np.cos((2*np.pi)/8) * soft_2.astype(np.float32) soft_3 = np.cos((3*np.pi)/8) * soft_3.astype(np.float32) """ Add the soft mask to the capsomer mask """ capsomer_mask = capsomer_mask.astype(np.float32) + soft_1 + soft_2 + soft_3 #print( "Unique values in capsomer mask", np.unique(capsomer_mask) ) """ Only take the capsomer_mask where capsomer map has values """ capsomer_bool = (capsomer!=0).astype(np.int) capsomer_mask = capsomer_mask * capsomer_bool """ Multiply by mask """ capsomer = capsomer * capsomer_mask.astype(np.float32) """ Find intersection of equidistance_mask and capsomer_mask """ #equidistance_mask = equidistance_mask * capsomer_mask.astype(np.bool) """ Update the correction_mask """ correction_mask = correction_mask + capsomer_mask.astype(np.float32) #print( "Unique values in correction mask", np.unique(correction_mask) ) return capsomer, correction_mask
def run(self, workspace): '''Run the module on the image set''' seed_objects_name = self.seed_objects_name.value skeleton_name = self.image_name.value seed_objects = workspace.object_set.get_objects(seed_objects_name) labels = seed_objects.segmented labels_count = np.max(labels) label_range = np.arange(labels_count, dtype=np.int32) + 1 skeleton_image = workspace.image_set.get_image(skeleton_name, must_be_binary=True) skeleton = skeleton_image.pixel_data if skeleton_image.has_mask: skeleton = skeleton & skeleton_image.mask try: labels = skeleton_image.crop_image_similarly(labels) except: labels, m1 = cpo.size_similarly(skeleton, labels) labels[~m1] = 0 # # The following code makes a ring around the seed objects with # the skeleton trunks sticking out of it. # # Create a new skeleton with holes at the seed objects # First combine the seed objects with the skeleton so # that the skeleton trunks come out of the seed objects. # # Erode the labels once so that all of the trunk branchpoints # will be within the labels # # # Dilate the objects, then subtract them to make a ring # my_disk = morph.strel_disk(1.5).astype(int) dilated_labels = grey_dilation(labels, footprint=my_disk) seed_mask = dilated_labels > 0 combined_skel = skeleton | seed_mask closed_labels = grey_erosion(dilated_labels, footprint=my_disk) seed_center = closed_labels > 0 combined_skel = combined_skel & (~seed_center) # # Fill in single holes (but not a one-pixel hole made by # a one-pixel image) # if self.wants_to_fill_holes: def size_fn(area, is_object): return (~is_object) and (area <= self.maximum_hole_size.value) combined_skel = morph.fill_labeled_holes(combined_skel, ~seed_center, size_fn) # # Reskeletonize to make true branchpoints at the ring boundaries # combined_skel = morph.skeletonize(combined_skel) # # The skeleton outside of the labels # outside_skel = combined_skel & (dilated_labels == 0) # # Associate all skeleton points with seed objects # dlabels, distance_map = propagate.propagate(np.zeros(labels.shape), dilated_labels, combined_skel, 1) # # Get rid of any branchpoints not connected to seeds # combined_skel[dlabels == 0] = False # # Find the branchpoints # branch_points = morph.branchpoints(combined_skel) # # Odd case: when four branches meet like this, branchpoints are not # assigned because they are arbitrary. So assign them. # # . . # B. # .B # . . # odd_case = (combined_skel[:-1, :-1] & combined_skel[1:, :-1] & combined_skel[:-1, 1:] & combined_skel[1, 1]) branch_points[:-1, :-1][odd_case] = True branch_points[1:, 1:][odd_case] = True # # Find the branching counts for the trunks (# of extra branches # eminating from a point other than the line it might be on). # branching_counts = morph.branchings(combined_skel) branching_counts = np.array([0, 0, 0, 1, 2])[branching_counts] # # Only take branches within 1 of the outside skeleton # dilated_skel = scind.binary_dilation(outside_skel, morph.eight_connect) branching_counts[~dilated_skel] = 0 # # Find the endpoints # end_points = morph.endpoints(combined_skel) # # We use two ranges for classification here: # * anything within one pixel of the dilated image is a trunk # * anything outside of that range is a branch # nearby_labels = dlabels.copy() nearby_labels[distance_map > 1.5] = 0 outside_labels = dlabels.copy() outside_labels[nearby_labels > 0] = 0 # # The trunks are the branchpoints that lie within one pixel of # the dilated image. # if labels_count > 0: trunk_counts = fix( scind.sum(branching_counts, nearby_labels, label_range)).astype(int) else: trunk_counts = np.zeros((0, ), int) # # The branches are the branchpoints that lie outside the seed objects # if labels_count > 0: branch_counts = fix( scind.sum(branch_points, outside_labels, label_range)) else: branch_counts = np.zeros((0, ), int) # # Save the endpoints # if labels_count > 0: end_counts = fix(scind.sum(end_points, outside_labels, label_range)) else: end_counts = np.zeros((0, ), int) # # Calculate the distances # total_distance = morph.skeleton_length(dlabels * outside_skel, label_range) # # Save measurements # m = workspace.measurements assert isinstance(m, cpmeas.Measurements) feature = "_".join((C_OBJSKELETON, F_NUMBER_TRUNKS, skeleton_name)) m.add_measurement(seed_objects_name, feature, trunk_counts) feature = "_".join( (C_OBJSKELETON, F_NUMBER_NON_TRUNK_BRANCHES, skeleton_name)) m.add_measurement(seed_objects_name, feature, branch_counts) feature = "_".join( (C_OBJSKELETON, F_NUMBER_BRANCH_ENDS, skeleton_name)) m.add_measurement(seed_objects_name, feature, end_counts) feature = "_".join( (C_OBJSKELETON, F_TOTAL_OBJSKELETON_LENGTH, skeleton_name)) m[seed_objects_name, feature] = total_distance # # Collect the graph information # if self.wants_objskeleton_graph: trunk_mask = (branching_counts > 0) & (nearby_labels != 0) intensity_image = workspace.image_set.get_image( self.intensity_image_name.value) edge_graph, vertex_graph = self.make_objskeleton_graph( combined_skel, dlabels, trunk_mask, branch_points & ~trunk_mask, end_points, intensity_image.pixel_data) image_number = workspace.measurements.image_set_number edge_path, vertex_path = self.get_graph_file_paths( m, m.image_number) workspace.interaction_request(self, m.image_number, edge_path, edge_graph, vertex_path, vertex_graph, headless_ok=True) if self.show_window: workspace.display_data.edge_graph = edge_graph workspace.display_data.vertex_graph = vertex_graph workspace.display_data.intensity_image = intensity_image.pixel_data # # Make the display image # if self.show_window or self.wants_branchpoint_image: branchpoint_image = np.zeros( (skeleton.shape[0], skeleton.shape[1], 3)) trunk_mask = (branching_counts > 0) & (nearby_labels != 0) branch_mask = branch_points & (outside_labels != 0) end_mask = end_points & (outside_labels != 0) branchpoint_image[outside_skel, :] = 1 branchpoint_image[trunk_mask | branch_mask | end_mask, :] = 0 branchpoint_image[trunk_mask, 0] = 1 branchpoint_image[branch_mask, 1] = 1 branchpoint_image[end_mask, 2] = 1 branchpoint_image[dilated_labels != 0, :] *= .875 branchpoint_image[dilated_labels != 0, :] += .1 if self.show_window: workspace.display_data.branchpoint_image = branchpoint_image if self.wants_branchpoint_image: bi = cpi.Image(branchpoint_image, parent_image=skeleton_image) workspace.image_set.add(self.branchpoint_image_name.value, bi)
def find_line_segments_ff(image, mask=None, seed_radius=7, border_size=4, n_bins=8, mag_ratio = 0.9, mag_tol=0.3, return_internals=False): """ Detect line segments The algotithm processds in this steps: * Calculate gradients and gradient magnitude and optionally downsample * Get seed points as local maximas of magnitude * Assign each seed point to an orientation bin based on its orientation * For each bin: * Calculate gradient magnitude for the bin orientation * From each seed point trace pixels using flood function * Fit line to the traced pixels and calculate endpoints Input ----- image : ndarray The input image with shape (H,W). Image is converted to float and normalized to 0-1 range. block_size : int Aggregation factor - image gradients and magnitude is downscaled by this factor before detecting lines in oder to increaseprocessing speed and reduce possible noise. Higher values lead to less precise lines. n_bins : int Number of orientation bins in which lines are traced. mag_ratio : float Ratio of mag_tol : float return_internals : bool Instead of line segments, return internal variables - gradients, etc. Output ------ lines: An instance of LineSegments or, if return_internals=True, a dict with internal stuff of the line traing algorithm Example ------- """ image = image.astype("f") / image.max() logging.debug(f"Calculating gradients and edge magnitudes") KX = gauss_deriv_kernel(2,1,direction="x") KY = gauss_deriv_kernel(2,1,direction="y") dx = ni.correlate(image, KX) dy = ni.correlate(image, KY) mask_borders(dx, border_size, 0) mask_borders(dy, border_size, 0) mag = np.sqrt(dx*dx + dy*dy) if mask is not None: mag *= mask # if block_size > 1: # block_size = (block_size,)*2 # scalar x -> tuple (x,x) # logging.debug(f"Downsampling image") # dx = block_reduce(dx, block_size, np.mean) # dy = block_reduce(dy, block_size, np.mean) # mag = block_reduce(mag, block_size, np.max) logging.debug(f"Calculating oriented gradient magnitude (nbins={n_bins})") theta = np.linspace(0, np.pi, n_bins, endpoint=False) # seed_dir = np.array([dx[r,c], dy[r,c]]).T grad_dir = np.array([np.sin(theta), np.cos(theta)]) # affinity = np.abs(seed_dir @ grad_dir) # grad_class = np.argmax(affinity, axis=1) grad_mag_arr = np.array([np.abs(dx*wx + dy*wy) for wx,wy in grad_dir.T], "f") grad_mag_ind = np.argmax(grad_mag_arr, axis=0) logging.debug(f"Searching for seed points") mag_top = ni.maximum_filter(mag, seed_radius) seed_mask = (mag == mag_top) r,c = np.nonzero(seed_mask) seed_mag = mag[r,c] grad_class = grad_mag_ind[r,c] mag_threshold = (1-mag_ratio) * seed_mag.max() seed_mask = seed_mag > mag_threshold logging.debug(f"{seed_mask.sum()} seed points found (total {r.size})") r = r[seed_mask] c = c[seed_mask] seed_mag = seed_mag[seed_mask] grad_class = grad_class[seed_mask] logging.debug(f"Sorting seed points") seed_order = np.argsort(seed_mag)[::-1] r = r[seed_order] c = c[seed_order] seed_mag = seed_mag[seed_order] grad_class = grad_class[seed_order] logging.debug("Tracing lines") found = np.zeros_like(mag,"i") components = [] grad_images = [] for g, grad_mag in enumerate(grad_mag_arr): bin_mask = ni.binary_dilation(grad_mag_ind==g, iterations=2) grad_image = grad_mag * bin_mask grad_images.append(grad_image) #seed_idx = grad_class == g #for i,seed in enumerate(zip(r[seed_idx],c[seed_idx]),start=found.max()+1): for i,seed in enumerate(zip(r,c,grad_class,seed_mag)): a,b,seed_bin,seed_mag = seed if found[a,b]: continue tol = seed_mag * mag_tol component = flood(grad_images[seed_bin], (a,b), tolerance=tol, selem=np.ones( (3,3) ) ) found[component] = i inds = np.nonzero(component) component_points = np.array((inds[1], inds[0]),"i").T components.append({"X": component_points, "seed_point": seed}) logging.debug("Calculating segment parameters") segments = fit_line_segments(components, mag, min_size=10, scale=1) if return_internals: return { "dx":dx, "dy":dy, "mag":mag, "mag_ind": grad_mag_ind, "seed_points": [r,c], "seed_grad_class": grad_class, "grad_dir": grad_dir, "segment_labels": found, "components": components, "segments" : segments, } else: return segments
def run_objectwise_metrics(refDSM, refDTM, refMask, testDSM, testDTM, testMask, tform, ignoreMask, merge_radius=2, plot=None, verbose=True): # parse plot input if plot is None: PLOTS_ENABLE = False else: PLOTS_ENABLE = True PLOTS_SAVE_PREFIX = "objectwise_" # Number of pixels to dilate reference object mask padding_pixels = np.round(merge_radius / getUnitWidth(tform)) strel = ndimage.generate_binary_structure(2, 1) # Dilate reference object mask to combine closely spaced objects ref_ndx_orig = np.copy(refMask) ref_ndx = ndimage.binary_dilation(ref_ndx_orig, structure=strel, iterations=padding_pixels.astype(int)) # Create index regions ref_ndx, num_ref_regions = ndimage.label(ref_ndx) ref_ndx_orig, num_ref_regions = ndimage.label(ref_ndx_orig) test_ndx, num_test_regions = ndimage.label(testMask) # Get Height from DSM-DTM ref_height = refDSM.astype(np.float64) - refDTM.astype(np.float64) ref_height[ref_ndx == 0] = 0 test_height = testDSM.astype(np.float64) - testDTM.astype(np.float64) test_height[test_ndx == 0] = 0 # Keep track of how many times each region is used test_use_counter = np.zeros([num_test_regions, 1]) ref_use_counter = np.zeros([num_ref_regions, 1]) # Calculate instance metrics class instance_parameters: def __init__(self): self.IOU_THRESHOLD = 0.5 self.MIN_AREA_FILTER = 0 self.UNCERTAIN_VALUE = 65 params = instance_parameters() metrics_container_no_merge, metrics_container_merge_fp, metrics_container_merge_fn = \ eval_instance_metrics(ref_ndx, params, test_ndx) no_merge_f1 = metrics_container_no_merge.f1_score merge_fp_f1 = metrics_container_merge_fp.f1_score merge_fn_f1 = metrics_container_merge_fn.f1_score num_buildings_performer = np.unique(test_ndx).__len__() - 1 num_buildings_truth = np.unique(ref_ndx).__len__() - 1 # Initialize metric list metric_list = [] # Make images to visualize certain metrics # Unused regions will be marked as zero, background as -1 image_out = refMask.astype(float).copy() - 1 #image_out[image_out == -1] = np.nan image_2d_completeness = image_out.copy() image_2d_correctness = image_out.copy() image_2d_jaccard_index = image_out.copy() image_3d_completeness = image_out.copy() image_3d_correctness = image_out.copy() image_3d_jaccard_index = image_out.copy() image_hrmse = image_out.copy() image_zrmse = image_out.copy() # Initialize min/max area/volume max_area = 0 min_area = 0 max_volume = 0 min_volume = 0 for loop_region in range(1, num_ref_regions + 1): # Reference region under evaluation ref_objs = (ref_ndx == loop_region) & refMask # Find test regions overlapping with ref test_regions = np.unique(test_ndx[ref_ndx == loop_region]) # Find test regions overlapping with ref ref_regions = np.unique(ref_ndx_orig[ref_ndx == loop_region]) # Remove background region, '0' if np.any(test_regions == 0): test_regions = test_regions.tolist() test_regions.remove(0) test_regions = np.array(test_regions) if np.any(ref_regions == 0): ref_regions = ref_regions.tolist() ref_regions.remove(0) ref_regions = np.array(ref_regions) if len(test_regions) == 0: continue for refRegion in ref_regions: # Increment counter for ref region used ref_use_counter[refRegion - 1] = ref_use_counter[refRegion - 1] + 1 # Make mask of overlapping test regions test_objs = np.zeros_like(testMask) for test_region in test_regions: test_objs = test_objs | (test_ndx == test_region) # Increment counter for test region used test_use_counter[test_region - 1] = test_use_counter[test_region - 1] + 1 # TODO: Not practical as implemented to enable plots. plots is forced to false. [result_geo, result_acc, unitArea] = eval_metrics(refDSM, refDTM, ref_objs, testDSM, testDTM, test_objs, tform, ignoreMask, plot=None, verbose=verbose) this_metric = dict() this_metric['ref_objects'] = test_regions.tolist() this_metric['test_objects'] = ref_regions.tolist() this_metric['threshold_geometry'] = result_geo this_metric['relative_accuracy'] = result_acc # Calculate min and max area/volume if this_metric['threshold_geometry']['area'][ 'test_area'] > max_area or loop_region == 1: max_area = this_metric['threshold_geometry']['area']['test_area'] if this_metric['threshold_geometry']['area'][ 'test_area'] < min_area or loop_region == 1: min_area = this_metric['threshold_geometry']['area']['test_area'] if this_metric['threshold_geometry']['volume'][ 'test_volume'] > max_volume or loop_region == 1: max_volume = this_metric['threshold_geometry']['volume'][ 'test_volume'] if this_metric['threshold_geometry']['volume'][ 'test_volume'] < min_volume or loop_region == 1: min_volume = this_metric['threshold_geometry']['volume'][ 'test_volume'] metric_list.append(this_metric) # Add scores to images for i in ref_regions: ind = ref_ndx_orig == i image_2d_completeness[ind] = result_geo['2D']['completeness'] image_2d_correctness[ind] = result_geo['2D']['correctness'] image_2d_jaccard_index[ind] = result_geo['2D']['jaccardIndex'] image_3d_completeness[ind] = result_geo['3D']['completeness'] image_3d_correctness[ind] = result_geo['3D']['correctness'] image_3d_jaccard_index[ind] = result_geo['3D']['jaccardIndex'] image_hrmse[ind] = result_acc['hrmse'] image_zrmse[ind] = result_acc['zrmse'] # Sort metrics by area # Calculate bins for area and volume num_bins = 10 area_range = max_area - min_area volume_range = max_volume - min_volume area_bin_width = np.round(area_range / num_bins) volume_bin_width = np.round(volume_range / num_bins) area_bins = [] volume_bins = [] for i in range(0, num_bins + 1): area_bins.append(np.floor(min_area) + i * area_bin_width) volume_bins.append(np.floor(min_volume) + i * volume_bin_width) # Create dicts with bins as keys iou_2d_area_bins = dict((el, []) for el in area_bins) iou_2d_volume_bins = dict((el, []) for el in volume_bins) iou_3d_area_bins = dict((el, []) for el in area_bins) iou_3d_volume_bins = dict((el, []) for el in volume_bins) # Create dicts with areas as keys iou_2d_area = {} iou_2d_volume = {} iou_3d_area = {} iou_3d_volume = {} # Keys are area/volume, Values are IOU for current_metric in metric_list: current_area = current_metric['threshold_geometry']['area'][ 'test_area'] * unitArea # Convert area to meters^2 current_volume = current_metric['threshold_geometry']['volume'][ 'test_volume'] # Get IOUS by area/volume iou_2d_area.update({ current_area: current_metric['threshold_geometry']['2D']['jaccardIndex'] }) iou_2d_volume.update({ current_volume: current_metric['threshold_geometry']['2D']['jaccardIndex'] }) iou_3d_area.update({ current_area: current_metric['threshold_geometry']['3D']['jaccardIndex'] }) iou_3d_volume.update({ current_volume: current_metric['threshold_geometry']['3D']['jaccardIndex'] }) # Create bins for area_bin_edge in area_bins: if current_area <= area_bin_edge: iou_2d_area_bins[area_bin_edge].append( current_metric['threshold_geometry']['2D']['jaccardIndex']) iou_3d_area_bins[area_bin_edge].append( current_metric['threshold_geometry']['3D']['jaccardIndex']) break for volume_bin_edge in volume_bins: if current_volume <= volume_bin_edge: iou_2d_volume_bins[volume_bin_edge].append( current_metric['threshold_geometry']['2D']['jaccardIndex']) iou_3d_volume_bins[volume_bin_edge].append( current_metric['threshold_geometry']['3D']['jaccardIndex']) break # Average IOUs in bins for current_bin in area_bins: iou_2d_area_bins[current_bin] = np.mean(iou_2d_area_bins[current_bin]) iou_3d_area_bins[current_bin] = np.mean(iou_3d_area_bins[current_bin]) for current_bin in volume_bins: iou_2d_volume_bins[current_bin] = np.mean( iou_2d_volume_bins[current_bin]) iou_3d_volume_bins[current_bin] = np.mean( iou_3d_volume_bins[current_bin]) # plot if PLOTS_ENABLE: print('Input plots...') # Save instance level stoplight charts plot.make_instance_stoplight_charts( metrics_container_no_merge.stoplight_chart, saveName=PLOTS_SAVE_PREFIX + "instanceStoplightNoMerge") plot.make_instance_stoplight_charts( metrics_container_merge_fp.stoplight_chart, saveName=PLOTS_SAVE_PREFIX + "instanceStoplightMergePerformer") plot.make_instance_stoplight_charts( metrics_container_merge_fn.stoplight_chart, saveName=PLOTS_SAVE_PREFIX + "instanceStoplightMergeGT") # IOU Histograms plot.make_iou_histogram(iou_2d_area_bins, 'Area (m^2)', '2D Mean IOUs by Area', 373, saveName=PLOTS_SAVE_PREFIX + "obj2dIOUbyAreaHistogram") plot.make_iou_histogram(iou_2d_volume_bins, 'Volume (m^3)', '2D Mean IOUs by Volume', 374, saveName=PLOTS_SAVE_PREFIX + "obj2dIOUbyVolumeHistogram") plot.make_iou_histogram(iou_3d_area_bins, 'Area (m^2)', '3D Mean IOUs by Area', 375, saveName=PLOTS_SAVE_PREFIX + "obj3dIOUbyAreaHistogram") plot.make_iou_histogram(iou_3d_volume_bins, 'Volume (m^3)', '3D Mean IOUs by Volume', 376, saveName=PLOTS_SAVE_PREFIX + "obj3dIOUbyVolumeHistogram") # Scatter plots plot.make_iou_scatter(iou_2d_area, 'Area (m^2)', '2D IOUs by Area', 373, saveName=PLOTS_SAVE_PREFIX + "obj2dIOUbyAreaScatter") plot.make_iou_scatter(iou_2d_volume, 'Volume (m^3)', '2D IOUs by Volume', 374, saveName=PLOTS_SAVE_PREFIX + "obj2dIOUbyVolumeScatter") plot.make_iou_scatter(iou_3d_area, 'Area (m^2)', '3D IOUs by Area', 375, saveName=PLOTS_SAVE_PREFIX + "obj3dIOUbyAreaScatter") plot.make_iou_scatter(iou_3d_volume, 'Volume (m^3)', '3D IOUs by Volume', 376, saveName=PLOTS_SAVE_PREFIX + "obj3dIOUbyVolumeScatter") plot.make(image_2d_completeness, 'Objectwise 2D Completeness', 351, saveName=PLOTS_SAVE_PREFIX + "obj2dCompleteness", colorbar=True, badValue=-1, vmin=0, vmax=1) plot.make(image_2d_correctness, 'Objectwise 2D Correctness', 352, saveName=PLOTS_SAVE_PREFIX + "obj2dCorrectness", colorbar=True, badValue=-1, vmin=0, vmax=1) plot.make(image_2d_jaccard_index, 'Objectwise 2D Jaccard Index', 353, saveName=PLOTS_SAVE_PREFIX + "obj2dJaccardIndex", colorbar=True, badValue=-1, vmin=0, vmax=1) plot.make(image_3d_completeness, 'Objectwise 3D Completeness', 361, saveName=PLOTS_SAVE_PREFIX + "obj3dCompleteness", colorbar=True, badValue=-1, vmin=0, vmax=1) plot.make(image_3d_correctness, 'Objectwise 3D Correctness', 362, saveName=PLOTS_SAVE_PREFIX + "obj3dCorrectness", colorbar=True, badValue=-1, vmin=0, vmax=1) plot.make(image_3d_jaccard_index, 'Objectwise 3D Jaccard Index', 363, saveName=PLOTS_SAVE_PREFIX + "obj3dJaccardIndex", colorbar=True, badValue=-1, vmin=0, vmax=1) plot.make(image_hrmse, 'Objectwise HRMSE', 371, saveName=PLOTS_SAVE_PREFIX + "objHRMSE", colorbar=True, badValue=-1, vmin=0, vmax=2) plot.make(image_zrmse, 'Objectwise ZRMSE', 372, saveName=PLOTS_SAVE_PREFIX + "objZRMSE", colorbar=True, badValue=-1, vmin=0, vmax=1) plot.make_obj_error_map(error_map=image_hrmse, ref=refMask, badValue=-1, saveName=PLOTS_SAVE_PREFIX + "HRMSE_Image_Only") plot.make_obj_error_map(error_map=image_zrmse, ref=refMask, badValue=-1, saveName=PLOTS_SAVE_PREFIX + "ZRMSE_Image_Only") # Make per metric reporting structure num_objs = len(metric_list) summary = {} summary['counts'] = {} summary['counts']['ref'] = { 'total': len(ref_use_counter), 'used': np.sum(ref_use_counter >= 1).astype(float), 'unused': np.sum(ref_use_counter == 0).astype(float) } # Track number of times test objs got reused key, val = np.unique(test_use_counter, return_counts=True) summary['counts']['test'] = { 'total': len(test_use_counter), 'used': np.sum(test_use_counter >= 1).astype(float), 'unused': np.sum(test_use_counter == 0).astype(float), 'counts': { 'key': key.tolist(), 'value': val.tolist() } } summary['threshold_geometry'] = {} summary['threshold_geometry']['2D'] = {} summary['threshold_geometry']['2D']['correctness'] = {} summary['threshold_geometry']['2D']['completeness'] = {} summary['threshold_geometry']['2D']['jaccardIndex'] = {} summary['threshold_geometry']['3D'] = {} summary['threshold_geometry']['3D']['correctness'] = {} summary['threshold_geometry']['3D']['completeness'] = {} summary['threshold_geometry']['3D']['jaccardIndex'] = {} summary['relative_accuracy'] = {} summary['relative_accuracy']['hrmse'] = {} summary['relative_accuracy']['zrmse'] = {} summary['threshold_geometry']['2D']['correctness']['values'] = np.zeros( num_objs) summary['threshold_geometry']['2D']['completeness']['values'] = np.zeros( num_objs) summary['threshold_geometry']['2D']['jaccardIndex']['values'] = np.zeros( num_objs) summary['threshold_geometry']['3D']['correctness']['values'] = np.zeros( num_objs) summary['threshold_geometry']['3D']['completeness']['values'] = np.zeros( num_objs) summary['threshold_geometry']['3D']['jaccardIndex']['values'] = np.zeros( num_objs) summary['relative_accuracy']['zrmse']['values'] = np.zeros(num_objs) summary['relative_accuracy']['hrmse']['values'] = np.zeros(num_objs) ctr = 0 for m in metric_list: summary['threshold_geometry']['2D']['correctness']['values'][ctr] = m[ 'threshold_geometry']['2D']['correctness'] summary['threshold_geometry']['2D']['completeness']['values'][ctr] = m[ 'threshold_geometry']['2D']['completeness'] summary['threshold_geometry']['2D']['jaccardIndex']['values'][ctr] = m[ 'threshold_geometry']['2D']['jaccardIndex'] summary['threshold_geometry']['3D']['correctness']['values'][ctr] = m[ 'threshold_geometry']['3D']['correctness'] summary['threshold_geometry']['3D']['completeness']['values'][ctr] = m[ 'threshold_geometry']['3D']['completeness'] summary['threshold_geometry']['3D']['jaccardIndex']['values'][ctr] = m[ 'threshold_geometry']['3D']['jaccardIndex'] summary['relative_accuracy']['zrmse']['values'][ctr] = m[ 'relative_accuracy']['zrmse'] summary['relative_accuracy']['hrmse']['values'][ctr] = m[ 'relative_accuracy']['hrmse'] ctr += 1 # Compute Summaries summary['threshold_geometry']['2D']['correctness'] = metric_stats( summary['threshold_geometry']['2D']['correctness']['values']) summary['threshold_geometry']['2D']['completeness'] = metric_stats( summary['threshold_geometry']['2D']['completeness']['values']) summary['threshold_geometry']['2D']['jaccardIndex'] = metric_stats( summary['threshold_geometry']['2D']['jaccardIndex']['values']) summary['threshold_geometry']['3D']['correctness'] = metric_stats( summary['threshold_geometry']['3D']['correctness']['values']) summary['threshold_geometry']['3D']['completeness'] = metric_stats( summary['threshold_geometry']['3D']['completeness']['values']) summary['threshold_geometry']['3D']['jaccardIndex'] = metric_stats( summary['threshold_geometry']['3D']['jaccardIndex']['values']) summary['relative_accuracy']['zrmse'] = metric_stats( summary['relative_accuracy']['zrmse']['values']) summary['relative_accuracy']['hrmse'] = metric_stats( summary['relative_accuracy']['hrmse']['values']) # Make summary of metrics results = { 'summary': summary, 'objects': metric_list, 'instance_f1': no_merge_f1, 'instance_f1_merge_fp': merge_fp_f1, 'instance_f1_merge_fn': merge_fn_f1, 'num_buildings_gt': num_buildings_truth, 'num_buildings_perf': num_buildings_performer } return results, test_ndx, ref_ndx
name_chs = ['V', 'A', 'PV'] out_h5py = './data/h5_rs_one_case/ct01m_c1.h5' logger = call_logger(log_file=None, 'GenData') n_class = 2 w_class = [1.0, 0.2] w_class_in_roi = [2.0, 2.0] itkimage1 = sitk.ReadImage(fn1) labels1 = (sitk.GetArrayFromImage(itkimage1)[::-1, :, :] == 1).astype( 'int') #dongmai #labels0 = (1-labels1).astype('int') labels1_dilate = binary_dilation(labels1, structure=np.ones( (5, 5, 5))).astype(labels1.dtype) labels = labels1.copy() #np.stack((labels0,labels1),axis = 0) labels_dilate = (labels1_dilate > 0).astype('int') #%% flist = [str(fn) for fn in (Path(dicom_fd) / name_chs[0]).glob('*')] n_slice = len(flist) row, col = 512, 512 labels_pos = np.where(labels_dilate == 1) z_min, z_max = labels_pos[0].min(), labels_pos[0].max() y_min, y_max = labels_pos[1].min(), labels_pos[1].max() x_min, x_max = labels_pos[2].min(), labels_pos[2].max() z_min = max(0, z_min - 16)
def test_morphology_fft_dilate_3D(self): im = self.im truth = spim.binary_dilation(im, structure=ball(3)) test = ps.tools.fftmorphology(im, strel=ball(3), mode='dilation') assert np.all(truth == test)
def make_cprops_mask(cube, prior=None, hi_thresh=4.0, lo_thresh=2.0, scale=1.0, n_chan=2, out_of=None, spec_axis=2, min_vol=None, min_area=None, min_vchan=None, timer=False, verbose=False): """ General purpose masking routine. Identify regions above a given threshold across several channels. Reject regions based on volume, area, and velociy extent. Returns a mask matchedin size to the input image. Rather than wrap a read/writer or signal-to-noise cube generator in here, these are held externally. The scale parameter gives the option to invert the cube by using -1 or to scale, e.g., by the RMS. """ # ------------------------------------------------------------ # Generate the candidate mask # ------------------------------------------------------------ if timer: start = time.time() full_start = time.time() # High threshold hi_mask = make_spec_mask(cube, thresh=hi_thresh, scale=scale, n_chan=n_chan, out_of=out_of, spec_axis=spec_axis) if timer: stop = time.time() print "High threshold mask took ", stop - start start = time.time() # Low threshold lo_mask = make_spec_mask(cube, thresh=lo_thresh, scale=scale, n_chan=n_chan, out_of=out_of, spec_axis=spec_axis) if timer: stop = time.time() print "Low threshold mask took ", stop - start start = time.time() # Expand regions that meet the high threshold to includ all # connected low threshold regions mask = binary_dilation(hi_mask, mask=lo_mask, iterations=-1) if timer: stop = time.time() print "Mask dilation took ", stop - start start = time.time() # ------------------------------------------------------------ # Pare small regions # ------------------------------------------------------------ # Note that this step could be accomplished to some degree by # using a (binary) opening operator. It's not perfect, it will # clip your edges but it might be much faster and better defined. if min_area != None or min_vol != None or min_vchan != None: reject_regions(mask, min_area=min_area, min_vol=min_vol, min_vchan=min_vchan, spec_axis=spec_axis, inplace=True, timer=timer) if timer: stop = time.time() print "Small region rejection took ", stop - start # ------------------------------------------------------------ # Apply any prior mask supplied # ------------------------------------------------------------ if prior != None: mask *= prior # ------------------------------------------------------------ # Return final mask # ------------------------------------------------------------ if timer: full_stop = time.time() print "Whole masking took ", full_stop - full_start return mask
run.load() pp = g.PreProcessor(run) pp.extract_valid_region() pp.filter_zeroes() # pp.interpolate_zeroes() coords = pp.Z, pp.X, pp.T data = pp.U, pp.V, pp.W all_coords = np.concatenate([c[None] for c in coords]) data = pp.U[:, :, 2000:-2000] invalid = np.isnan(data) invalid_with_shell = ndi.binary_dilation(invalid, iterations=1, structure=np.ones((3, 3, 3))) complete_valid_shell = invalid_with_shell & ~invalid volumes, n = ndi.label(invalid_with_shell) slices = ndi.find_objects(volumes) def interpolate_region(slice): nans = invalid[slice] shell = complete_valid_shell[slice] valid_points = np.vstack(c[slice][shell] for c in coords).T valid_values = data[slice][shell] interpolator = interp.LinearNDInterpolator(valid_points, valid_values)
def segmentNucleiOnly(nuc, varargin={}): ## define analysis parameters # parameters for nuclei detection arg = dict() arg['nuc_erode'] = disk(4) # initial erosion to enhance nuclei centers arg['nuc_smooth'] = 14 # filtering to smooth it out arg['nuc_suppress'] = 0.05 # suppression of small peaks - units are in a [0 1] space arg['nuc_minarea'] = 30 # smaller then this its not a nuclei arg['nuc_maxarea'] = float('inf') arg['nuc_stretch'] = [1, 99] arg['nuc_channel'] = 'DeepBlue' arg['mindistancefromedge'] = 150 arg['shrinkmsk'] = disk(50) arg['removetopprcentile'] = 0 arg['register'] = [] # optional registration object arg['registerreference'] = [] arg['project'] = False arg['specificframeonly'] = [ ] # will duplicate that frame for all timepoints arg['singleframe'] = False # must be used in conjunction with specificframeonly. IF true will only return single timepoint arg['track_method'] = 'none' arg['threshold_method'] = 'otsu' arg = parseVarargin(varargin, arg) NucLabels = np.zeros(nuc.shape, dtype=int) for i in range(nuc.shape[2]): current_img = nuc[:, :, i] nucbw = current_img > filters.threshold_otsu(current_img) nucbw = binary_closing(nucbw, disk(2)) nucbw = binary_fill_holes(nucbw, disk(5)) nucpeaks = erosion(current_img, arg['nuc_erode']) nucpeaks = filters.gaussian(nucpeaks, sigma=arg['nuc_smooth']) nucpeaks = np.clip(nucpeaks, np.percentile(nucpeaks, 1), np.percentile(nucpeaks, 99)) nucpeaks = np.divide(nucpeaks, float(np.amax(nucpeaks))) np.place(nucpeaks, ~nucbw, 0) dist_img = distance(nucpeaks) nucpeaks_max = peak_local_max( dist_img, min_distance=10) #, threshold_abs=arg['nuc_suppress']) peak_img = np.zeros(current_img.shape) for yx in nucpeaks_max: peak_img[yx[0], yx[1]] = 1 peak_labels = label(peak_img) props = regionprops(peak_labels) for p in props: if p.area == 1: continue elif p.area == 2: peak_img[p.coords[0][0], p.coords[0][1]] = 0 elif p.area > 2: centroid = p.centroid pcenter = np.argmin( [np.sum(np.abs(centroid - jj)) for jj in p.coords]) for idx, xy in enumerate(p.coords): if not idx == pcenter: peak_img[xy[0], xy[1]] = 0 labels = label(peak_img) lbls = watershed(dist_img * -1, labels, connectivity=1, watershed_line=True, mask=nucbw) lbls = binary_erosion(lbls, disk(3)) lbls = binary_dilation(lbls, disk(2)) lbls = label(lbls) lbls = remove_small_objects(lbls, arg['nuc_minarea']) NucLabels[:, :, i] = lbls return NucLabels
def run_function(self, function, pixel_data, mask): '''Apply the function once to the image, returning the result''' count = function.repeat_count function_name = function.function.value scale = function.scale.value custom_repeats = function.custom_repeats.value is_binary = pixel_data.dtype.kind == 'b' if function.structuring_element == SE_ARBITRARY: strel = np.array(function.strel.get_matrix()) elif function.structuring_element == SE_DISK: strel = morph.strel_disk(scale / 2.0) elif function.structuring_element == SE_DIAMOND: strel = morph.strel_diamond(scale / 2.0) elif function.structuring_element == SE_LINE: strel = morph.strel_line(scale, function.angle.value) elif function.structuring_element == SE_OCTAGON: strel = morph.strel_octagon(scale / 2.0) elif function.structuring_element == SE_PAIR: strel = morph.strel_pair(function.x_offset.value, function.y_offset.value) elif function.structuring_element == SE_PERIODIC_LINE: xoff = function.x_offset.value yoff = function.y_offset.value n = max(scale / 2.0 / np.sqrt(float(xoff * xoff + yoff * yoff)), 1) strel = morph.strel_periodicline(xoff, yoff, n) elif function.structuring_element == SE_RECTANGLE: strel = morph.strel_rectangle(function.width.value, function.height.value) else: strel = morph.strel_square(scale) if (function_name in (F_BRANCHPOINTS, F_BRIDGE, F_CLEAN, F_DIAG, F_CONVEX_HULL, F_DISTANCE, F_ENDPOINTS, F_FILL, F_FILL_SMALL, F_HBREAK, F_LIFE, F_MAJORITY, F_REMOVE, F_SHRINK, F_SKEL, F_SKELPE, F_SPUR, F_THICKEN, F_THIN, F_VBREAK) and not is_binary): # Apply a very crude threshold to the image for binary algorithms logger.warning("Warning: converting image to binary for %s\n" % function_name) pixel_data = pixel_data != 0 if (function_name in (F_BRANCHPOINTS, F_BRIDGE, F_CLEAN, F_DIAG, F_CONVEX_HULL, F_DISTANCE, F_ENDPOINTS, F_FILL, F_FILL_SMALL, F_HBREAK, F_INVERT, F_LIFE, F_MAJORITY, F_REMOVE, F_SHRINK, F_SKEL, F_SKELPE, F_SPUR, F_THICKEN, F_THIN, F_VBREAK, F_OPENLINES) or (is_binary and function_name in (F_CLOSE, F_DILATE, F_ERODE, F_OPEN))): # All of these have an iterations argument or it makes no # sense to iterate if function_name == F_BRANCHPOINTS: return morph.branchpoints(pixel_data, mask) elif function_name == F_BRIDGE: return morph.bridge(pixel_data, mask, count) elif function_name == F_CLEAN: return morph.clean(pixel_data, mask, count) elif function_name == F_CLOSE: if mask is None: return scind.binary_closing(pixel_data, strel, iterations=count) else: return (scind.binary_closing( pixel_data & mask, strel, iterations=count) | (pixel_data & ~mask)) elif function_name == F_CONVEX_HULL: if mask is None: return morph.convex_hull_image(pixel_data) else: return morph.convex_hull_image(pixel_data & mask) elif function_name == F_DIAG: return morph.diag(pixel_data, mask, count) elif function_name == F_DILATE: return scind.binary_dilation(pixel_data, strel, iterations=count, mask=mask) elif function_name == F_DISTANCE: image = scind.distance_transform_edt(pixel_data) if function.rescale_values.value: image = image / np.max(image) return image elif function_name == F_ENDPOINTS: return morph.endpoints(pixel_data, mask) elif function_name == F_ERODE: return scind.binary_erosion(pixel_data, strel, iterations=count, mask=mask) elif function_name == F_FILL: return morph.fill(pixel_data, mask, count) elif function_name == F_FILL_SMALL: def small_fn(area, foreground): return (not foreground) and (area <= custom_repeats) return morph.fill_labeled_holes(pixel_data, mask, small_fn) elif function_name == F_HBREAK: return morph.hbreak(pixel_data, mask, count) elif function_name == F_INVERT: if is_binary: if mask is None: return ~pixel_data result = pixel_data.copy() result[mask] = ~result[mask] return result elif mask is None: return 1 - pixel_data else: result = pixel_data.copy() result[mask] = 1 - result[mask] return result elif function_name == F_LIFE: return morph.life(pixel_data, count) elif function_name == F_MAJORITY: return morph.majority(pixel_data, mask, count) elif function_name == F_OPEN: if mask is None: return scind.binary_opening(pixel_data, strel, iterations=count) else: return (scind.binary_opening( pixel_data & mask, strel, iterations=count) | (pixel_data & ~mask)) elif function_name == F_OPENLINES: return morph.openlines(pixel_data, linelength=custom_repeats, mask=mask) elif function_name == F_REMOVE: return morph.remove(pixel_data, mask, count) elif function_name == F_SHRINK: return morph.binary_shrink(pixel_data, count) elif function_name == F_SKEL: return morph.skeletonize(pixel_data, mask) elif function_name == F_SKELPE: return morph.skeletonize( pixel_data, mask, scind.distance_transform_edt(pixel_data) * poisson_equation(pixel_data)) elif function_name == F_SPUR: return morph.spur(pixel_data, mask, count) elif function_name == F_THICKEN: return morph.thicken(pixel_data, mask, count) elif function_name == F_THIN: return morph.thin(pixel_data, mask, count) elif function_name == F_VBREAK: return morph.vbreak(pixel_data, mask) else: raise NotImplementedError( "Unimplemented morphological function: %s" % function_name) else: for i in range(count): if function_name == F_BOTHAT: new_pixel_data = morph.black_tophat(pixel_data, mask=mask, footprint=strel) elif function_name == F_CLOSE: new_pixel_data = morph.closing(pixel_data, mask=mask, footprint=strel) elif function_name == F_DILATE: new_pixel_data = morph.grey_dilation(pixel_data, mask=mask, footprint=strel) elif function_name == F_ERODE: new_pixel_data = morph.grey_erosion(pixel_data, mask=mask, footprint=strel) elif function_name == F_OPEN: new_pixel_data = morph.opening(pixel_data, mask=mask, footprint=strel) elif function_name == F_TOPHAT: new_pixel_data = morph.white_tophat(pixel_data, mask=mask, footprint=strel) else: raise NotImplementedError( "Unimplemented morphological function: %s" % function_name) if np.all(new_pixel_data == pixel_data): break pixel_data = new_pixel_data return pixel_data
def median_otsu(input_volume, vol_idx=None, median_radius=4, numpass=4, autocrop=False, dilate=None): """Simple brain extraction tool method for images from DWI data. It uses a median filter smoothing of the input_volumes `vol_idx` and an automatic histogram Otsu thresholding technique, hence the name *median_otsu*. This function is inspired from Mrtrix's bet which has default values ``median_radius=3``, ``numpass=2``. However, from tests on multiple 1.5T and 3T data from GE, Philips, Siemens, the most robust choice is ``median_radius=4``, ``numpass=4``. Parameters ---------- input_volume : ndarray 3D or 4D array of the brain volume. vol_idx : None or array, optional. 1D array representing indices of ``axis=3`` of a 4D `input_volume`. None is only an acceptable input if ``input_volume`` is 3D. median_radius : int Radius (in voxels) of the applied median filter (default: 4). numpass: int Number of pass of the median filter (default: 4). autocrop: bool, optional if True, the masked input_volume will also be cropped using the bounding box defined by the masked data. Should be on if DWI is upsampled to 1x1x1 resolution. (default: False). dilate : None or int, optional number of iterations for binary dilation Returns ------- maskedvolume : ndarray Masked input_volume mask : 3D ndarray The binary brain mask Notes ----- Copyright (C) 2011, the scikit-image team All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of skimage nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ if len(input_volume.shape) == 4: if vol_idx is not None: b0vol = np.mean(input_volume[..., tuple(vol_idx)], axis=3) else: raise ValueError("For 4D images, must provide vol_idx input") else: b0vol = input_volume.copy() # Make a mask using a multiple pass median filter and histogram # thresholding. mask = multi_median(b0vol, median_radius, numpass) thresh = otsu(mask) mask = mask > thresh if dilate is not None: cross = generate_binary_structure(3, 1) mask = binary_dilation(mask, cross, iterations=dilate) # Auto crop the volumes using the mask as input_volume for bounding box # computing. if autocrop: mins, maxs = bounding_box(mask) mask = crop(mask, mins, maxs) croppedvolume = crop(input_volume, mins, maxs) maskedvolume = applymask(croppedvolume, mask) else: maskedvolume = applymask(input_volume, mask) return maskedvolume, mask
def identify_sources(self): """Create a master coordinate list of sources identified in the specified total detection product image """ # read in sci, wht extensions of drizzled product image = self.image.data.copy() # Estimate FWHM from image sources # Background statistics need to be computed prior to subtracting background from image bkg_sigma = mad_std(image, ignore_nan=True) detect_sources_thresh = self.param_dict["dao"]["bkgsig_sf"] * bkg_sigma # Input image will be background subtracted using pre-computed background, unless # specified explicitly by the user if self.param_dict["dao"]["simple_bkg"]: self.bkg_used = np.nanmedian(image) image -= self.bkg_used else: # Estimate background self.bkg_used = self.image.bkg.background image -= self.bkg_used segm = detect_sources(image, detect_sources_thresh, npixels=self.param_dict["sourcex"]["source_box"], filter_kernel=self.image.kernel) cat = source_properties(image, segm) source_table = cat.to_table() smajor_sigma = source_table['semimajor_axis_sigma'].mean().value source_fwhm = smajor_sigma * gaussian_sigma_to_fwhm if not self.tp_sources: # Report configuration values to log log.info("{}".format("=" * 80)) log.info("") log.info("Point-source finding settings") log.info("Total Detection Product - Input Parameters") log.info("INPUT PARAMETERS") log.info("{}: {}".format("self.param_dict['dao']['bkgsig_sf']", self.param_dict["dao"]["bkgsig_sf"])) log.info("{}: {}".format( "self.param_dict['dao']['kernel_sd_aspect_ratio']", self.param_dict['dao']['kernel_sd_aspect_ratio'])) log.info("{}: {}".format("self.param_dict['dao']['simple_bkg']", self.param_dict['dao']['simple_bkg'])) log.info("{}: {}".format("self.image.bkg_rms_mean", self.image.bkg_rms_mean)) log.info("{}: {}".format("self.image.bkg_rms_mean", self.image.bkg_rms_mean)) log.info("{}: {}".format( "self.param_dict['sourcex']['source_box']", self.param_dict["sourcex"]["source_box"])) log.info("\nDERIVED PARAMETERS") log.info("{}: {}".format("bkg_sigma", bkg_sigma)) log.info("{}: {}".format("detect_sources_thresh", detect_sources_thresh)) log.info("{}: {}".format("smajor_sigma", smajor_sigma)) log.info("{}: {}".format("source_fwhm", source_fwhm)) log.info("") log.info("{}".format("=" * 80)) # find ALL the sources!!! log.info("DAOStarFinder(fwhm={}, threshold={}, ratio={})".format( source_fwhm, self.image.bkg_rms_mean, self.param_dict['dao']['kernel_sd_aspect_ratio'])) daofind = DAOStarFinder( fwhm=source_fwhm, threshold=self.image.bkg_rms_mean, ratio=self.param_dict['dao']['kernel_sd_aspect_ratio']) # create mask to reject any sources located less than 10 pixels from a image/chip edge wht_image = self.image.data.copy() binary_inverted_wht = np.where(wht_image == 0, 1, 0) exclusion_mask = ndimage.binary_dilation(binary_inverted_wht, iterations=10) sources = daofind(image, mask=exclusion_mask) for col in sources.colnames: sources[ col].info.format = '%.8g' # for consistent table output self.sources = sources # if processing filter product, use sources identified by parent total drizzle product identify_sources() run if self.tp_sources: self.sources = self.tp_sources['aperture']['sources']
stellarMask = (stellarMask > 0.01) # Recombine the nebular and stellar components fgdRegion = np.logical_or(nebularMask, stellarMask) # Return the flux-bright pixels to the user return fgdRegion ################################################################################ # Read in the Kokopelli Mask kokopelliMask = ai.reduced.ReducedScience.read('kokopelliMask.fits') # Dilate the mask in order to be more conservative. kokopelliMask.data = ndimage.binary_dilation(kokopelliMask.data, iterations=8).astype(int) # Construct the 2MASS masks and save to disk # Read in all the 2MASS images and store them in a dictionary for quick reference TMASS_Hfiles = np.array(glob.glob(os.path.join(TMASSdir, '*H.fits'))) TMASS_Kfiles = np.array(glob.glob(os.path.join(TMASSdir, '*Ks.fits'))) # Read in the 2MASS images TMASS_HimgList = [ai.reduced.ReducedScience.read(f) for f in TMASS_Hfiles] TMASS_KimgList = [ai.reduced.ReducedScience.read(f) for f in TMASS_Kfiles] # Convert the images to "background masks" for img in TMASS_HimgList: # Construct the output name for this mask base = os.path.basename(img.filename) targetMask = base.split('.')[0] + '_mask.fits'
def lacosmic(data, contrast, cr_threshold, neighbor_threshold, error=None, mask=None, background=None, effective_gain=None, readnoise=None, maxiter=4, border_mode='mirror'): """ Remove cosmic rays from an astronomical image using the L.A.Cosmic algorithm. The `L.A.Cosmic algorithm <http://www.astro.yale.edu/dokkum/lacosmic/>`_ is based on Laplacian edge detection and is described in `van Dokkum (2001; PASP 113, 1420) <https://ui.adsabs.harvard.edu/abs/2001PASP..113.1420V/abstract>`_. Parameters ---------- data : array_like The 2D array of the image. contrast : float Contrast threshold between the Laplacian image and the fine-structure image. If your image is critically sampled, use a value around 2. If your image is undersampled (e.g., HST data), a value of 4 or 5 (or more) is more appropriate. If your image is oversampled, use a value between 1 and 2. For details, please see `PASP 113, 1420 (2001) <https://ui.adsabs.harvard.edu/abs/2001PASP..113.1420V/abstract>`_, which calls this parameter :math:`f_{\\mbox{lim}}`. In particular, Figure 4 shows the approximate relationship between the ``contrast`` parameter and the full-width half-maximum (in pixels) of stars in your image. cr_threshold : float The Laplacian signal-to-noise ratio threshold for cosmic-ray detection. neighbor_threshold : float The Laplacian signal-to-noise ratio threshold for detection of cosmic rays in pixels neighboring the initially-identified cosmic rays. error : array_like, optional The 1-sigma errors of the input ``data``. If ``error`` is not input, then ``effective_gain`` and ``readnoise`` will be used to construct an approximate model of the ``error``. If ``error`` is input, it will override the ``effective_gain`` and ``readnoise`` parameters. ``error`` must have the same shape as ``data``. mask : array_like (bool), optional A boolean mask, with the same shape as ``data``, where a `True` value indicates the corresponding element of ``data`` is masked. Masked pixels are ignored when identifying cosmic rays. It is highly recommended that saturated stars be included in ``mask``. background : float or array_like, optional The background level previously subtracted from the input ``data``. ``background`` may either be a scalar value or a 2D image with the same shape as the input ``data``. If the input ``data`` has not been background subtracted, then set ``background=None`` (default). effective_gain : float, array-like, optional Ratio of counts (e.g., electrons or photons) to the units of ``data``. For example, if your input ``data`` are in units of ADU, then ``effective_gain`` should represent electrons/ADU. If your input ``data`` are in units of electrons/s then ``effective_gain`` should be the exposure time (or an exposure time map). ``effective_gain`` and ``readnoise`` must be specified if ``error`` is not input. readnoise : float, optional The read noise (in electrons) in the input ``data``. ``effective_gain`` and ``readnoise`` must be specified if ``error`` is not input. maxiter : float, optional The maximum number of iterations. The default is 4. The routine will automatically exit if no additional cosmic rays are identified in an interation. If the routine is still identifying cosmic rays after four iterations, then you are likely digging into sources (e.g., saturated stars) and/or the noise. In that case, try inputing a ``mask`` or increasing the value of ``cr_threshold``. border_mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional The mode in which the array borders are handled during convolution and median filtering. For 'constant', the fill value is 0. The default is 'mirror', which matches the original L.A.Cosmic algorithm. Returns ------- cleaned_image : `~numpy.ndarray` The cosmic-ray cleaned image. crmask : `~numpy.ndarray` (bool) A mask image of the identified cosmic rays. Cosmic-ray pixels have a value of `True`. """ block_size = 2.0 kernel = np.array([[0.0, -1.0, 0.0], [-1.0, 4.0, -1.0], [0.0, -1.0, 0.0]]) clean_data = data.copy() if background is not None: clean_data += background final_crmask = np.zeros(data.shape, dtype=bool) if error is not None: if data.shape != error.shape: raise ValueError('error and data must have the same shape') clean_error_image = error ncosmics, ncosmics_tot = 0, 0 for iteration in range(maxiter): sampled_img = block_replicate(clean_data, block_size) convolved_img = ndimage.convolve(sampled_img, kernel, mode=border_mode).clip(min=0.0) laplacian_img = block_reduce(convolved_img, block_size) if clean_error_image is None: if effective_gain is None or readnoise is None: raise ValueError('effective_gain and readnoise must be ' 'input if error is not input') med5_img = ndimage.median_filter(clean_data, size=5, mode=border_mode).clip(min=1.e-5) error_image = (np.sqrt(effective_gain*med5_img + readnoise**2) / effective_gain) else: error_image = clean_error_image snr_img = laplacian_img / (block_size * error_image) # this is used to remove extended structures (larger than ~5x5) snr_img -= ndimage.median_filter(snr_img, size=5, mode=border_mode) # used to remove compact bright objects med3_img = ndimage.median_filter(clean_data, size=3, mode=border_mode) med7_img = ndimage.median_filter(med3_img, size=7, mode=border_mode) finestruct_img = ((med3_img - med7_img) / error_image).clip(min=0.01) cr_mask1 = snr_img > cr_threshold # NOTE: to follow the paper exactly, this condition should be # "> contrast * block_size". "lacos_im.cl" uses simply "> contrast" cr_mask2 = (snr_img / finestruct_img) > contrast cr_mask = cr_mask1 * cr_mask2 if mask is not None: cr_mask = np.logical_and(cr_mask, ~mask) # grow cosmic rays by one pixel and check in snr_img selem = np.ones((3, 3)) neigh_mask = ndimage.binary_dilation(cr_mask, selem) cr_mask = cr_mask1 * neigh_mask # now grow one more pixel and lower the detection threshold neigh_mask = ndimage.binary_dilation(cr_mask, selem) cr_mask = (snr_img > neighbor_threshold) * neigh_mask # previously unknown cosmic rays found in this iteration crmask_new = np.logical_and(~final_crmask, cr_mask) ncosmics = np.count_nonzero(crmask_new) final_crmask = np.logical_or(final_crmask, cr_mask) ncosmics_tot += ncosmics log.info('Iteration {0}: Found {1} cosmic-ray pixels, ' 'Total: {2}'.format(iteration + 1, ncosmics, ncosmics_tot)) if ncosmics == 0: if background is not None: clean_data -= background return clean_data, final_crmask clean_data = _clean_masked_pixels(clean_data, final_crmask, size=5, exclude_mask=mask) if background is not None: clean_data -= background return clean_data, final_crmask
def predict(args): if not Path(args.save_path).exists(): os.mkdir(args.save_path) for id in xrange(70): print('-' * 30) print('Loading model and preprocessing test data...' + str(id)) print('-' * 30) model = dense_rnn_net(args) model.load_weights(args.model_weight) sgd = SGD(lr=1e-2, momentum=0.9, nesterov=True) model.compile(optimizer=sgd, loss=[weighted_crossentropy]) # load data img_test, img_test_header = load(args.data + str(id) + '.nii') img_test -= args.mean # load liver mask mask, mask_header = load(args.liver_path + str(id) + '-ori.nii') mask[mask == 2] = 1 mask = ndimage.binary_dilation(mask, iterations=1).astype(mask.dtype) index = np.where(mask == 1) mini = np.min(index, axis=-1) maxi = np.max(index, axis=-1) print('-' * 30) print('Predicting masks on test data...' + str(id)) print('-' * 30) score1, score2 = predict_tumor_inwindow(model, img_test, 3, mini, maxi, args) K.clear_session() result1 = score1 result2 = score2 result1[result1 >= args.thres_liver] = 1 result1[result1 < args.thres_liver] = 0 result2[result2 >= args.thres_tumor] = 1 result2[result2 < args.thres_tumor] = 0 result1[result2 == 1] = 1 print('-' * 30) print('Postprocessing on mask ...' + str(id)) print('-' * 30) # preserve the largest liver Segmask = result2 box = [] [liver_res, num] = measure.label(result1, return_num=True) region = measure.regionprops(liver_res) for i in range(num): box.append(region[i].area) label_num = box.index(max(box)) + 1 liver_res[liver_res != label_num] = 0 liver_res[liver_res == label_num] = 1 # preserve the largest liver mask = ndimage.binary_dilation(mask, iterations=1).astype(mask.dtype) box = [] [liver_labels, num] = measure.label(mask, return_num=True) region = measure.regionprops(liver_labels) for i in range(num): box.append(region[i].area) label_num = box.index(max(box)) + 1 liver_labels[liver_labels != label_num] = 0 liver_labels[liver_labels == label_num] = 1 liver_labels = ndimage.binary_fill_holes(liver_labels).astype(int) # preserve tumor within ' largest liver' only Segmask = Segmask * liver_labels Segmask = ndimage.binary_fill_holes(Segmask).astype(int) Segmask = np.array(Segmask, dtype='uint8') liver_res = np.array(liver_res, dtype='uint8') liver_res = ndimage.binary_fill_holes(liver_res).astype(int) liver_res[Segmask == 1] = 2 liver_res = np.array(liver_res, dtype='uint8') save(liver_res, args.save_path + 'test-segmentation-' + str(id) + '.nii', img_test_header) del Segmask, liver_labels, mask, region, label_num, liver_res
out_cut[np.nonzero(out_cut <= threshold)] = 0. out_cut[np.nonzero(out_cut > threshold)] = 1. out_cut, nr_objects = ndimage.label(out_cut) for ii in range(1, nr_objects + 1): if (out_cut[out_cut == ii].sum() / ii) < SMALL_OBJ_THRESHOLD: out_cut[np.nonzero(out_cut == ii)] = 0. out_cut[np.nonzero(out_cut != 0)] = 1. ## fill out_cut = ndimage.binary_fill_holes(out_cut).astype( out_cut.dtype) out_cut = ndimage.binary_dilation( out_cut, iterations=2).astype(out_cut.dtype) postproc = '_fill_dilation2' imsave(model_name + '_out_all/' + image_id + '.png', out_cut) rle = mask_to_rle(out_cut, width, height) sublist.append([image_id, rle]) else: rle = " -1" sublist.append([image_id, rle]) else: rle = " -1" sublist.append([image_id, rle]) submission_df = pd.DataFrame(sublist, columns=sample_df.columns.values)
def generate_mask(hdata, mask_type, cstk, pos, z, new_mask, iteration, md): if new_mask == False: iteration = 1 if iteration == 0: if mask_type == False: mask = cstk[:, :, 0] mask = mask == 0 elif mask_type == 'nuclear': img = md.stkread(Position=pos, Zindex=z, Channel='DeepBlue') thresh = threshold_local(img[:, :, 0], 325, offset=0) binary = img[:, :, 0] > thresh erode = ndimage.binary_erosion(binary) remove = remove_small_objects(erode, min_size=500) fill = ndimage.binary_fill_holes(remove) dialate = ndimage.binary_dilation(fill, iterations=50) mask = ndimage.binary_fill_holes(dialate) hdata.add_and_save_data(mask, pos, z, 'mask') elif mask_type == 'spots': stk = copy.copy(cstk) img = np.max(stk, axis=2) blurr = ndimage.gaussian_filter(img, 7) thresh = np.percentile(blurr, 90) binary = blurr > thresh fill = ndimage.binary_dilation(binary, iterations=2) remove = remove_small_objects(fill, min_size=10000) fill = ndimage.binary_dilation(remove, iterations=20) mask = ndimage.binary_fill_holes(fill) hdata.add_and_save_data(mask, pos, z, 'mask') else: if mask_type == False: mask = cstk[:, :, 0] mask = mask == 0 elif mask_type == 'nuclear': if os.path.exists( os.path.join( hdata.base_path, hdata.generate_fname(pos, z, 'mask', sep="_z_"))): mask = hdata.load_data(pos, z, 'mask') mask = mask == 1 else: img = md.stkread(Position=pos, Zindex=z, Channel='DeepBlue') thresh = threshold_local(img[:, :, 0], 325, offset=0) binary = img[:, :, 0] > thresh erode = ndimage.binary_erosion(binary) remove = remove_small_objects(erode, min_size=500) fill = ndimage.binary_fill_holes(remove) dialate = ndimage.binary_dilation(fill, iterations=50) mask = ndimage.binary_fill_holes(dialate) hdata.add_and_save_data(mask, pos, z, 'mask') elif mask_type == 'spots': if os.path.exists( os.path.join( hdata.base_path, hdata.generate_fname(pos, z, 'mask', sep="_z_"))): mask = hdata.load_data(pos, z, 'mask') mask = mask == 1 else: stk = copy.copy(cstk) img = np.max(stk, axis=2) blurr = ndimage.gaussian_filter(img, 7) thresh = np.percentile(blurr, 90) binary = blurr > thresh fill = ndimage.binary_dilation(binary, iterations=2) remove = remove_small_objects(fill, min_size=10000) fill = ndimage.binary_dilation(remove, iterations=20) mask = ndimage.binary_fill_holes(fill) hdata.add_and_save_data(mask, pos, z, 'mask') return mask
def shapeawewm(mask, sigma): diststeps = 10000 mask = mask.astype('float') wc = balancewm(mask) binimage = (mask == 1).astype('float') cells, cellscount = ndimage.measurements.label(binimage) chull = np.zeros_like(mask) # convex hull of each object for ci in range(1, cellscount + 1): I = (cells == ci).astype('float') R = convex_hull_image(I) - I R = ndimage.binary_opening(R, structure=np.ones( (3, 3))).astype('float') R = ndimage.binary_dilation(R, structure=np.ones( (3, 3))).astype('float') chull += R # distance transform to object skeleton skcells = thin(binimage) dtcells = ndimage.distance_transform_edt(skcells != 1) border = binimage - ndimage.binary_erosion( input=(binimage), structure=np.ones( (3, 3)), iterations=1).astype('float') tau = np.max(dtcells[border == 1]) + 0.1 dtcells = np.abs(1 - dtcells * border / tau) * border # distance transform to convex hull skeleton skchull = thin(chull) dtchull = ndimage.distance_transform_edt(skchull != 1) border = chull - ndimage.binary_erosion( input=(chull), structure=np.ones((3, 3)), iterations=1).astype('float') dtchull = np.abs(1 - dtchull * border / tau) * border # maximum border saw = np.concatenate( (dtcells[:, :, np.newaxis], dtchull[:, :, np.newaxis]), 2) saw = np.max(saw, 2) saw /= np.max(saw) # propagate contour values inside the objects prop = binimage + chull prop[prop > 1] = 1 prop = ndimage.binary_erosion(input=(prop), structure=np.ones((3, 3)), iterations=1).astype('float') current_saw = saw for i in range(20): tprop = ndimage.binary_erosion(input=(prop), structure=np.ones((3, 3)), iterations=1).astype('float') border = prop - tprop prop = tprop x1, y1 = np.where(border != 0) x2, y2 = np.where(current_saw != 0) if x1.size == 0 or x2.size == 0: break tsaw = np.zeros_like(saw) for a in range(0, x1.size, diststeps): minl = np.min(np.array([diststeps + a - 1, x1.size - 1])) + 1 dis = cdist( np.vstack((x2, y2)).transpose(), np.vstack((x1[a:minl], y1[a:minl])).transpose()) ind = np.argmin(dis, axis=0) tsaw[x1[a:minl], y1[a:minl]] = current_saw[x2[ind], y2[ind]] saw = np.concatenate((saw[:, :, np.newaxis], tsaw[:, :, np.newaxis]), 2) saw = np.max(saw, 2) saw = ndimage.filters.gaussian_filter(saw, sigma) saw /= np.max(saw) current_saw = saw * (border != 0).astype('float') saw = saw + wc + 1 return saw