def FilterMaxima(self, Minimum=None): if Minimum == None: Minimum = self.ManipulatedData.min() Seed = self.ManipulatedData - Minimum Mask = self.ManipulatedData self.Dilated = reconstruction(Seed, Mask, method="dilation") self.ManipulatedData = self.ManipulatedData - self.Dilated
def detectOpticDisc(image): kernel = octagon(10, 10) thresh = threshold_otsu(image[:,:,1]) binary = image > thresh print binary.dtype luminance = convertToHLS(image)[:,:,2] t = threshold_otsu(luminance) t = erosion(luminance, kernel) labels = segmentation.slic(image[:,:,1], n_segments = 3) out = color.label2rgb(labels, image[:,:,1], kind='avg') skio.imshow(out) x, y = computeCentroid(t) print x, y rows, cols, _ = image.shape p1 = closing(image[:,:,1],kernel) p2 = opening(p1, kernel) p3 = reconstruction(p2, p1, 'dilation') p3 = p3.astype(np.uint8) #g = dilation(p3, kernel)-erosion(p3, kernel) #g = rank.gradient(p3, disk(5)) g = cv2.morphologyEx(p3, cv2.MORPH_GRADIENT, kernel) #markers = rank.gradient(p3, disk(5)) < 10 markers = drawCircle(rows, cols, x, y, 85) #markers = ndimage.label(markers)[0] #skio.imshow(markers) g = g.astype(np.uint8) #g = cv2.cvtColor(g, cv2.COLOR_GRAY2RGB) w = watershed(g, markers) print np.max(w), np.min(w) w = w.astype(np.uint8) #skio.imshow(w) return w
def dilation(data): """ Use dilation to define the background. Not working too well... """ image = gaussian_filter(data, 1) h = 1 seed = np.copy(image) - h mask = image dilated = reconstruction(seed, mask, method='dilation') dilated = data - dilated fileIO.writeFITS(dilated, 'dilation.fits', int=False) #plot the image fig = plt.figure(figsize=(18, 10)) ax1 = fig.add_subplot(121) ax2 = fig.add_subplot(122) ax1.set_title('Data') ax2.set_title('Background') im1 = ax1.imshow(np.log10(data), origin='lower', vmin=2., vmax=3.5, interpolation='none') im2 = ax2.imshow(dilated, origin='lower', interpolation='none') c1 = plt.colorbar(im1, ax=ax1, orientation='horizontal') c2 = plt.colorbar(im2, ax=ax2, orientation='horizontal') c1.set_label('$\log_{10}$(Counts [ADU])') c2.set_label('Dilation') plt.savefig('dilation.png') plt.close() return dilated.ravel()
def imfill(image): """ Replicates the imfill function available within MATLAB. Based on the example provided in http://scikit-image.org/docs/dev/auto_examples/plot_holes_and_peaks.html#example-plot-holes-and-peaks-py. :param image): A 2D numpy array containing "holes". Darker pixels surrounded by brighter pixels. :return: A 2D numpy array of type Float with the "holes" from image filled. """ seed = image.copy() # Define seed points and the start points for the erosion process. seed[1:-1, 1:-1] = image.max() # Define the mask; Probably unneeded. mask = image # Fill the holes filled = morphology.reconstruction(seed, mask, method='erosion') return filled
def get_stomata(max_proj_image, min_obj_size=200, max_obj_size=1000): """Performs image segmentation from a max_proj_image. Disposes of objects in range min_obj_size to max_obj_size :param max_proj_image: the maximum projection image :type max_proj_image: numpy.ndarray, uint16 :param min_obj_size: minimum size of object to keep :type min_obj_size: int :param max_obj_size: maximum size of object to keep :type max_obj_size: int :returns: list of [ [coordinates of kept objects - list of slice objects], binary object image - numpy.ndarray, labelled object image - numpy.ndarray ] """ # pore_margin = 10 # max_obj_size = 1000 # min_obj_size = 200 # for prop, value in segment_options: # if prop == 'pore_margin': # pore_margin = value # if prop == 'max_obj_size': # max_obj_size = value # if prop == 'min_obj_size': # min_obj_size = value # # print(pore_margin) # print(max_obj_size) # print(min_obj_size) #rescale_min = 50 #rescale_max= 100 #rescaled = exposure.rescale_intensity(max_proj_image, in_range=(rescale_min,rescale_max)) rescaled = max_proj_image seed = np.copy(rescaled) seed[1:-1, 1:-1] = rescaled.max() #mask = rescaled #if gamma != None: # rescaled = exposure.adjust_gamma(max_proj_image, gamma) #filled = reconstruction(seed, mask, method='erosion') closed = dilation(rescaled) seed = np.copy(closed) seed[1:-1, 1:-1] = closed.max() mask = closed filled = reconstruction(seed, mask, method='erosion') label_objects, nb_labels = ndimage.label(filled) sizes = np.bincount(label_objects.ravel()) mask_sizes = sizes mask_sizes = (sizes > min_obj_size) & (sizes < max_obj_size) #mask_sizes = (sizes > 200) & (sizes < 1000) mask_sizes[0] = 0 big_objs = mask_sizes[label_objects] stomata, _ = ndimage.label(big_objs) obj_slices = ndimage.find_objects(stomata) return [obj_slices, big_objs, stomata]
def morpho_rec2(self, img, size=10): # internal gradient of the cells: se = morphology.diamond(size) dil = morphology.dilation(img, se) rec = morphology.reconstruction(dil, img, method='erosion').astype(np.dtype('uint8')) return rec
def h_minima(self, img, h): img_shift = img.copy() img_shift[img_shift >= 255 - h] = 255-h img_shift = img_shift + h rec = morphology.reconstruction(img_shift, img, method='erosion').astype(np.dtype('uint8')) diff = rec - img return diff
def bin_to_color(RGB_image, bin_image, feat_vec, marge=None, do_label=True): bin_image_copy = bin_image.copy() res = np.zeros(shape=(bin_image.shape[0], bin_image.shape[1], 3)) if marge is not None: seed = np.zeros_like(bin_image_copy) seed[marge:-marge, marge:-marge] = 1 mask = bin_image_copy.copy() mask[ mask > 0 ] = 1 mask[marge:-marge, marge:-marge] = 1 reconstructed = reconstruction(seed, mask, 'dilation') bin_image_copy[reconstructed == 0] = 0 if do_label: bin_image_copy = label(bin_image_copy) if len(np.unique(bin_image_copy)) != 2: if len(np.unique(bin_image_copy)) == 1: if 0 in bin_image_copy: print "Return blank matrix." return bin_image_copy else: print "Error, must give a bin image." RegProp = regionprops(bin_image_copy) for i in range(len(RegProp)): mini_reg = RegProp[i] bin_image_copy[mini_reg.coords] = feat_vec[i] return bin_image_copy
def bin_analyser(RGB_image, bin_image, list_feature, marge=None, pandas_table=False, do_label=True): bin_image_copy = bin_image.copy() p = 0 for feat in list_feature: p += feat.size if marge is not None and marge != 0: seed = np.zeros_like(bin_image_copy) seed[marge:-marge, marge:-marge] = 1 mask = bin_image_copy.copy() mask[ mask > 0 ] = 1 mask[marge:-marge, marge:-marge] = 1 reconstructed = reconstruction(seed, mask, 'dilation') bin_image_copy[reconstructed == 0] = 0 if do_label: bin_image_copy = label(bin_image_copy) if len(np.unique(bin_image_copy)) != 2: if len(np.unique(bin_image_copy)) == 1: if 0 in bin_image_copy: print "Return blank matrix. Change this shit" white_npy = np.zeros(shape=(1, p)) if not pandas_table: return white_npy else: names = GetNames(list_feature) return pd.DataFrame(white_npy, columns=names) else: print "Error, must give a bin image." GrowRegion_N = NeededGrownRegion(list_feature) img = {0: bin_image_copy} RegionProp = {0: regionprops(bin_image_copy)} for val in GrowRegion_N: if val != 0: img[val] = GrowRegion(bin_image_copy, val) RegionProp[val] = regionprops(img[val]) n = len(RegionProp[0]) TABLE = np.zeros(shape=(n,p)) for i in range(n): offset_ALL = 0 for j, feat in enumerate(list_feature): tmp_regionprop = RegionProp[feat._return_n_extension()][i] off_tmp = feat.size TABLE[i, (j + offset_ALL):(j + offset_ALL + off_tmp)] = feat._apply_region(tmp_regionprop ,RGB_image) offset_ALL += feat.size - 1 if pandas_table: names = GetNames(list_feature) return pd.DataFrame(TABLE, columns=names) else: return TABLE
def homogenize(self, img): img_sub = img.astype(np.dtype('float')) - self.settings.homogenize_settings['h'] img_sub[img_sub<0] = 0 img_sub = img_sub.astype(img.dtype) # seed and then mask print 'img: ', np.min(img), np.max(img) print 'img_sub: ', np.min(img_sub), np.max(img_sub) temp = morphology.reconstruction(img_sub, img) res = img - temp.astype(img.dtype) return res
def shadow_morphology(image): image_shape = image.shape _image = np.zeros((image_shape[0], image_shape[1])) _image = image seed = np.copy(_image) seed[1:-1, 1:-1] = image.max() mask = _image filled = reconstruction(seed, mask, method='erosion') result = filled - _image return result
def local_maxima(self, img, h=1): img_sub = img.astype(np.dtype('float')) - h img_sub[img_sub<0] = 0 img_sub = img_sub.astype(img.dtype) # seed and then mask temp = morphology.reconstruction(img_sub, img) res = img - temp.astype(img.dtype) res[res>0] = 255 res = res.astype(np.dtype('uint8')) return res
def temblob(image,ind): """ Laplacian of gaussian blob detection for TEM images """ org = image[4:-256,4:-4] with warnings.catch_warnings(): warnings.simplefilter("ignore") warnings.warn("user", UserWarning) igray = img_as_ubyte(rgb2gray(org)) iinv = np.invert(igray) igaus = img_as_float(iinv) igaus = gaussian_filter(igaus, 1) h = 0.5 sd = igaus - h msk = igaus dilat = reconstruction(sd, msk, method='dilation') hdome = igaus - dilat if ind == 'AgNP': kwargs = {} kwargs['threshold'] = 0.01 kwargs['overlap'] = 0.4 kwargs['min_sigma'] = 25 kwargs['max_sigma'] = 50 kwargs['num_sigma'] = 25 calib = 500/(969-26) elif ind == 'AuNP': kwargs = {} kwargs['threshold'] = 0.01 kwargs['overlap'] = 0.4 kwargs['min_sigma'] = 18 kwargs['max_sigma'] = 23 kwargs['num_sigma'] = 5 calib = 200/(777-23) else: warnmsg='Unable to identify keyword: {:}'.format(ind) warnings.warn(warnmsg, UserWarning) blobs = blob_log(hdome, **kwargs) diam = 2*sqrt(2)*blobs[:,-1] npdiam = [ind] npdiam.extend(calib*diam) return(npdiam)
def change_color(table, bin, color_vec, indice, res): x_cent, y_cent = table[indice, 3:5] X, Y = int(x_cent), int(y_cent) only_cell = np.zeros_like(bin) if bin[X, Y] != 1: X, Y = find_closest_cc(X, Y, bin) only_cell[X, Y] = 1 only_cell = reconstruction(only_cell, bin) x, y = np.where(only_cell == 1) res[x, y] = color_vec
def bg_sub_signPreserveNorm(imageFile, path, extent, extension): vec = signPreserveNorm(imageFile, path, extent, extension) image = np.reshape(vec, (20,20), order="F") image = gaussian_filter(image, 1) seed = np.copy(image) seed[1:-1, 1:-1] = image.min() mask = image dilated = reconstruction(seed, mask, method='dilation') return np.ravel(image - dilated, order="F")
def opening_by_reconstruction(img, se): diffImg = True orig_img = img.copy() last_rec = img.copy() while diffImg: er_img = morphology.erosion(img, se) img = morphology.reconstruction(er_img, orig_img) if np.array_equal(last_rec, img): diffImg = False else: last_rec = img.copy() return last_rec
def HreconstructionErosion(prob_img, h): def making_top_mask(x, lamb=h): return min(255, x + lamb) f = np.vectorize(making_top_mask) shift_prob_img = f(prob_img) seed = shift_prob_img mask = prob_img recons = reconstruction( seed, mask, method='erosion').astype(np.dtype('ubyte')) return recons
def ifcb_segment(img): Y = img_as_float(img) # step 1. local variance Yv = rescale_intensity(generic_filter(Y, np.var, footprint=disk(3))) # step 2. threshold local variance, aggressively Ye = Yv > (threshold_otsu(Yv) / 2.) # step 3. dark areas Yt = Y < threshold_otsu(Y) thin_blob = Ye | Yt # step 4. morphological reconstruction seed = np.copy(thin_blob) seed[1:-1,1:-1] = 1 four=np.disk(1).astype(np.bool) return reconstruction(seed,thin_blob,method='erosion',selem=four)
def analyze_image(): global sin_par, dipole, multiple, contours # Change to the gray image img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Adaptive Thresholding block_size = 201 img_binary = threshold_adaptive(img_gray, block_size, offset=7) img_binary = img_binary.astype(dtype='uint8')*255 # Remove the salt and pepper noise img_binary = cv2.medianBlur(img_binary,7) # Fill the hole img_binary = 255-img_binary # Change the white/black for filling the hole seed = np.copy(img_binary) seed[1:-1, 1:-1] = img_binary.max() mask = img_binary filled = reconstruction(seed, mask, method='erosion') filled = filled.astype(dtype = 'uint8') # Find the contours # For terminal use # image, contours, hierarchy = cv2.findContours(filled, # cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) # For Anaconda use hierarchy, contours, hierarchy = cv2.findContours(filled, cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) # Calculate the area (= len) of each contour area = np.zeros(len(contours)) area_contour = np.zeros(len(contours)) for kk in range(0,len(contours)): area[kk] = len(contours[kk]) area_contour[kk] = cv2.contourArea(contours[kk]) single = np.where((area_contour>200)&(area_contour<=624))[0] dipole = np.where((area_contour>624)&(area_contour<=936))[0] multiple = np.where(area_contour>936)[0] # Analyze the single particle sin_par = analyze_single(single,filled) # Draw the contours refreshFigure_contours(img,sin_par,dipole,multiple,contours)
def remove_bg(args): """ Remove background from reconstructions. """ data, args, ind_start, ind_end = args for m in range(ind_end-ind_start): img = data[m, :, :] # first remove background. seed = np.copy(img) seed[1:-1, 1:-1] = img.min() img -= reconstruction(seed, img, method='dilation') data[m, :, :] = img return ind_start, ind_end, data
def subtract_background(img): """ Subtract background""" # Convert to float: Important for subtraction later which won't work with uint8 """ image 1 """ image = gaussian_filter(image, 1) seed = np.copy(image) seed[1:-1, 1:-1] = image.min() mask = image dilated = reconstruction(seed, mask, method='dilation') img_background_subtracted = image - dilated return(img_background_subtracted)
def shadow_morphology_land(image, csl_nir=csl_nir): image_shape = image.shape _image = np.zeros((image_shape[0]+2, image_shape[1]+2)) _image[1:-1, 1:-1] = image print(image_shape, _image.shape) seed = np.copy(_image) seed[1:-1, 1:-1] = image.max() nir_low, nir_high = utils.calculate_percentile(csl_nir, 17.5), utils.calculate_percentile(csl_nir, 82.5) print(image.max(), image.mean(), nir_low, image.min()) seed[0:1, :] = image.max() #image.mean() # nir_low seed[-1:, :] = image.max() #image.mean() # nir_low seed[:, 0:1] = image.max() #image.mean() # nir_low seed[:, -1:] = image.max() #image.mean() # nir_low mask = _image filled = reconstruction(seed, mask, method='erosion') result = filled - _image return result[1:-1, 1:-1] #, result, seed, mask
def fillholes(img, selem=None): """ Fill holes in the grayscale input image using specified structuring element. args: img (ndarray): input image kwargs: selem (ndarray): structuring element (default = disk of radius 50) returns: ndarray: output image """ if selem is None: selem = morphology.disk(50) seed = filters.rank.maximum(img, selem) return morphology.reconstruction(seed, img, method='erosion')
def create_word_mask(roi, threshold=None, rel_height=0.5): if threshold is None: threshold = threshold_otsu(roi) # binarize bw = roi > threshold # remove small objects lbl, _ = ndi.label(bw) sizes = np.bincount(lbl.ravel()) mask_sizes = sizes > 20 mask_sizes[0] = 0 bwc = mask_sizes[lbl] # dilate bwcd = ndi.binary_dilation(bwc, diamond(1)) # morphological reconstruction from the center part # to make sure that the mask contains that every considered object # is in fact anchored there x = bwc.sum(0) x[x < x.max() * 0.1] = 0 nzx = x.nonzero() x_lb = nzx[0].min() x_ub = nzx[0].max() y = bwc.sum(1) y[y < y.max() * rel_height] = 0 nzy = y.nonzero() y_lb = nzy[0].min() y_ub = nzy[0].max() if y_lb == y_ub: y_lb -= 3 y_ub += 3 seed = np.zeros(bwcd.shape, dtype=bool) seed[y_lb:y_ub, x_lb:x_ub] = True seed &= bwcd rec = reconstruction(seed, bwcd) return np.array(rec, dtype=bool)
def remove_spurs(mask, min_distance=9): ''' Remove spurious mask features with reconstruction. ''' # Distance transform of the mask dist_trans = nd.distance_transform_edt(mask) # We don't want to return local maxima within the minimum distance # Use reconstruction to remove. seed = dist_trans + min_distance reconst = mo.reconstruction(seed, dist_trans, method='erosion') - \ min_distance if CV2_FLAG: return cv2.morphologyEx((reconst > 0).astype("uint8"), cv2.MORPH_DILATE, mo.disk(min_distance).astype("uint8")).astype(bool) else: return mo.dilation(reconst > 0, selem=mo.disk(min_distance))
def skeletonize_and_reconstruct(image, dfunc=None, sfunc=None): """ Deconstruct image into skeleton and distance mask. Alter the skeleton and/or distance mask and then reconstruct a new morphology. With no altering functions passed, this function will not modify the image. :param image: data :type image: :py:class:`numpy.ndarray` :param dfunc: function that alters distance mask :type dfunc: func :param sfunc: function that alters skeleton :type sfunc: func :return: modified image data :rtype: :py:class:`numpy.ndarray` """ image = (image > image.mean()) # compute skeleton s0 = morphology.skeletonize(image) # compute mask d0 = ndimage.distance_transform_edt(image) # alter the mask if not dfunc is None: d0 = dfunc(image, s0, d0) # alter the skeleton if not sfunc is None: s0 = dfunc(image, s0, d0) # fix d-matrix d0[d0 <= s0] = s0[d0 <= s0] # recomute morphology r0 = morphology.reconstruction(s0, d0) # threshold return r0 > r0.mean()
def clean(image, threshold=0.1, niter=20, selem=np.ones((3,3))): """ Clean the image through a series of steps: 1. normalize 2. erode 3. threshold (trim) 4. measure (connected components) Parameters ---------- :image, 2D nd.array: 2D array of intensity data :threshold, float or iterable of floats: values to use for identifying peaks. All values should be in the range (0, 1), exclusive. If a single float is specified, *niter* should be specified. :niter, int: Number of times *threshold* should be applied. Default: 20. This has no effect if *threshold* is an iterable. :selem, 2D array: mask to use for the erosion step Returns ------- The cleaned image: a 2D nd.array the same size/shape as the original. """ filtered = normalized(image) try: niter = len(threshold) except TypeError: threshold = niter*[threshold] for trim in threshold: # erosion seed = np.copy(filtered) seed[:-1, :-1] = filtered.max() filtered = reconstruction(seed, filtered, method='erosion', selem=selem) # threshold filtered -= trim # identify the remaining connected regions and normalize each separately labelMasks = threshold_masks(filtered, 0.0, direction='gt') for m in labelMasks: filtered[m] = normalized(filtered[m]) return filtered
def show_eyeballs(work_im): work_im = invert(work_im) work_im = gaussian_filter(work_im, 1.2) seed = np.copy(work_im) seed[1:-1, 1:-1] = work_im.min() mask = work_im dilated = reconstruction(seed, mask, method='dilation') image = work_im - dilated blobs_dog = None try: blobs_dog = blob_dog(image, min_sigma=5, max_sigma=10, threshold=.6) except: return None if blobs_dog.shape[0] == 0 or blobs_dog.shape[0] == 0: return None blobs_dog[:, 2] = blobs_dog[:, 2] * sqrt(2) return blobs_dog
def remove_background(args): """ Remove background from reconstructed data. We use morphological reconstruction to create a background image, which we can subtract from the original image to isolate bright features. Parameters ---------- data : ndarray, float32 3-D reconstructed data with dimensions: [slices, pixels, pixels] Returns ------- output : ndarray Background removed data. References ---------- - `http://scikit-image.org/docs/dev/auto_examples/plot_regional_maxima.html \ <http://scikit-image.org/docs/dev/auto_examples/plot_regional_maxima.html>`_ """ # Arguments passed by multi-processing wrapper ind, dshape, inputs = args # Function inputs data = mp.tonumpyarray(mp.shared_arr, dshape) # shared-array for m in ind: img = data[m, :, :] # first remove background. seed = np.copy(img) seed[1:-1, 1:-1] = img.min() img -= reconstruction(seed, img, method='dilation') data[m, :, :] = img
def autemblob(image): """ Laplacian of gaussian blob detection for TEM images """ org = image[4:-256,4:-4] with warnings.catch_warnings(): warnings.simplefilter("ignore") warnings.warn("user", UserWarning) igray = img_as_ubyte(rgb2gray(org)) iinv = np.invert(igray) igaus = img_as_float(iinv) igaus = gaussian_filter(igaus, 1) h = 0.5 sd = igaus - h msk = igaus dilat = reconstruction(sd, msk, method='dilation') hdome = igaus - dilat kwargs = {} kwargs['threshold'] = 0.01 kwargs['overlap'] = 0.4 kwargs['min_sigma'] = 18 kwargs['max_sigma'] = 30 kwargs['num_sigma'] = 12 calib = 200/(777-23) blobs = blob_log(hdome, **kwargs) diam = 2*sqrt(2)*blobs[:,-1] npdiam = calib*diam return(npdiam)
def openrec(I, selem=m.disk(1)): B = m.erosion(I, selem=selem) F = m.reconstruction(B, I) return F
dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 0) ret, sure_fg = cv2.threshold(dist_transform, 0.04 * dist_transform.max(), 255, 0) labels_ws = watershed(-dist_transform, C_Core, mask=C_binary8) C_binary8_second = np.where(labels_ws > 0, C_binary8, 0) C_flag = np.where(labels_ws == 0, 0, labels_ws) #% #% Second round - divide the previous mask into 2 blobs ################################################################## seed = np.copy(C_binary8_second) seed[1:-1, 1:-1] = C_binary8_second.max() mask = C_binary8_second # fill all holes the do distance transform C_binary8_filled = reconstruction(seed, mask, method='erosion') C_binary8_filled8 = C_binary8_filled.astype(np.uint8) dist_transform_second = cv2.distanceTransform(C_binary8_filled8, cv2.DIST_L2, 0) # Maximum them find maxima maximum_fil_result = ndimage.maximum_filter(dist_transform_second, size=1) min_distance_val = 1 flag = 0 # maximum_fil_result = maximum_fil_result[I_idx[0]-200:I_idx[0]+200,I_idx[1]-200:I_idx[1]+200] while flag == 0: maximum_coordinates = peak_local_max(maximum_fil_result, min_distance=min_distance_val, indices=True) min_distance_val += 1
def THR(ivtNV, kernel, oroNV=None, high_terrain=600, verbose=True): '''Perform THR filtering process on 3d data Args: ivtNV (NCVAR): 3D or 4D input IVT data, with dimensions (time, lat, lon) or (time, level, lat, lon). kernel (list or tuple): list/tuple of integers specifying the shape of the kernel/structuring element used in the gray erosion process. Keyword Args: oroNV (NCVAR or None): 2D array, surface orographic data in meters. This optional surface height info is used to perform a separate reconstruction computation for areas with high elevations, and the results can be used to enhance the continent-penetration ability of landfalling ARs. Sensitivity in landfalling ARs is enhanced, other areas are not affected. Needs to have compatible (lat, lon) shape as <ivt>. If None, omit this process and treat areas with different heights all equally. New in v2.0. high_terrain (float): minimum orographic height (in m) to define as high terrain area, within which a separate reconstruction is performed. Only used if <oroNV> is not None. New in v2.0. verbose (bool): print some messages or not. Returns: ivtNV (NCVAR): 3D or 4D array, input <ivt>. ivtrecNV (NCVAR): 3D or 4D array, the reconstruction component from the THR process. ivtanoNV (NCVAR): 3D or 4D array, the difference between input <ivt> and <ivtrecNV>. ''' ivt=ivtNV.data ndim=np.ndim(ivt) ivt=np.squeeze(ivt) #-------------------3d ellipsoid------------------- ele=funcs.get3DEllipse(*kernel) #################### use a cube to speed up ############## # empirical if kernel[0]>=16 or kernel[1]>=6: ele=np.ones(ele.shape) ########################################################## # reconstruction element: a 6-connectivity element rec_ele=np.zeros([3,3,3]) rec_ele[0,1,1]=1 rec_ele[1,:,1]=1 rec_ele[1,1,:]=1 rec_ele[2,1,1]=1 if verbose: print('\n# <THR>: Computing erosion ...') lm=morphology.erosion(ivt, selem=ele) if verbose: print('\n# <THR>: Computing reconstruction ...') ivtrec=morphology.reconstruction(lm, ivt, method='dilation', selem=rec_ele) # perform an extra reconstruction over land if oroNV is not None: orodata=np.squeeze(oroNV.data) oro_rs=np.where(orodata>=high_terrain, 1, 0) oro_rs=oro_rs[None,...] oro_rs=np.repeat(oro_rs, len(ivt), axis=0) ivtrec_oro=morphology.reconstruction(lm*oro_rs, ivt, method='dilation', selem=rec_ele) ivtano=np.ma.maximum(ivt-ivtrec, (ivt-ivtrec_oro)*oro_rs) else: ivtano=ivt-ivtrec ivtrec=ivt-ivtano axislist=ivtNV.axislist #---------If inputs are 4d, also return 4d--------- if ndim==4: ivt=ivt[:,None,...] ivtrec=ivtrec[:,None,...] ivtano=ivtano[:,None,...] ivtrecNV=funcs.NCVAR(ivtrec, 'ivt_rec', axislist, {'name': 'ivt_rec', 'long_name': '%s, THR reconstruction' %(getattr(ivt, 'long_name', '')), 'standard_name': '%s, THR reconstruction' %(getattr(ivt, 'long_name', '')), 'units': getattr(ivt, 'units', '')}) ivtanoNV=funcs.NCVAR(ivtano, 'ivt_ano', axislist, {'name': 'ivt_ano', 'long_name': '%s, THR anomaly' %(getattr(ivt, 'long_name', '')), 'standard_name': '%s, THR anomaly' %(getattr(ivt, 'long_name', '')), 'units': getattr(ivt, 'units', '')}) return ivtNV, ivtrecNV, ivtanoNV
def fill_image(in_image): seed = np.copy(in_image) seed[1:-1, 1:-1] = in_image.max() mask = in_image return reconstruction(seed, mask, method='erosion')
def avg_over_trials(self): self.shaped = [] single = int(self.period * self.framerate) self.shaped = np.reshape(self.divided, [ self.nrepetitions, single, self.imageData.shape[1], self.imageData.shape[2] ]) self.shapedavg = np.mean(self.shaped[1:], axis=0) pg.image(self.shapedavg) self.stddev = np.std(self.shaped[1:], axis=0) pg.image(np.log(self.stddev)) image77 = np.copy(self.shapedavg) image77[np.where(np.log(self.stddev) < -5)] = 0 # pg.image(image77, 'image77') # pg.image(np.sum(image77[1:16],axis=0),title='stim 1 max locus') # pg.image(np.sum(image77[121:136],axis=0),title='stim 2 max locus') image1 = np.sum(image77[1:21], axis=0) seed1 = np.copy(image1) seed1[1:-1, 1:-1] = image1.min() rec1 = reconstruction(seed1, image1, method='dilation') image2 = np.sum(image77[121:141], axis=0) seed2 = np.copy(image2) seed2[1:-1, 1:-1] = image2.min() rec2 = reconstruction(seed2, image2, method='dilation') # print 'shape of shapedavg, ', np.shape(self.shapedavg) self.filtshapedavg = scipy.ndimage.gaussian_filter(self.shapedavg, sigma=[0, 3, 3], order=0, mode='reflect', truncate=4.0) stim1 = self.shapedavg[1:16] stim2 = self.shapedavg[121:136] mpl.figure(1) mpl.imshow(np.amax(self.shapedavg, axis=0), cmap=matplotlib.cm.gray) top1 = np.mean(np.sum(stim1, axis=0)) + 3 * np.std(np.sum(stim1, axis=0)) top2 = np.mean(np.sum(stim2, axis=0)) + 3 * np.std(np.sum(stim2, axis=0)) print 'top: ', (top1, top2) loci1 = np.where(np.sum(stim1, axis=0) > top1) loci2 = np.where(np.sum(stim2, axis=0) > top2) # print 'loci1 dim:', np.shape(loci1) mpl.hold('on') mpl.plot(loci1[1], loci1[0], 'co') mpl.plot(loci2[1], loci2[0], 'rx') # pg.image(self.shapedavg, title = 'average over repetitions') mpl.figure(2) mpl.subplot(2, 3, 1) mpl.imshow(np.amax(self.shapedavg, axis=0), cmap=matplotlib.cm.gray, interpolation='nearest') mpl.colorbar() mpl.title('Average image') mpl.subplot(2, 3, 2) # mpl.imshow(np.amax(self.filtshapedavg,axis=0),cmap=matplotlib.cm.gray,interpolation=None) # mpl.subplot(2,3,4) mpl.imshow(np.amax(stim1, axis=0), cmap=matplotlib.cm.Blues, interpolation=None) mpl.title('Stim 1 (integral over time)') # maxamp = np.amax(stim1) # shstim1.set_clim = (0.0, maxamp) mpl.colorbar() mpl.subplot(2, 3, 3) mpl.imshow(np.amax(stim2, axis=0), cmap=matplotlib.cm.Reds, interpolation=None) mpl.title('Stim 2 (integral over time') mpl.colorbar() mpl.subplot(2, 3, 4) # mpl.hold('on') # mpl.title('Stim1, Stim2 overlaid') mpl.imshow(np.sum(stim1, axis=0), cmap=matplotlib.cm.Blues, interpolation=None) mpl.colorbar() mpl.subplot(2, 3, 5) mpl.imshow(np.sum(stim2, axis=0), cmap=matplotlib.cm.Reds, interpolation=None, alpha=0.5) mpl.colorbar() # image1 = np.sum(stim1,axis=0) # seed1 = np.copy(image1) # seed1[1:-1, 1:-1] = image1.min() # rec1 = reconstruction(seed1,image1,method='dilation') # # mpl.subplot(2,3,6) # # mpl.imshow(image1-rec1,cmap=matplotlib.cm.Blues) # image2 = np.sum(stim2,axis=0) # seed2 = np.copy(image2) # seed2[1:-1, 1:-1] = image2.min() # rec2 = reconstruction(seed2,image2,method='dilation') # # mpl.subplot(2,3,3) # # mpl.imshow(image2-rec2,cmap=matplotlib.cm.Reds) # # mpl.figure(2) # # mpl.hold('on') # mpl.imshow(np.amax(self.shapedavg,axis=0),cmap=matplotlib.cm.gray, interpolation='nearest') # mpl.subplot(2,3,5) # mpl.imshow(image2-rec2,cmap=matplotlib.cm.Reds) # mpl.colorbar() # mpl.subplot(2,3,6) # mpl.imshow(image1-rec1,cmap=matplotlib.cm.Blues) # mpl.colorbar() # mpl.imshow(np.sum(stim1,axis=0),cmap=matplotlib.cm.Blues,interpolation=None) # mpl.imshow(np.sum(stim2,axis=0),cmap=matplotlib.cm.Reds,interpolation=None) # mpl.hold('off') # mpl.figure(3) # loci3 = np.where(image1>.8) # loci4 = np.where(image2>.8) # mpl.imshow(np.amax(self.shapedavg,axis=0),cmap=matplotlib.cm.gray) # mpl.hold('on') # mpl.plot(loci1[1],loci1[0],'ko') # mpl.plot(loci2[1],loci2[0],'rx') # mpl.title('std dev correction') return
def remove_segmented_nuc(image, mask, nuclei_size=2000): """Remove the nuclei we have already segmented in an image. 1) We only keep the segmented nuclei. The missed ones and the background are set to 0 and removed from the original image, using a dilated mask. 2) We reconstruct the missing nuclei by small dilatation. As we used the original image as a mask (the maximum allowed value at each pixel), the background pixels remain unchanged. However, pixels from the missing nuclei are partially reconstructed by the dilatation. This reconstructed image only differs from the original one where the nuclei have been missed. 3) We subtract the reconstructed image from the original one. 4) From the few pixels kept and restored from the missing nuclei, we build a binary mask (dilatation, small object removal). 5) We apply this mask to the original image to get the original pixel intensity of the missing nuclei. 6) We remove pixels with a too low intensity (using Otsu thresholding). Parameters ---------- image : np.ndarray, np.uint Original image with shape (y, x). mask : np.ndarray, Result of the segmentation (with instance differentiation or not). nuclei_size : int Threshold above which we detect a nuclei. Returns ------- unsegmented_nuclei : np.ndarray Image with shape (y, x) and the same dtype of the original image. Nuclei previously detected in the mask are removed. """ # TODO fix the dtype of the mask # TODO start from the original image to manage the potential rescaling # TODO improve the threshold # TODO correct the word dilatation -> dilation # check parameters stack.check_array(image, ndim=2, dtype=[np.uint8, np.uint16]) stack.check_array(mask, ndim=2, dtype=[np.uint8, np.uint16, np.int64, bool]) # cast mask in np.int64 if it is binary if mask.dtype == bool or mask.dtype == np.uint16: mask = mask.astype(np.int64) # store original dtype original_dtype = image.dtype # dilate the mask s = disk(10, bool) dilated_mask = binary_dilation(mask, selem=s) # remove the unsegmented nuclei from the original image diff = image.copy() diff[dilated_mask == 0] = 0 # reconstruct the missing nuclei by dilation s = disk(1) image_reconstructed = reconstruction(diff, image, selem=s) image_reconstructed = image_reconstructed.astype(original_dtype) # substract the reconstructed image from the original one image_filtered = image.copy() image_filtered -= image_reconstructed # build the binary mask for the missing nuclei missing_mask = image_filtered > 0 missing_mask = remove_small_objects(missing_mask, nuclei_size) s = disk(20, bool) missing_mask = binary_dilation(missing_mask, selem=s) # get the original pixel intensity of the unsegmented nuclei unsegmented_nuclei = image.copy() unsegmented_nuclei[missing_mask == 0] = 0 if original_dtype == np.uint8: unsegmented_nuclei[unsegmented_nuclei < 40] = 0 else: unsegmented_nuclei[unsegmented_nuclei < 10000] = 0 return unsegmented_nuclei
def main(): parser=argparse.ArgumentParser(description='Image-file processing to identify total number of droplets', formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument('input_image', type=str, help='input image file') parser.add_argument('template_image', type=str, help='template image file') parser.add_argument('--outfile', type=str, help='output image name. Will default to input_image_processed.png') parser.add_argument('--cutoff_area', type=int, help='rectangle size. only detected rectangles above this size will be displayed') parser.add_argument('--include', action='store_true', dest="include_edge", help='Include edge or boundary or border droplets; incomplete droplets found on the edge of the picture will be included too.') parser.add_argument("--exclude", action="store_false", dest="include_edge", help='Exclude edge or boundary or border droplets; incomplete droplets found on the edge of the picture will be excluded.') parser.add_argument('--threshold', type=float, default=0.6, help='thresholding parameter, tweak from 0 to 1 and visually examine results, depends on the image, default is 0.6') args=parser.parse_args() if args.outfile==None: args.outfile=''.join([path.splitext(args.input_image)[0],"_border_",str(args.include_edge),'_processed']) ## leaving out the file extension; adding it later just before writing out the image file filename=args.input_image rectsize=args.cutoff_area print "...." print "image file being processed..." print "..." from skimage import img_as_float image = io.imread(filename, flatten=True) # conver 3d to 2d image by using flatten=True image = img_as_float(image) #io.imshow(image) ## check the image #io.show() ## check the image from skimage.color import rgb2gray img_gray = rgb2gray(image) #io.imshow(img_gray) ## check the image #io.show() ## check the image image = gaussian_filter(image, 1) seed = np.copy(image) seed[1:-1, 1:-1] = image.min() mask = image dilated = reconstruction(seed, mask, method='dilation') """ fig, (ax0, ax1, ax2) = plt.subplots(nrows=1, ncols=3, figsize=(8, 2.5), sharex=True, sharey=True) ax0.imshow(image, cmap='gray') ax0.set_title('original image') ax0.axis('off') ax0.set_adjustable('box-forced') ax1.imshow(dilated, vmin=image.min(), vmax=image.max(), cmap='gray') ax1.set_title('dilated') ax1.axis('off') ax1.set_adjustable('box-forced') ax2.imshow(image - dilated, cmap='gray') ax2.set_title('image - dilated') ax2.axis('off') ax2.set_adjustable('box-forced') fig.tight_layout() """ print "...." print "background correction..." print "..." h = 0.4 seed = image - h dilated = reconstruction(seed, mask, method='dilation') hdome = image - dilated #io.imshow(hdome) ## check the image #io.show() ## check the image """ fig, (ax0, ax1, ax2) = plt.subplots(nrows=1, ncols=3, figsize=(8, 2.5)) yslice = 197 ax0.plot(mask[yslice], '0.5', label='mask') ax0.plot(seed[yslice], 'k', label='seed') ax0.plot(dilated[yslice], 'r', label='dilated') ax0.set_ylim(-0.2, 2) ax0.set_title('image slice') ax0.set_xticks([]) ax0.legend() ax1.imshow(dilated, vmin=image.min(), vmax=image.max(), cmap='gray') ax1.axhline(yslice, color='r', alpha=0.4) ax1.set_title('dilated') ax1.axis('off') ax2.imshow(hdome, cmap='gray') ax2.axhline(yslice, color='r', alpha=0.4) ax2.set_title('image - dilated') ax2.axis('off') fig.tight_layout() plt.show() """ print "...." print "edge detection..." print "..." im = hdome edges1 = feature.canny(image, sigma=3) edges2 = feature.canny(im, sigma=3) #io.imshow(edges1) ## check the image #io.show() ## check the image #io.imshow(edges2) ## check the image #io.show() ## check the image """ # display results fig, (ax1, ax2, ax3) = plt.subplots(nrows=1, ncols=3, figsize=(8, 3), sharex=True, sharey=True) ax1.imshow(im, cmap=plt.cm.gray) ax1.axis('off') ax1.set_title('Original image', fontsize=10) ax2.imshow(edges1, cmap=plt.cm.gray) ax2.axis('off') ax2.set_title('Canny filter on original image, $\sigma=3$', fontsize=10) ax3.imshow(edges2, cmap=plt.cm.gray) ax3.axis('off') ax3.set_title('Canny filter on background subtracted image, $\sigma=3$', fontsize=10) fig.tight_layout() plt.show() """ # ### check how good are the original and processed images by selecting corresponding image here # image=image #image=edges2 #image=hdome ## apply threshold thresh = threshold_otsu(image) bw = closing(image > thresh, square(2)) #io.imshow(bw) ## check the image #io.show() ## check the image print "... are we including incomplete droplets at the edge of image?..." print ".............................................................", args.include_edge if args.include_edge is False: ## remove artifacts connected to image border cleared = clear_border(bw) ## use this option to avoid the incomplete droplets at boundary/edge of picture frame else: cleared = bw ## use this to use all droplets in the image; even incomplete ones at the boundary/edge of pciture frame #io.imshow(cleared) ## check the image #io.show() ## check the image # label image regions label_image = label(cleared) image_label_overlay = label2rgb(label_image, image=image) #io.imshow(image_label_overlay) ## check the image #io.show() ## check the image fig, ax = plt.subplots(figsize=(10, 6)) #ax.imshow(label_image) #ax.imshow(image_label_overlay) targetimagefile=args.input_image templateimagefile=args.template_image thresholdval=args.threshold beads_count=0 droplet_count=0 outfile=0 for region in regionprops(label_image): # take regions with large enough areas; should correspond to droplets if region.area >= rectsize: # draw rectangle around segmented droplets droplet_count=droplet_count+1 minr, minc, maxr, maxc = region.bbox rect = mpatches.Rectangle((minc, minr), maxc - minc, maxr - minr, fill=False, edgecolor='yellow', linewidth=2) #print region.bbox try: crop_image = image[minr-50:maxr+50, minc-50:maxc+50] ## offset the bounding box in all directions; seems better this way based on visual examination except ValueError: #raised if this subtraction takes it out of bounds pass finally: crop_image = image[minr:maxr, minc:maxc] #io.imshow(crop_image) ## check the image #io.show() ## check the image outfile=outfile+1 ## improve this and instead of passing file names of whole images to the findtemplate function ## pass the cropped image that already exists here ## for now, not changing too drastically on something that works ## slow tweaks ensuring what works functionally well doesnt break down beads=findtemplate(templateimagefile, targetimagefile, region.bbox, outfile, thresholdval) ax.add_patch(rect) ax.set_axis_off() plt.tight_layout() outfile=args.outfile + "_totaldroplets-" + str(droplet_count) + ".png" print "...saving image file...." print outfile print "........................" plt.savefig(outfile) #plt.show() ## activate this if you want to examine how the processed images are turning out and stop/start with different input parameters; key one being --cutoff_area print "...total droplets identified in the image:" print droplet_count print "..."
def join_segments_helper(L, Lines, combinedLines, segments2Join, max_scale, pixelList): mask = Lines length = len(segments2Join) LabelNum = False minX = np.inf minY = np.inf maxX = 0 maxY = 0 for i in range(len(segments2Join)): X = pixelList[segments2Join[i]].coords minX = min(np.amin(X[:, 0]), minX) minY = min(np.amin(X[:, 1]), minY) maxX = max(np.amax(X[:, 0]), maxX) maxY = max(np.amax(X[:, 1]), maxY) # tested CroppedLines = Lines[minY:maxY + 1, minX:maxX + 1] for i in range(length - 1): bw1 = CroppedLines == pixelList[segments2Join[i]].label bw2 = CroppedLines == pixelList[segments2Join[i + 1]].label # TODO talk to baret, no quasi-euclidean transformation avaliable https://www.mathworks.com/help/images/ref/bwdist.html D1 = ndimage.distance_transform_edt(bw1 == 0) D2 = ndimage.distance_transform_edt(bw2 == 0) D = np.round((D1 + D2) * 32) / 32 paths = matlabic_minima(D) paths = skeletonize(paths) tempMask = dilate(paths, ksize=10, i=1) mask = np.full(L.shape, False) mask[minY:maxY + 1, minX:maxX + 1] = tempMask AdjacentIndices = np.unique(combinedLines[mask]) rows_to_remove = np.argwhere(AdjacentIndices == 0) mask_for_removal = np.ones(AdjacentIndices.shape) mask_for_removal[rows_to_remove] = False AdjacentIndices = AdjacentIndices[mask_for_removal.astype(np.bool)] if (len(AdjacentIndices) > 1) or (not np.any(np.logical_and(mask, L))): LabelNum = False else: mask = np.logical_and(mask, np.logical_not(combinedLines)) bw1 = Lines == pixelList[segments2Join[i]].label bw2 = Lines == pixelList[segments2Join[i + 1]].label try: t = reconstruction(np.logical_or(bw1, bw2), np.logical_or( combinedLines == AdjacentIndices, mask), method='dilation') except Exception as e: t = reconstruction(np.logical_or(bw1, bw2), np.logical_or( combinedLines == AdjacentIndices, mask), method='dilation') fitting = approximate_using_piecewise_linear_pca( t.astype(np.int), 1, [], 0) if fitting[0] < 0.8 * max_scale: LabelNum = AdjacentIndices else: LabelNum = False return mask, LabelNum
# Edge detection print("Edge detection") sobel = filters.sobel(Contrast_image) io.imshow(sobel) plt.figure() # Gradient magnitude Sobel_gradient_array = filters.rank.gradient(sobel,disk(1)) # Compute the opening-by-reconstruction (erode) print("Compute the opening-by-reconstruction (erode)") Image_erosion = erosion(Contrast_image,disk(1)) Image_by_reconstruction_opening = reconstruction(Image_erosion,Contrast_image) io.imshow(Image_by_reconstruction_opening) plt.figure() # Compute the closing-by-reconstruction (dilation) print("Compute the closing-by-reconstruction (dilation)") Image_dilation = dilation(Image_by_reconstruction_opening,disk(1)) Image_by_reconstruction_closing = reconstruction(util.invert(Image_dilation),util.invert(Image_by_reconstruction_opening)) Image_by_reconstruction_closing = util.invert(Image_by_reconstruction_closing) io.imshow(Image_by_reconstruction_closing) plt.figure() # Regional max print("REGIONAL MAX")
def extract_maxima_positions(cls, image): seed_min = image - 1 dilated = reconstruction(seed_min, image, method='dilation') cleaned_image = image - dilated return np.where(cleaned_image > 0)[::-1]
Original=sobel(Original) Original=np.copy(Original) #Dilation Dilation= dilation(Original) #show_images([Original,Dilation ],["Original","Dilation"]) #Filling seed = np.copy(Dilation) seed[1:-1,1:-1] = Dilation.max() mask = Dilation filled = reconstruction(seed, mask, method='erosion') filled = filled >0.14 #show_images([filled]) #Erosion filled = np.copy(filled) rosion = erosion(filled) rosion = erosion(rosion) #show_images([filled,rosion])
def place_field(firing_map, **kwargs): ''' Locate place fields on a firing map. Identifies place fields in 2D firing map. Placefields are identified by using an adaptive threshold. The idea is that we start with a peak value as the threshold. Then we gradually decrease the threshold until the field area doesn't change any more or the area explodes (this means the threshold is too low). Parameters ---------- firing_map: np.ndarray or np.ma.MaskedArray smoothed rate map. If supplied as an np.ndarray, it is assumed that the map takes values of np.nan at locations of zero occupancy. If supplied as an np.ma.MaskedArray, it is assumed that the map is masked at locations of zero occupancy Other Parameters ---------------- min_bins: int Fields containing fewer than this many bins will be discarded. Default 9 min_peak: float Fields with a peak firing rate lower than this absolute value will be discarded. Default 1 Hz min_mean: float Fields with a mean firing rate lower than this absolute value will be discarded. Default 0 Hz init_thresh: float Initial threshold to search for fields from. Must be in the range [0, 1]. Default 0.96 search_method: str Peak detection finding method. By default, use `skimage.morphology.local_maxima` Acceptable values are defined in `opexebo.defaults`. Not required if peak_coords are provided peak_coords: array-like List of peak co-ordinates to consider instead of auto detection. [y, x]. Default None Returns ------- fields: list of dict coords: np.ndarray Coordinates of all bins in the firing field peak_coords: np.ndarray Coordinates peak firing rate [y,x] centroid_coords: np.ndarray Coordinates of centroid (decimal) [y,x] area: int Number of bins in firing field. [bins] bbox: tuple Coordinates of bounding box including the firing field (y_min, x_min, y_max, y_max) mean_rate: float mean firing rate [Hz] peak_rate: float peak firing rate [Hz] map: np.ndarray Binary map of arena. Cells inside firing field have value 1, all other cells have value 0 fields_map : np.ndarray labelled integer image (i.e. background = 0, field1 = 1, field2 = 2, etc.) Raises ------ ValueError Invalid input arguments NotImplementedError non-defined peack searching methods Notes -------- BNT.+analyses.placefieldAdaptive https://se.mathworks.com/help/images/understanding-morphological-reconstruction.html Copyright (C) 2018 by Vadim Frolov, (C) 2019 by Simon Ball, Horst Obenhaus ''' ########################################################################## ##### Part 1: Handle inputs # Get keyword arguments min_bins = kwargs.get("min_bins", default.firing_field_min_bins) min_peak = kwargs.get("min_peak", default.firing_field_min_peak) min_mean = kwargs.get("min_mean", default.firing_field_min_mean) init_thresh = kwargs.get("init_thresh", default.initial_search_threshold) search_method = kwargs.get("search_method", default.search_method) peak_coords = kwargs.get("peak_coords", None) debug = kwargs.get("debug", False) if not 0 < init_thresh <= 1: raise ValueError("Keyword 'init_thresh' must be in the range [0, 1]."\ f" You provided {init_thresh}") try: search_method = search_method.lower() except AttributeError: raise ValueError("Keyword 'search_method' is expected to be a string"\ f" You provided a {type(search_method)} ({search_method})") if search_method not in default.all_methods: raise ValueError("Keyword 'search_method' must be left blank or given a"\ f" value from the following list: {default.all_methods}."\ f" You provided '{search_method}'.") global_peak = np.nanmax(firing_map) if np.isnan(global_peak) or global_peak == 0: if debug: print(f"Terminating due to invalid global peak: {global_peak}") return [], np.zeros_like(firing_map) # Construct a mask of bins that the animal never visited (never visited -> true) # This needs to account for multiple input formats. # The standard that I want to push is that firing_map is type MaskedArray # In this case, the cells that an animal never visited have firing_map.mask[cell]=True # while firing_map.data[cell] PROBABLY = 0 # An alternative is the BNT standard, where firing_map is an ndarray # In this case, the cells never visited are firing_map[cell] = np.nan # In either case, we need to get out the following: # finite_firing_map is an ndarray (float) where unvisted cells have a # meaningfully finite value (e.g. zero, or min()) # mask is an ndarray (bool) where unvisited cells are True, all other cells are False if isinstance(firing_map, np.ma.MaskedArray): occupancy_mask = firing_map.mask finite_firing_map = firing_map.data.copy() finite_firing_map[np.isnan(firing_map.data)] = 0 else: occupancy_mask = np.zeros_like(firing_map).astype('bool') occupancy_mask[np.isnan(firing_map)] = True finite_firing_map = firing_map.copy() finite_firing_map[np.isnan(firing_map)] = 0 structured_element = morphology.disk(1) image_eroded = morphology.erosion(finite_firing_map, structured_element) fmap = morphology.reconstruction(image_eroded, finite_firing_map) ########################################################################## ##### Part 2: find local maxima # Based on the user-requested search method, find the co-ordinates of local maxima if peak_coords is None: if search_method == default.search_method: peak_coords = opexebo.general.peak_search(fmap, **kwargs) elif search_method == "sep": #fmap = finite_firing_map peak_coords = opexebo.general.peak_search(fmap, **kwargs) else: raise NotImplementedError("The search method you have requested (%s) is"\ " not yet implemented" % search_method) # obtain value of found peaks found_peaks = finite_firing_map[peak_coords[:, 0], peak_coords[:, 1]] # leave only peaks that satisfy the threshold good_peaks = (found_peaks >= min_peak) peak_coords = peak_coords[good_peaks, :] ########################################################################## ##### Part 3: from local maxima get fields by expanding around maxima max_value = np.max(fmap) # prevent peaks with small values from being detected # SWB - This causes problems where a local peak is next to a cell that the animal never went # As that risks the field becoming the entire null region # Therefore, adding 2nd criterion to avoid adding information where none was actually known. fmap[np.logical_and(fmap < min_peak, fmap > 0.01)] = max_value * 1.5 # this can be confusing, but this variable is just an index for the vector # peak_linear_ind peaks_index = np.arange(len(peak_coords)) fields_map = np.zeros(fmap.shape, dtype=np.integer) field_id = 1 for i, peak_rc in enumerate(peak_coords): # peak_rc == [row, col] # select all peaks except the current one other_fields = peak_coords[peaks_index != i] if other_fields.size > 0: other_fields_linear = np.ravel_multi_index( multi_index=(other_fields[:, 0], other_fields[:, 1]), dims=fmap.shape, order='F') else: other_fields_linear = [] used_th = init_thresh res = _area_change(fmap, occupancy_mask, peak_rc, used_th, used_th - 0.02, other_fields_linear) initial_change = res['acceleration'] area2 = res['area2'] first_pixels = np.nan if np.isnan(initial_change): for j in np.linspace(used_th + 0.01, 1., 4): # Thresholds get higher, area should tend downwards to 1 # (i.e. only including the actual peak) res = _area_change(fmap, occupancy_mask, peak_rc, j, j - 0.01, other_fields_linear) initial_change = res['acceleration'] area1 = res['area1'] area2 = res['area2'] # initial_change is the change from area1 to area 2 # area2>area1 -> initial_change > 1 # area2<area1 -> initial_change < 1 # area 2 is calculated with lower threshold - should usually be larger first_pixels = res['first_pixels'] if not np.isnan(initial_change) and initial_change > 0: # True is both area1 and area2 are valid # Weird conditonal from Vadim - initial change will EITHER: # be greater than zero (can't get a negative area to give negative % change) # OR be NaN (which will always yield false when compared to a number) used_th = j - 0.01 break if np.isnan(initial_change) and not np.isnan(area1): # For the final change pixels = np.unravel_index(first_pixels, fmap.shape, 'F') fmap[pixels] = max_value * 1.5 fields_map[pixels] = field_id field_id = field_id + 1 if np.isnan(initial_change): # failed to extract the field # Do nothing and continue for-loop pass pixel_list = _expand_field(fmap, occupancy_mask, peak_rc, initial_change, area2, other_fields_linear, used_th) if np.any(np.isnan(pixel_list)): _, pixel_list, _ = _area_for_threshold(fmap, occupancy_mask, peak_rc, used_th + 0.01, other_fields_linear) if len(pixel_list) > 0: pixels = np.unravel_index(pixel_list, fmap.shape, 'F') else: pixels = [] fmap[pixels] = max_value * 1.5 fields_map[pixels] = field_id field_id = field_id + 1 ########################################################################## ##### Part 4: Determine which, if any, fields meet filtering criteria regions = measure.regionprops(fields_map) fields = [] fields_map = np.zeros( finite_firing_map.shape) # void it as we can eliminate some fields for region in regions: field_map = finite_firing_map[region.coords[:, 0], region.coords[:, 1]] mean_rate = np.nanmean(field_map) num_bins = len(region.coords) peak_rate = np.nanmax(field_map) peak_relative_index = np.argmax(field_map) peak_coords = region.coords[peak_relative_index, :] if num_bins >= min_bins and mean_rate >= min_mean: field = {} field['coords'] = region.coords field['peak_coords'] = peak_coords field['area'] = region.area field['bbox'] = region.bbox field['centroid_coords'] = region.centroid field['mean_rate'] = mean_rate field['peak_rate'] = peak_rate mask = np.zeros(finite_firing_map.shape) mask[region.coords[:, 0], region.coords[:, 1]] = 1 field['map'] = mask fields.append(field) fields_map[region.coords[:, 0], region.coords[:, 1]] = len(fields) elif debug: # Print out some information about *why* the field failed if num_bins < min_bins: print("Field size too small (%d)" % num_bins) if mean_rate < min_mean: print("Field mean rate too low (%.2f Hz)" % mean_rate) else: # Field too small and debugging information not needed # Do nothing pass #fields_map = np.ma.masked_where(occupancy_mask, fields_map) return (fields, fields_map)
def process_wrapper(self, **kwargs): """ Filtering regional maxima: From scikit image - Filtering regional maxima: Perform a morphological reconstruction of an image. Morphological reconstruction by dilation is similar to basic morphological dilation: high-intensity values will replace nearby low-intensity values. The basic dilation operator, however, uses a structuring element to determine how far a value in the input image can spread. In contrast, reconstruction uses two images: a “seed” image, which specifies the values that spread, and a “mask” image, which gives the maximum allowed value at each pixel. The mask image, like the structuring element, limits the spread of high-intensity values. Reconstruction by erosion is simply the inverse: low-intensity values spread from the seed image and are limited by the mask image, which represents the minimum allowed value. Alternatively, you can think of reconstruction as a way to isolate the connected regions of an image. For dilation, reconstruction connects regions marked by local maxima in the seed image: neighboring pixels less-than-or-equal-to those seeds are connected to the seeded region. Local maxima with values larger than the seed image will get truncated to the seed value. Real time : True Keyword Arguments (in parentheses, argument name): * Select source file type (source_file): * Channel (channel): * Offset for uneven image border (brightness_offset): Use when image border perimeter has uneven brightness * Overlay text on top of images (text_overlay): Draw description text on top of images * Activate tool (enabled): Toggle whether or not tool is active * Select pseudo color map (color_map): * use color palette (use_palette): Use color palette in postprocessing * Normalize channel (normalize): * Real time (real_time): Set if tool reacts in real time """ wrapper = self.init_wrapper(**kwargs) if wrapper is None: return False res = False try: if self.get_value_of("enabled") == 1: brightness_offset = self.get_value_of("brightness_offset") / 100 channel = self.get_value_of("channel") text_overlay = self.get_value_of("text_overlay") == 1 color_map = self.get_value_of("color_map") _, color_map = color_map.split("_") normalize = self.get_value_of("normalize") == 1 img = self.wrapper.current_image c = wrapper.get_channel(img, channel) if c is None: self.do_channel_failure(channel) return image = img_as_float(c) image = gaussian_filter(image, 1) seed = image - brightness_offset seed[1:-1, 1:-1] = image.min() mask = image dilated = reconstruction(seed, mask, method="dilation") dilated = self.to_uint8(dilated, normalize=normalize) if self.get_value_of("use_palette") == 1: dilated = cv2.applyColorMap(dilated, int(color_map)) self.result = dilated img_name = f"frm_{self.input_params_as_str()}" if text_overlay: wrapper.store_image( self.result, img_name, text_overlay=self.input_params_as_str( exclude_defaults=False, excluded_params=("progress_callback",), ).replace(", ", "\n"), ) else: wrapper.store_image(self.result, img_name, text_overlay=text_overlay) else: wrapper.store_image(wrapper.current_image, "source") res = True except Exception as e: wrapper.error_holder.add_error( new_error_text=f'Failed to process {self. name}: "{repr(e)}"', new_error_level=35, target_logger=logger, ) res = False else: pass finally: return res
def imimposemin(I, BW, conn=None, max_value=255): if not I.ndim in (2, 3): raise Exception("'I' must be a 2-D or 3D array.") if BW.shape != I.shape: raise Exception("'I' and 'BW' must have the same shape.") if BW.dtype is not bool: BW = BW != 0 # set default connectivity depending on whether the image is 2-D or 3-D if conn == None: if I.ndim == 3: conn = 26 else: conn = 8 else: if conn in (4, 8) and I.ndim == 3: raise Exception("'conn' is invalid for a 3-D image.") elif conn in (6, 18, 26) and I.ndim == 2: raise Exception("'conn' is invalid for a 2-D image.") # create structuring element depending on connectivity if conn == 4: selem = disk(1) elif conn == 8: selem = square(3) elif conn == 6: selem = ball(1) elif conn == 18: selem = ball(1) selem[:, 1, :] = 1 selem[:, :, 1] = 1 selem[1] = 1 elif conn == 26: selem = cube(3) fm = I.astype(float) try: fm[BW] = -math.inf fm[np.logical_not(BW)] = math.inf except: fm[BW] = -float("inf") fm[np.logical_not(BW)] = float("inf") if I.dtype == float: I_range = np.amax(I) - np.amin(I) if I_range == 0: h = 0.1 else: h = I_range*0.001 else: h = 1 fp1 = I + h g = np.minimum(fp1, fm) # perform reconstruction and get the image complement of the result if I.dtype == float: J = reconstruction(1 - fm, 1 - g, selem=selem) J = 1 - J else: J = reconstruction(255 - fm, 255 - g, method='dilation', selem=selem) J = 255 - J try: J[BW] = -math.inf except: J[BW] = -float("inf") return J
dst = np.array([[0, 0], [side - 1, 0], [side - 1, side - 1], [0, side - 1]], dtype='float32') # Gets the transformation matrix for skewing the image to fit a square by comparing the 4 before and after points m = cv2.getPerspectiveTransform(src, dst) # Performs the transformation on the original image sodoku = cv2.warpPerspective(img_origin, m, (int(side), int(side))) thresh = cv2.adaptiveThreshold(sodoku, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 5, 7) thresh = cv2.bitwise_not(thresh) salem = morphology.disk(1) eroded = morphology.erosion(thresh, salem) thresh = morphology.reconstruction(eroded, thresh) q = 28 # q = quality cut = 10 #invert the image (this is how MNIST digits is formatted) array = thresh #array = cv2.bitwise_not(array) #this will be the width and length of each sub-image divisor = array.shape[0] // 9 puzzle = [] for i in range(9): row = [] for j in range(9):
def PoresWatershedSegmentationOnePhase(phaseImage, structuringElement=np.ones((3, 3, 3)), distanceType='ITKDanielson', watershedAlgo='ITK', seedMethod='hMaxima', seedParam=4): #Calcul de la carte de distance distanceMap if distanceType == 'euclidean': memoryType = np.float16 distanceMap = ndimage.distance_transform_edt(phaseImage).astype( memoryType) elif distanceType == 'chamfer': memoryType = np.int8 distanceMap = ndimage.distance_transform_cdt( phaseImage, metric='chessboard').astype(memoryType) elif distanceType == 'ITKDanielson': memoryType = np.float16 itkimage = sitk.GetImageFromArray( np.logical_not(phaseImage).astype(np.uint8)) itkdistanceMap = sitk.DanielssonDistanceMap(itkimage) del itkimage distanceMap = sitk.GetArrayFromImage(itkdistanceMap).astype(memoryType) del itkdistanceMap #Choix des marqueurs pour la segmentation (centres des pores) : H-maxima : #maxima de la carte de distance dont les pics sont étêtés d'une hauteur h. Utilise #une recontruction morphologique pour construire la carte de distance étêtée. hMaximaLib = 'skimage' if seedMethod == 'localMax': seedParam = int(seedParam) local_maxi = feature.peak_local_max(distanceMap.astype(np.float), min_distance=seedParam, indices=False) elif seedMethod == 'hMaxima' and hMaximaLib == 'skimage' and seedParam > 0: hContrast = np.asarray(seedParam).astype(memoryType) reconstructed = morphology.reconstruction( distanceMap - hContrast, distanceMap).astype(memoryType) local_maxi = (distanceMap - reconstructed).astype(np.bool) del reconstructed elif seedMethod == 'hMaxima' and hMaximaLib == 'ITK' and seedParam > 0: hContrast = np.asarray(seedParam).astype(memoryType) itkSeedimage = sitk.GetImageFromArray( (distanceMap - hContrast).astype(np.float32)) itkMarkerImage = sitk.GetImageFromArray(distanceMap.astype(np.float32)) itkReconstructed = sitk.ReconstructionByDilation( itkSeedimage, itkMarkerImage) del itkMarkerImage, itkSeedimage reconstructed = sitk.GetArrayFromImage(itkReconstructed).astype( memoryType) del itkReconstructed local_maxi = ((distanceMap - reconstructed) > hContrast / 10).astype( np.bool) del reconstructed markers = ndimage.measurements.label(local_maxi, structure=structuringElement)[0] del local_maxi #Calcul des lignes de partage de niveau if watershedAlgo == 'Mahotas': _, watershedLines = mahotas.cwatershed( (distanceMap.max() - distanceMap).astype(np.int8), markers, Bc=structuringElement, return_lines=True) elif watershedAlgo == 'ITK': itkMarkers = sitk.GetImageFromArray(markers) itkDistance = sitk.GetImageFromArray(distanceMap.astype(np.float)) itkDistance = sitk.InvertIntensity(itkDistance) wsITK = sitk.MorphologicalWatershedFromMarkers(itkDistance, itkMarkers, markWatershedLine=True, fullyConnected=True) del itkMarkers, itkDistance # mask = itk.MaskImageFilter.IUC2IUC2IUC2.New(ws, fill) # overlay = itk.LabelOverlayImageFilter.IUC2IUC2IRGBUC2.New(reader, # mask, # UseBackground=True) ws = sitk.GetArrayFromImage(wsITK).astype(np.uint8) del wsITK watershedLines = (ws == 0).astype(np.bool) watershedLines[np.logical_not(phaseImage)] = False del markers #Labeler des pores séparés par les lignes de partage de niveau pores = ndimage.measurements.label(np.logical_and( phaseImage, np.logical_not(watershedLines)), structure=structuringElement)[0] pores[np.logical_not(phaseImage)] = 0 memoryType = utilities.BestMemoryType(pores.max()) pores = pores.astype(memoryType) return pores, watershedLines, distanceMap
from skimage.morphology import reconstruction from skimage.color import rgb2gray from skimage.io import imread # Convert to float: Important for subtraction later which won't work with uint8 #image = img_as_float(data.coins()) #image = gaussian_filter(image, 1) image = rgb2gray(imread('Dataset/13291658032668.jpg')) image = image.max() - image seed = np.copy(image) seed[1:-1, 1:-1] = image.min() mask = image dilated = reconstruction(seed, mask, method='dilation') fig, (ax0, ax1, ax2, ax3) = plt.subplots(nrows=1, ncols=4, figsize=(8, 2.5), sharex=True, sharey=True) ax0.imshow(image, cmap='gray') ax0.set_title('original image') ax0.axis('off') ax1.imshow(dilated, vmin=image.min(), vmax=image.max(), cmap='gray') ax1.set_title('dilated') ax1.axis('off')
def fillhole(img): seed = np.copy(img) seed[1:-1, 1:-1] = np.max(img) mask = img return reconstruction(seed, mask, method='erosion').astype(np.uint8)
def get_dmp(bd, image_min, image_max, ses=None): """ Calculates the Differential Morphological Profile Args: bd (2d array) image_min (int or float) image_max (int or float) ses (Optional[list]): The structuring elements. Returns: """ if not ses: ses = [3, 5, 7, 9, 11] if bd.dtype != 'uint8': bd = np.uint8( rescale_intensity(bd, in_range=(image_min, image_max), out_range=(0, 255))) section_rows, section_cols = bd.shape dmp_counter = 0 marker = bd.copy() previous = bd.copy() # The DMP holder # closings --> 1st len(ses) bands # openings --> last len(ses) bands dims = len(ses) * 2 dmp_array = np.uint8(np.empty((dims, section_rows, section_cols))) # Morphological opening for se_size in ses: se = cv2.getStructuringElement(cv2.MORPH_RECT, (se_size, se_size)) marker = cv2.erode(marker, se, iterations=1) current = reconstruction(marker, bd, method='dilation', selem=se) dmp_array[dmp_counter] = previous - current previous = current.copy() dmp_counter += 1 marker = bd.copy() previous = bd.copy() # Morphological closing for se_size in ses: se = cv2.getStructuringElement(cv2.MORPH_RECT, (se_size, se_size)) marker = cv2.dilate(marker, se, iterations=1) current = reconstruction(marker, bd, method='erosion', selem=se) dmp_array[dmp_counter] = current - previous previous = current.copy() dmp_counter += 1 # Reshape to [samples X dimensions]. dmp_array = dmp_array.reshape(dims, section_rows, section_cols).transpose( 1, 2, 0).reshape(section_rows * section_cols, dims) # Get the derivative of the # morphological openings # and closings. return np.uint8( rescale_intensity(np.gradient(dmp_array, axis=1).T.reshape( dims, section_rows, section_cols), in_range=(0, 200), out_range=(0, 255)))
def regional_flattener(z, h): """Localised erosion of the image 'z' for features below a value 'h'""" seed = np.copy(z) + h mask = z eroded = morphology.reconstruction(seed, mask, method='erosion') return eroded - h
img = np.asarray(Image.open('./images/tree_noise.png')).astype(np.uint8) size = np.size(img[0]) imEro43 = erosion(img, disk(3)) imr = RECONSTRUIT(imEro43, img) # Affichage avec matplotlib plt.subplot(131) plt.imshow(img, cmap='gray', vmin=0.0, vmax=255.0) plt.title('Originale') plt.subplot(132) plt.imshow(imr, cmap='gray', vmin=0.0, vmax=255.0) plt.title('Ouverture par reconstruction') imDil43 = dilation(img, disk(3)) imr = reconstruction(imDil43, img, method='erosion') plt.subplot(133) plt.imshow(imr, cmap='gray', vmin=0.0, vmax=255.0) plt.title('Fermeture par reconstruction') plt.show() ######################## Q4.3 ########################## img = np.asarray(Image.open('./images/coffee.png')).astype(np.uint8) def EROSION_ULTIME(img): imX = np.tile(img, 1) size = np.size(img[0]) imEroU = np.zeros((size, size), dtype=int) for k in range(10): ##répéter jusqu'à stable
# make the filename folder_name = args[0] output_folder = args[1] # sphere, meta_header = md.load_raw_data_with_mhd(folder_name + "/gradientgauss.mhd") sphere, header = md.load_raw_data_with_mhd(folder_name + "/sphere1.mhd") sphere = median_filter(sphere, 3) np_image = (sphere > threshold_otsu(sphere)) * 1 import numpy as np from skimage.morphology import reconstruction seed = np.copy(np_image) seed[1:-1, 1:-1] = np_image.max() mask = np_image np_image = reconstruction(seed, mask, method='erosion') from scipy import ndimage distance = ndimage.distance_transform_edt(np_image) max_pos = ndimage.measurements.maximum_position(distance) print max_pos for z in range(np_image.shape[0]): slice = distance[z, :, :] misc.imsave(folder_name + '/slice{0}.tif'.format(z), slice)
def reconstruct(seed, mask): return reconstruction(seed, mask)
Now we need to create the seed image, where the minima represent the starting points for erosion. To fill holes, we initialize the seed image to the maximum value of the original image. Along the borders, however, we use the original values of the image. These border pixels will be the starting points for the erosion process. We then limit the erosion by setting the mask to the values of the original image. """ import numpy as np from skimage.morphology import reconstruction seed = np.copy(image) seed[1:-1, 1:-1] = image.max() mask = image filled = reconstruction(seed, mask, method='erosion') #imshow(filled, vmin=image.min(), vmax=image.max()) #plt.title('after filling holes') """ .. image:: PLOT2RST.current_figure As shown above, eroding inward from the edges removes holes, since (by definition) holes are surrounded by pixels of brighter value. Finally, we can isolate the dark regions by subtracting the reconstructed image from the original image. """ #imshow(image - filled) #plt.title('holes') """
(255, 255, 255), thickness=30) grid_crop = morphology.remove_small_objects(grid_crop, min_size=9000) img = morphology.dilation( grid_crop, morphology.square(5) ) > 0 # dilation para deixas as linhas mais largas, para que os quadrados (props) fiquem bem definidos img1 = morphology.remove_small_objects(img, min_size=100) img1 = morphology.remove_small_holes(img1, 110, connectivity=2) img1 = morphology.remove_small_holes(img1, 110) verticals = morphology.erosion(img1, morphology.rectangle( 130, 1)) # separar as linhas verticais img2 = morphology.reconstruction(verticals, img1) img3 = morphology.dilation(img2, morphology.square(5)) > 0 img = ~img3 props = regionprops(morphology.label(img)) prop_list = [] crop_list = [] soma = 0 for i in props: soma += i.area soma /= len(props)