def get_disease2(self): r,g,b = my_image.image_split(self.i_source) hsv = rgb2hsv(self.i_source) dise= np.copy(self.i_source[:,:,1]) dise[dise>1]=0 dise[(hsv[:,:,0]>0) & (hsv[:,:,0]<float(45.0/255))&(r<100)&(g<100)&(b<50)]=1 label_im, nb_labels = ndimage.label(dise) sizes = ndimage.sum(dise, label_im, range(nb_labels + 1)) mean_vals = ndimage.sum(dise, label_im, range(1, nb_labels + 1)) mask_size = sizes < np.max(sizes) remove_pixel = mask_size[label_im] label_im[remove_pixel] = 0 label_im[label_im>0]=1 # pdb.set_trace() ### # remove artifacts connected to image border self.s_disease = 0 # label image regions region = regionprops(label(label_im)) for r1 in region: # pdb.set_trace() if r1.area > 10: self.s_disease += r1.area else: minr, minc, maxr, maxc = r1.bbox label_im[float(minr):float(maxr),float(minc):float(maxc)]=0 self.i_desease = label_im self.i_leaf = self.i_source#blade self.i_back = self.i_source#background self.s_leaf = 1#s_b+s_d
def _estimate_profile(self, image, image_err, mask): """ Estimate image profile. """ from scipy import ndimage p = self.parameters labels = self._label_image(image, mask) profile_err = None index = np.arange(1, len(self._get_x_edges(image))) if p['method'] == 'sum': profile = ndimage.sum(image.data, labels.data, index) if image.unit.is_equivalent('counts'): profile_err = np.sqrt(profile) elif image_err: # gaussian error propagation err_sum = ndimage.sum(image_err.data ** 2, labels.data, index) profile_err = np.sqrt(err_sum) elif p['method'] == 'mean': # gaussian error propagation profile = ndimage.mean(image.data, labels.data, index) if image_err: N = ndimage.sum(~np.isnan(image_err.data), labels.data, index) err_sum = ndimage.sum(image_err.data ** 2, labels.data, index) profile_err = np.sqrt(err_sum) / N return profile, profile_err
def calculate_zernikes(self, workspace): zernike_indexes = cpmz.get_zernike_indexes(self.zernike_degree.value + 1) meas = workspace.measurements for o in self.objects: object_name = o.object_name.value objects = workspace.object_set.get_objects(object_name) # # First, get a table of centers and radii of minimum enclosing # circles per object # ij = np.zeros((objects.count + 1, 2)) r = np.zeros(objects.count + 1) for labels, indexes in objects.get_labels(): ij_, r_ = minimum_enclosing_circle(labels, indexes) ij[indexes] = ij_ r[indexes] = r_ # # Then compute x and y, the position of each labeled pixel # within a unit circle around the object # ijv = objects.ijv l = ijv[:, 2] yx = (ijv[:, :2] - ij[l, :]) / r[l, np.newaxis] z = cpmz.construct_zernike_polynomials( yx[:, 1], yx[:, 0], zernike_indexes) for image_group in self.images: image_name = image_group.image_name.value image = workspace.image_set.get_image( image_name, must_be_grayscale=True) pixels = image.pixel_data mask = (ijv[:, 0] < pixels.shape[0]) & \ (ijv[:, 1] < pixels.shape[1]) mask[mask] = image.mask[ijv[mask, 0], ijv[mask, 1]] yx_ = yx[mask, :] l_ = l[mask] z_ = z[mask, :] if len(l_) == 0: for i, (n, m) in enumerate(zernike_indexes): ftr = self.get_zernike_magnitude_name(image_name, n, m) meas[object_name, ftr] = np.zeros(0) if self.wants_zernikes == Z_MAGNITUDES_AND_PHASE: ftr = self.get_zernike_phase_name(image_name, n, m) meas[object_name, ftr] = np.zeros(0) continue areas = scind.sum( np.ones(l_.shape, int), labels=l_, index=objects.indices) for i, (n, m) in enumerate(zernike_indexes): vr = scind.sum( pixels[ijv[mask, 0], ijv[mask, 1]] * z_[:, i].real, labels=l_, index=objects.indices) vi = scind.sum( pixels[ijv[mask, 0], ijv[mask, 1]] * z_[:, i].imag, labels=l_, index=objects.indices) magnitude = np.sqrt(vr * vr + vi * vi) / areas ftr = self.get_zernike_magnitude_name(image_name, n, m) meas[object_name, ftr] = magnitude if self.wants_zernikes == Z_MAGNITUDES_AND_PHASE: phase = np.arctan2(vr, vi) ftr = self.get_zernike_phase_name(image_name, n, m) meas[object_name, ftr] = phase
def getRealLabeledAreaCenter(image,labeled_image,indices,info): print "Getting real area and center" shape=numpy.shape(image) ones=numpy.ones(shape) area=nd.sum(ones,labels=labeled_image,index=indices) center=nd.center_of_mass(ones,labels=labeled_image,index=indices) ll=0 try: len(area) except: area=[area] center=[center] try: len(indices) except: indices=[indices] try: info.keys() except: offset=1 else: offset=0 for l in indices: info[l-offset][0]=area[ll] info[l-offset][4]=center[ll] ll += 1 return info
def getRealLabeledPerimeter(image,edge_mask,labeled_image,indices,info,testlog): print "Getting real perimeter length" edgeimage,testlog=findEdgeSobel(edge_mask,0,0.5,1.0,False,testlog) mask=ma.getmask(edgeimage) length=nd.sum(mask,labels=labeled_image,index=indices) ll=0 try: len(length) except: length=[length] try: len(indices) except: indices=[indices] try: info.keys() except: offset=1 else: offset=0 for l in indices: info[l-offset][3]=length[ll] ll += 1 return info
def remove_small_objects(arr, min_size, connectivity=8): ''' Remove objects less than the given size. Function is based on skimage.morphology.remove_small_objects Parameters ---------- arr : numpy.ndarray Binary array containing the mask. min_size : int Smallest allowed size. connectivity : int, optional Connectivity of the neighborhood. ''' struct = nd.generate_binary_structure(arr.ndim, connectivity) labels, num = nd.label(arr, struct) sizes = nd.sum(arr, labels, range(1, num + 1)) for i, size in enumerate(sizes): if size >= min_size: continue posns = np.where(labels == i + 1) arr[posns] = 0 return arr
def kill_isolated_pixels(array, structure=None, threshold=0.2): """ Return array with isolated islands removed. Only keeping the biggest islands (largest surface). :param array: Array with completely isolated cells :param struct: Structure array for generating unique regions :return: Filtered array with just the largest island """ filtered_array = np.copy(array) # Put to 0 pixels that are below 'threshold' filtered_array[filtered_array < threshold] = 0 mask = filtered_array > 0 # Detect islands ("label") label_array, num_labels = ndimage.label(mask, structure=structure) # Count the number of pixels for each island num_pixels_per_island = ndimage.sum(filtered_array, label_array, range(num_labels + 1)) # Only keep the biggest island mask_biggest_island = num_pixels_per_island < np.max(num_pixels_per_island) remove_pixel = mask_biggest_island[label_array] filtered_array[remove_pixel] = 0 return filtered_array
def fillDataDict(radlist, anglelist, freqlist): """ Get min/max statistics on data lists """ d = {} freqnumpy = numpy.asarray(freqlist, dtype=numpy.int32) d['minf'] = float(ndimage.minimum(freqnumpy)) d['maxf'] = float(ndimage.maximum(freqnumpy)) if ndimage.sum(freqnumpy) < 10: apDisplay.printWarning("not enough eulers to draw a map") return None d['rangef'] = d['maxf']-d['minf']+1 angnumpy = numpy.asarray(anglelist, dtype=numpy.float32) d['mina'] = float(ndimage.minimum(angnumpy)) d['maxa'] = float(ndimage.maximum(angnumpy)) if d['maxa'] > 330.0*math.pi/180.0: d['maxa'] = 2.0*math.pi d['rangea'] = d['maxa']-d['mina'] radnumpy = numpy.asarray(radlist, dtype=numpy.float32) d['minr'] = float(ndimage.minimum(radnumpy)) d['maxr'] = float(ndimage.maximum(radnumpy)) d['ranger'] = d['maxr']-d['minr'] xnumpy = radnumpy * numpy.cos(angnumpy - d['mina']) ynumpy = radnumpy * numpy.sin(angnumpy - d['mina']) d['minx'] = float(ndimage.minimum(xnumpy)) d['maxx'] = float(ndimage.maximum(xnumpy)) d['miny'] = float(ndimage.minimum(ynumpy)) d['maxy'] = float(ndimage.maximum(ynumpy)) d['rangex'] = d['maxx']-d['minx'] d['rangey'] = d['maxy']-d['miny'] return d
def segmentationize(imageSe): """ Divides coherent forms of an image in smaller groups of type integer. """ # create an matrix of distances to the next sourrounding area distance = ndimage.distance_transform_edt(imageSe, sampling=3) erosed = ndimage.binary_erosion(imageSe, iterations=8).astype(imageSe.dtype) distanceE = ndimage.distance_transform_edt(erosed, sampling=3) distance += (2 * distanceE) labels, num = label(imageSe, background=0, return_num='True') sizes_image = ndimage.sum(imageSe, labels, range(num)) sizes_image = np.sort(sizes_image, axis=None) pos = int(0.4 * num) areal = int(sizes_image[pos] ** 0.5) if areal <= 10: areal = 10 elif (areal % 2) != 0: areal += 1 footer = circarea(areal) # draw circle area # find the positions of the maxima from the distances local_maxi = peak_local_max(distance, indices=False, footprint=footer, labels=imageSe) markers = label(local_maxi) # watershed algorithm starts at the maxima and returns labels of particles simplefilter("ignore", FutureWarning) # avoid warning in watershed method labels_ws = watershed(-distance, markers, mask=imageSe) simplefilter("default", FutureWarning) return labels, labels_ws, local_maxi
def removeSmall(im, size): ''' Remove small regions from a numpy image inut image is black on white (std) inversion is done internally Image for labeling needs to be White on black The image returned is inverted (black on white ) ''' #im = 255-im label_im, nb_labels = ndimage.label(im) mask = im>im.mean() sizes = ndimage.sum(mask, label_im, range(nb_labels + 1)) mean_vals = ndimage.sum(im, label_im, range(1, nb_labels + 1)) mask_size = sizes < size remove_pixel = mask_size[label_im] return remove_pixel
def threshold_connect_components(map, threshold, copy=True): """ Given a map with some coefficients set to zero, segment the connect components with number of voxels smaller than the threshold and set them to 0. Parameters ---------- map: ndarray The map to segment threshold: The minimum number of voxels to keep a cluster. copy: bool, optional If copy is false, the input array is modified inplace """ labels, n_labels = ndimage.label(map) weights = ndimage.sum(np.ones_like(labels), labels, index=range(n_labels)) if copy: map = map.copy() for index, weight in enumerate(weights): if index == 0: continue if weight < threshold: map[labels == index] = 0 return map
def clean_zones(zones, minSegmentSize): ''' Remove small zones and replace with nearest neigbour''' # bad zones mask badMask = zones == -1 # split multi-part zones zonesAll = split_multi_zones(zones) # find areas of all zones zAllIndeces = np.unique(zonesAll) zAllIndeces = zAllIndeces[zAllIndeces >= 0] zAllAreas = nd.sum(np.ones_like(zones), zonesAll, zAllIndeces) # set zones with small areas to -1 for zai in zAllIndeces[zAllAreas < minSegmentSize]: zonesAll[zonesAll == zai] = -1 # fill small segments with values from nearest neighbours invalid_cell_mask = zonesAll == -1 indices = nd.distance_transform_edt(invalid_cell_mask, return_distances=False, return_indices=True) zonesClean = zones[tuple(indices)] # mask bad values with 0 zonesClean[badMask] = -1 return zonesClean
def updatemain(self): if self.verbose: print "updating",self.layer if self.xview: # dataswappedX = np.swapaxes(self.data,0,1) self.arr=self.dataswappedX[self.layer] elif self.yview: # dataswappedY = np.swapaxes(self.data,0,2) self.arr=self.dataswappedY[self.layer] else: self.arr=self.data[self.layer] self.img1a.setImage(self.arr) if self.firsttime: self.firsttime = False else: if self.verbose: print self.rois if self.rois[self.layer]: # self.p1.removeItem(self.roi) # self.restorePolyLineState(self.roi, self.rois[self.layer]) self.roi.setState(self.rois[self.layer]) # self.p1.addItem(self.roi) self.update() self.label_layer.setText("layer: "+str(self.layer+1)+"/"+str(len(self.data[:,:,:,0]))) self.label_shape.setText("shape: "+str(self.arr[:,:,0].shape)) self.label_size.setText("size: "+str(self.arr[:,:,0].size)) self.label_min.setText("min: "+str(self.arr[:,:,0].min())) self.label_max.setText("max: "+str(self.arr[:,:,0].max())) self.label_mean.setText("mean: "+str(self.arr[:,:,0].mean())) self.label_sd.setText("sd: "+str(ndimage.standard_deviation(self.arr[:,:,0]))) self.label_sum.setText("sum: "+str(ndimage.sum(self.arr[:,:,0]))) self.img1a.updateImage()
def makeSummedMask(self): '''Make a mask by summing all frames, creating a histogram and using the maximum bin ''' #self.sumData = self.imgData.sum(axis=0) sumData = self.cData.getImageSum() hist, bins = np.histogram(sumData.ravel(), bins=100) mask = sumData > bins[3] label_im, nb_labels = ndimage.label(mask) sizes = ndimage.sum(mask, label_im, range(nb_labels + 1)) mean_vals = ndimage.sum(self.cData.getImage(), label_im, range(1, nb_labels + 1)) mask_size = sizes < max(sizes) #print(sizes, mask_size, max(sizes)) remove_pixel = mask_size[label_im] label_im[remove_pixel] = 0 label_im = ndimage.binary_dilation(label_im) return label_im
def print_mask_summary(mask): labels, nmax = ni.label(mask) pix_sum = ni.sum(mask, labels=labels, index=np.arange(1, nmax+1)) print "number of islands :" , len(pix_sum), "%5.2f%%" % (float(len(pix_sum))/len(mask.flat)*100.) print "total area :", np.max(pix_sum) print "number of large islands (A > 9 pix) :", np.sum(pix_sum > 9)
def test_sum11(): labels = np.array([1, 2], np.int8) for type in types: input = np.array([[1, 2], [3, 4]], type) output = ndimage.sum(input, labels=labels, index=2) assert_almost_equal(output, 6.0)
def get_labels(self): """ find clusters and extract center and size Parameters ---------- Returns ------- self.labels : 'list' list of cluster labels self.centers : 'list' list of cluster centers self.sizes : 'list' list of cluster sizes """ b_img = self.img_b label_im, nb_labels = ndimage.label(b_img) center = np.asarray(ndimage.center_of_mass(b_img, label_im, range(1, nb_labels + 1))) size = np.asarray(ndimage.sum(b_img, label_im, range(1, nb_labels + 1))) self.labels = label_im self.centers = center self.sizes = size
def test_sum12(): labels = np.array([[1, 2], [2, 4]], np.int8) for type in types: input = np.array([[1, 2], [3, 4]], type) output = ndimage.sum(input, labels=labels, index=[4, 8, 2]) assert_array_almost_equal(output, [4.0, 0.0, 5.0])
def test_sum08(): "sum 8" labels = np.array([1, 0], bool) for type in types: input = np.array([1, 2], type) output = ndimage.sum(input, labels=labels) assert_equal(output, 1.0)
def test_sum09(): "sum 9" labels = np.array([1, 0], bool) for type in types: input = np.array([[1, 2], [3, 4]], type) output = ndimage.sum(input, labels=labels) assert_almost_equal(output, 4.0)
def test_sum06(): "sum 6" labels = np.array([], bool) for type in types: input = np.array([], type) output = ndimage.sum(input, labels=labels) assert_equal(output, 0.0)
def test_sum07(): "sum 7" labels = np.ones([0, 4], bool) for type in types: input = np.zeros([0, 4], type) output = ndimage.sum(input, labels=labels) assert_equal(output, 0.0)
def FilterArea(img, labels, nb_labels, threshold): """ Function definition +++++++++++++++++++ .. py:function:: FilterArea(img, labels, nb_labels, threshold) This method eliminates connected components whose areas are smaller than a threshold. :param numpy_array img: binary image on which connected components were estimated. :param numpy_array img: labeled image that corresponds to the output of the **ConnenctedComponents(img)** method. :param int nb_labels: number of labels in labeled image. :param float threshold: area threshold to eliminate small connected components. :return: new labeled image and number of newlabels. :rtype: numpy array of the same dimensions with img - int. """ filt_labels = np.copy(labels) areas = ndimage.sum(img, labels, range(nb_labels + 1)) mask_size = areas < threshold remove_pixel = mask_size[labels] filt_labels[remove_pixel] = 0 new_labels = np.unique(filt_labels) filt_labels = np.searchsorted(new_labels, filt_labels) return filt_labels, new_labels.shape[0]-1
def get_largest_two_component(img, prt = False, threshold = None): s = ndimage.generate_binary_structure(3,2) # iterate structure labeled_array, numpatches = ndimage.label(img,s) # labeling sizes = ndimage.sum(img,labeled_array,range(1,numpatches+1)) sizes_list = [sizes[i] for i in range(len(sizes))] sizes_list.sort() #if(prt): # print('component size', sizes_list, flush = True) if(len(sizes) == 1): return img else: if(threshold): out_img = np.zeros_like(img) for temp_size in sizes_list: if(temp_size > threshold): temp_lab = np.where(sizes == temp_size)[0] + 1 temp_cmp = labeled_array == temp_lab out_img = (out_img + temp_cmp) > 0 return out_img else: max_size1 = sizes_list[-1] max_size2 = sizes_list[-2] max_label1 = np.where(sizes == max_size1)[0] + 1 max_label2 = np.where(sizes == max_size2)[0] + 1 component1 = labeled_array == max_label1 component2 = labeled_array == max_label2 #if(prt): # print(max_size2, max_size1, max_size2/max_size1, flush = True) if(max_size2*10 > max_size1): component1 = (component1 + component2) > 0 return component1
def _sim_mask(in_file): import nibabel as nb import numpy as np import os.path as op import scipy.ndimage as sn from scipy.ndimage.morphology import (binary_opening, binary_dilation) from pyacwereg.misc import ball as gen_ball if not isinstance(in_file, basestring): in_file = in_file[2] out_file = op.abspath('sim_mask.nii.gz') im = nb.load(in_file) data = im.get_data() data[data > 1.0e-4] = 1 data[data < 1] = 0 ball1 = gen_ball(4, 1.9) data = binary_opening(data.astype(np.uint8), structure=ball1, iterations=1).astype(np.uint8) # Get largest object label_im, nb_labels = sn.label(data) sizes = sn.sum(data, label_im, range(nb_labels + 1)) larger = np.squeeze(np.argwhere(sizes == sizes.max())) data[label_im != larger] = 0 # Dilate # data = binary_dilation(data, structure=ball1, # iterations=1).astype(np.uint8) nb.Nifti1Image( data, im.get_affine(), im.get_header()).to_filename(out_file) return out_file
def connected_comonents(self, threshold): connect_structure = np.ones((3,3,3), dtype='int') grid = self.grid labeled_arr, n_labels = ni.label(grid, connect_structure) if n_labels == 0: return labeled_arr, n_labels labels_list = range(1,n_labels+1) count_objects = ni.sum(grid, labeled_arr, labels_list) if n_labels == 1: count_objects = [count_objects] t0 = time.time() new_labels_list = [] for c,l in zip(count_objects, labels_list): if c > threshold: new_labels_list.append(l) else: labeled_arr[np.where(labeled_arr == l)] = 0 # relabel stuff for nl,l in enumerate(new_labels_list): labeled_arr[np.where(labeled_arr == l)] = nl+1 n_labels = len(new_labels_list) t1 = time.time() print 'time:', t1-t0 return labeled_arr, n_labels
def reg_median_cont(data,mask,min_size): emask = ndimage.morphology.binary_erosion(mask,iterations=2) cmask = mask[:] cmask[emask==1]=0 label_im, nb_labels = ndimage.label(cmask) sizes = ndimage.sum(cmask,label_im,range(nb_labels+1)) mask_size = sizes < min_size remove_pixel = mask_size[label_im] label_im[remove_pixel] = 0 labels = np.unique(label_im) label_im = np.searchsorted(labels, label_im) labels = np.unique(label_im) out = np.array(label_im,dtype=np.float) for lab in labels: if( lab==0 ): continue try: slice_x, slice_y = ndimage.find_objects(label_im==lab)[0] except IndexError: print ("Bad index: "%lab) continue # print lab rois = data[slice_x, slice_y] tmask = label_im==lab roim = tmask[slice_x, slice_y] roio = out[slice_x, slice_y] mean = np.ma.median(np.ma.array(rois,mask=~roim)) roio[roim] = mean return out
def getAxyLabels(labels, imageDir="Left_to_right", edgeThickness=1, fraction=None): """ Get the edges touched by clusters/avalanche given by a 2D array of 1's or labels, as obtained using scipy.ndimage.label Parameters: ---------------- labels : ndarray A 2D array with the cluster numbers as calculated from scipy.ndimage.label imageDir : string The direction of the avalanche motion Left <-> right Bottom <-> top edgeThickness : int the width of the frame around the image which is considered as the thickness of each edge fraction : float This is the minimum fraction of the size of the avalanche/cluster inside an edge (of thickness edgeThickness) with sets the avalanche/cluster as touching """ et = edgeThickness if not fraction: fraction = 0. # Find first the 4 borders # Definition good for "Left_to_right" left = list(labels[0:et,:].flatten()) right = list(labels[-et:,:].flatten()) bottom = list(labels[:,0:et].flatten()) top = list(labels[:,-et:].flatten()) if imageDir=="Left_to_right": lrbt = left, right, bottom, top elif imageDir == "Right_to_left": lrbt = right, left, top, bottom elif imageDir == "Bottom_to_top": lrbt = bottom, top, right, left elif imageDir == "Top_to_bottom": lrbt = top, bottom, left, right else: raise ValueError, "avalanche direction not defined" # Find the sets # Select the labels contained in the edges #setLrbt = set(left+right+bottom+top) setLrbt = set(itertools.chain(*lrbt)) # Remove the value 0 as it is not a cluster setLrbt.remove(0) maxLabel = labels.max() # Prepare a list with all '0000' labels list_Axy = ['0000']*maxLabel # Search for the Axy except for the A00 for cluster in setLrbt: # iter over clusters touching the edges fraction_size = int(fraction * nd.sum(labels == cluster)) + 1 list_Axy[cluster-1] = "".join([((edge.count(cluster) >= fraction_size) and '1' or '0') for edge in lrbt]) #pixels_on_the_edge = "/".join([str(edge.count(cluster)) for edge in lrbt]) return scipy.array(list_Axy)
def downsample(ar, fact): sx, sy = ar.shape X, Y = np.ogrid[0:sx, 0:sy] regions = sy/fact * (X/fact) + Y/fact res = ndimage.sum(ar, labels=regions, index=np.arange(regions.max() + 1)) res.shape = (sx/fact, sy/fact) return res
def largestObjectInMask(self, mask=None, saveMask=False): """Takes a numpy.uint8 array with 1s and 0s for values. Labels each connected region of 1s with a different integer. Determines the largest such connected region. Discards all blocks except the largest, and returns the mask with only the largest object represented by 1s.""" if mask==None: mask = self.mask # this is the part where all the magic is performed by scipy labeled, how_many_labels = ndimage.label(mask) # spit out an array listing the size of each labeled block sizes = ndimage.sum(mask, labeled, range(how_many_labels + 1)) # find the label of the largest region max_label = list(sizes).index(max(sizes)) # if you're not in the largest region, you get set to zero # if you are in the largest region, you get set to 1 mask[labeled!=max_label]=0 mask[labeled==max_label]=1 mask = np.uint8(mask) if saveMask: self.mask = mask return mask
def test_sum03(): for type in types: input = np.ones([], type) output = ndimage.sum(input) assert_almost_equal(output, 1.0)
def weightedregionprops(L, ncc, dfore): if DEBUG_TRACKINGSETTINGS: print 'in weightedregionprops, ncc = ' + str( ncc) + ', max(L) = ' + str(num.max(L)) + ', nnz(L) = ' + str( num.flatnonzero(L).shape) + ', sum(dfore) = ' + str( num.sum(num.sum(dfore))) if DEBUG_TRACKINGSETTINGS: for l in range(1, num.max(L) + 1): print 'nnz(L == %d) = ' % l + str(num.alen( num.flatnonzero(L == l))) if ncc == 0: return [] # all connected components index = range(1, ncc + 1) time0 = time.time() # create the unnormalized weight matrix w = dfore #w[L==0] = 0 # compute the normalization terms z = num.array(meas.sum(w, L, index), ndmin=1) z[z == 0] = 1 # compute the unnormalized centers cx = num.array(meas.sum(w * params.GRID.X, L, index), ndmin=1) cy = num.array(meas.sum(w * params.GRID.Y, L, index), ndmin=1) # normalize centers cx /= z cy /= z # compute unnormalized, uncentered variances cx2 = num.array(meas.sum(w * params.GRID.X2, L, index), ndmin=1) cy2 = num.array(meas.sum(w * params.GRID.Y2, L, index), ndmin=1) cxy = num.array(meas.sum(w * params.GRID.XY, L, index), ndmin=1) # normalize variances cx2 /= z cy2 /= z cxy /= z # center variances cx2 -= cx**2 cy2 -= cy**2 cxy -= cx * cy # create ellipses ellipses = [] for i in range(len(cx)): # compute major, minor, angle from cov (sizeH, sizeW, angle) = cov2ell2(cx2[i], cy2[i], cxy[i]) if (sizeH < .125) or num.isnan(sizeH): sizeH = .125 if (sizeW < .125) or num.isnan(sizeW): sizeW = .125 # compute area area = num.pi * sizeW * sizeH * 4 ellipses.append(Ellipse(cx[i], cy[i], sizeW, sizeH, angle, area, -1)) return ellipses
def test_sum10(): labels = np.array([1, 0], bool) input = np.array([[1, 2], [3, 4]], bool) output = ndimage.sum(input, labels=labels) assert_almost_equal(output, 2.0)
def test_sum09(): labels = np.array([1, 0], bool) for type in types: input = np.array([[1, 2], [3, 4]], type) output = ndimage.sum(input, labels=labels) assert_almost_equal(output, 4.0)
def isolateregions(binary_array, size_threshold=0, pad_size=5, fill_hole=False, rel_size=0.1, morph_smooth=False): ''' Labels regions in a boolean array and returns individual arrays for each region. Regions below a threshold can optionlly be removed. Small holes may also be filled in. Parameters ---------- binary_array : numpy.ndarray A binary array of regions. size_threshold : int, optional Sets the pixel size on the size of regions. pad_size : int, optional Padding to be added to the individual arrays. fill_hole : int, optional Enables hole filling. rel_size : float or int, optional If < 1.0, sets the minimum size a hole must be relative to the area of the mask. Otherwise, this is the maximum number of pixels the hole must have to be deleted. morph_smooth : bool, optional Morphologically smooth the image using a binar opening and closing. Returns ------- output_arrays : list Regions separated into individual arrays. num : int Number of filaments corners : list Contains the indices where each skeleton array was taken from the original. ''' output_arrays = [] corners = [] # Label skeletons labels, num = nd.label(binary_array, eight_con()) # Remove skeletons which have fewer pixels than the threshold. if size_threshold != 0: sums = nd.sum(binary_array, labels, range(1, num + 1)) remove_fils = np.where(sums <= size_threshold)[0] for lab in remove_fils: binary_array[np.where(labels == lab + 1)] = 0 # Relabel after deleting short skeletons. labels, num = nd.label(binary_array, eight_con()) # Split each skeleton into its own array. for n in range(1, num + 1): x, y = np.where(labels == n) # Make an array shaped to the skeletons size and padded on each edge # the +1 is because, e.g., range(0, 5) only has 5 elements, but the # indices we're using are range(0, 6) shapes = (x.max() - x.min() + 2 * pad_size, y.max() - y.min() + 2 * pad_size) eachfil = np.zeros(shapes) eachfil[x - x.min() + pad_size, y - y.min() + pad_size] = 1 # Fill in small holes if fill_hole: eachfil = _fix_small_holes(eachfil, rel_size=rel_size) if morph_smooth: eachfil = nd.binary_opening(eachfil, np.ones((3, 3))) eachfil = nd.binary_closing(eachfil, np.ones((3, 3))) output_arrays.append(eachfil) # Keep the coordinates from the original image lower = (x.min() - pad_size, y.min() - pad_size) upper = (x.max() + pad_size + 1, y.max() + pad_size + 1) corners.append([lower, upper]) return output_arrays, num, corners
def run(self, workspace): """Run the module on an image set""" object_name = self.object_name.value remaining_object_name = self.remaining_objects.value original_objects = workspace.object_set.get_objects(object_name) if self.mask_choice == MC_IMAGE: mask = workspace.image_set.get_image(self.masking_image.value, must_be_binary=True) mask = mask.pixel_data else: masking_objects = workspace.object_set.get_objects( self.masking_objects.value) mask = masking_objects.segmented > 0 if self.wants_inverted_mask: mask = ~mask # # Load the labels # labels = original_objects.segmented.copy() nobjects = np.max(labels) # # Resize the mask to cover the objects # mask, m1 = cpo.size_similarly(labels, mask) mask[~m1] = False # # Apply the mask according to the overlap choice. # if nobjects == 0: pass elif self.overlap_choice == P_MASK: labels = labels * mask else: pixel_counts = fix( scind.sum(mask, labels, np.arange(1, nobjects + 1, dtype=np.int32))) if self.overlap_choice == P_KEEP: keep = pixel_counts > 0 else: total_pixels = fix( scind.sum( np.ones(labels.shape), labels, np.arange(1, nobjects + 1, dtype=np.int32), )) if self.overlap_choice == P_REMOVE: keep = pixel_counts == total_pixels elif self.overlap_choice == P_REMOVE_PERCENTAGE: fraction = self.overlap_fraction.value keep = pixel_counts / total_pixels >= fraction else: raise NotImplementedError( "Unknown overlap-handling choice: %s", self.overlap_choice.value) keep = np.hstack(([False], keep)) labels[~keep[labels]] = 0 # # Renumber the labels matrix if requested # if self.retain_or_renumber == R_RENUMBER: unique_labels = np.unique(labels[labels != 0]) indexer = np.zeros(nobjects + 1, int) indexer[unique_labels] = np.arange(1, len(unique_labels) + 1) labels = indexer[labels] parent_objects = unique_labels else: parent_objects = np.arange(1, nobjects + 1) # # Add the objects # remaining_objects = cpo.Objects() remaining_objects.segmented = labels remaining_objects.unedited_segmented = original_objects.unedited_segmented workspace.object_set.add_objects(remaining_objects, remaining_object_name) # # Add measurements # m = workspace.measurements m.add_measurement( remaining_object_name, cellprofiler_core.measurement.FF_PARENT % object_name, parent_objects, ) if np.max(original_objects.segmented) == 0: child_count = np.array([], int) else: child_count = fix( scind.sum( labels, original_objects.segmented, np.arange(1, nobjects + 1, dtype=np.int32), )) child_count = (child_count > 0).astype(int) m.add_measurement( object_name, cellprofiler_core.measurement.FF_CHILDREN_COUNT % remaining_object_name, child_count, ) if self.retain_or_renumber == R_RETAIN: remaining_object_count = nobjects else: remaining_object_count = len(unique_labels) I.add_object_count_measurements(m, remaining_object_name, remaining_object_count) I.add_object_location_measurements(m, remaining_object_name, labels) # # Save the input, mask and output images for display # if self.show_window: workspace.display_data.original_labels = original_objects.segmented workspace.display_data.final_labels = labels workspace.display_data.mask = mask
def run_image_pair_objects(self, workspace, first_image_name, second_image_name, object_name): '''Calculate per-object correlations between intensities in two images''' first_image = workspace.image_set.get_image(first_image_name, must_be_grayscale=True) second_image = workspace.image_set.get_image(second_image_name, must_be_grayscale=True) objects = workspace.object_set.get_objects(object_name) # # Crop both images to the size of the labels matrix # labels = objects.segmented try: first_pixels = objects.crop_image_similarly(first_image.pixel_data) first_mask = objects.crop_image_similarly(first_image.mask) except ValueError: first_pixels, m1 = cpo.size_similarly(labels, first_image.pixel_data) first_mask, m1 = cpo.size_similarly(labels, first_image.mask) first_mask[~m1] = False try: second_pixels = objects.crop_image_similarly(second_image.pixel_data) second_mask = objects.crop_image_similarly(second_image.mask) except ValueError: second_pixels, m1 = cpo.size_similarly(labels, second_image.pixel_data) second_mask, m1 = cpo.size_similarly(labels, second_image.mask) second_mask[~m1] = False mask = ((labels > 0) & first_mask & second_mask) first_pixels = first_pixels[mask] second_pixels = second_pixels[mask] labels = labels[mask] result = [] first_pixel_data = first_image.pixel_data first_mask = first_image.mask first_pixel_count = np.product(first_pixel_data.shape) second_pixel_data = second_image.pixel_data second_mask = second_image.mask second_pixel_count = np.product(second_pixel_data.shape) # # Crop the larger image similarly to the smaller one # if first_pixel_count < second_pixel_count: second_pixel_data = first_image.crop_image_similarly(second_pixel_data) second_mask = first_image.crop_image_similarly(second_mask) elif second_pixel_count < first_pixel_count: first_pixel_data = second_image.crop_image_similarly(first_pixel_data) first_mask = second_image.crop_image_similarly(first_mask) mask = (first_mask & second_mask & (~ np.isnan(first_pixel_data)) & (~ np.isnan(second_pixel_data))) if np.any(mask): # # Perform the correlation, which returns: # [ [ii, ij], # [ji, jj] ] # fi = first_pixel_data[mask] si = second_pixel_data[mask] n_objects = objects.count # Handle case when both images for the correlation are completely masked out if n_objects == 0: corr = np.zeros((0,)) overlap = np.zeros((0,)) K1 = np.zeros((0,)) K2 = np.zeros((0,)) M1 = np.zeros((0,)) M2 = np.zeros((0,)) RWC1 = np.zeros((0,)) RWC2 = np.zeros((0,)) C1 = np.zeros((0,)) C2 = np.zeros((0,)) elif np.where(mask)[0].__len__() == 0: corr = np.zeros((n_objects,)) corr[:] = np.NaN overlap = K1 = K2 = M1 = M2 = RWC1 = RWC2 = C1 = C2 = corr else: # # The correlation is sum((x-mean(x))(y-mean(y)) / # ((n-1) * std(x) *std(y))) # lrange = np.arange(n_objects, dtype=np.int32) + 1 area = fix(scind.sum(np.ones_like(labels), labels, lrange)) mean1 = fix(scind.mean(first_pixels, labels, lrange)) mean2 = fix(scind.mean(second_pixels, labels, lrange)) # # Calculate the standard deviation times the population. # std1 = np.sqrt(fix(scind.sum((first_pixels - mean1[labels - 1]) ** 2, labels, lrange))) std2 = np.sqrt(fix(scind.sum((second_pixels - mean2[labels - 1]) ** 2, labels, lrange))) x = first_pixels - mean1[labels - 1] # x - mean(x) y = second_pixels - mean2[labels - 1] # y - mean(y) corr = fix(scind.sum(x * y / (std1[labels - 1] * std2[labels - 1]), labels, lrange)) # Explicitly set the correlation to NaN for masked objects corr[scind.sum(1, labels, lrange) == 0] = np.NaN result += [ [first_image_name, second_image_name, object_name, "Mean Correlation coeff", "%.3f" % np.mean(corr)], [first_image_name, second_image_name, object_name, "Median Correlation coeff", "%.3f" % np.median(corr)], [first_image_name, second_image_name, object_name, "Min Correlation coeff", "%.3f" % np.min(corr)], [first_image_name, second_image_name, object_name, "Max Correlation coeff", "%.3f" % np.max(corr)]] # Threshold as percentage of maximum intensity of objects in each channel tff = (self.thr.value / 100) * fix(scind.maximum(first_pixels, labels, lrange)) tss = (self.thr.value / 100) * fix(scind.maximum(second_pixels, labels, lrange)) combined_thresh = (first_pixels >= tff[labels - 1]) & (second_pixels >= tss[labels - 1]) fi_thresh = first_pixels[combined_thresh] si_thresh = second_pixels[combined_thresh] tot_fi_thr = scind.sum(first_pixels[first_pixels >= tff[labels - 1]], labels[first_pixels >= tff[labels - 1]], lrange) tot_si_thr = scind.sum(second_pixels[second_pixels >= tss[labels - 1]], labels[second_pixels >= tss[labels - 1]], lrange) nonZero = (fi > 0) | (si > 0) xvar = np.var(fi[nonZero], axis=0, ddof=1) yvar = np.var(si[nonZero], axis=0, ddof=1) xmean = np.mean(fi[nonZero], axis=0) ymean = np.mean(si[nonZero], axis=0) z = fi[nonZero] + si[nonZero] zvar = np.var(z, axis=0, ddof=1) covar = 0.5 * (zvar - (xvar + yvar)) denom = 2 * covar num = (yvar - xvar) + np.sqrt((yvar - xvar) * (yvar - xvar) + 4 * (covar * covar)) a = (num / denom) b = (ymean - a * xmean) i = 1 while i > 0.003921568627: thr_fi_c = i thr_si_c = (a * i) + b combt = (fi < thr_fi_c) | (si < thr_si_c) costReg = scistat.pearsonr(fi[combt], si[combt]) if costReg[0] <= 0: break i = i - 0.003921568627 # Costes' thershold for entire image is applied to each object fi_above_thr = first_pixels > thr_fi_c si_above_thr = second_pixels > thr_si_c combined_thresh_c = fi_above_thr & si_above_thr fi_thresh_c = first_pixels[combined_thresh_c] si_thresh_c = second_pixels[combined_thresh_c] if np.any(fi_above_thr): tot_fi_thr_c = scind.sum(first_pixels[first_pixels >= thr_fi_c], labels[first_pixels >= thr_fi_c], lrange) else: tot_fi_thr_c = np.zeros(len(lrange)) if np.any(si_above_thr): tot_si_thr_c = scind.sum(second_pixels[second_pixels >= thr_si_c], labels[second_pixels >= thr_si_c], lrange) else: tot_si_thr_c = np.zeros(len(lrange)) # Manders Coefficient M1 = np.zeros(len(lrange)) M2 = np.zeros(len(lrange)) if np.any(combined_thresh): M1 = np.array(scind.sum(fi_thresh,labels[combined_thresh],lrange)) / np.array(tot_fi_thr) M2 = np.array(scind.sum(si_thresh,labels[combined_thresh],lrange)) / np.array(tot_si_thr) result += [[first_image_name, second_image_name, object_name,"Mean Manders coeff","%.3f"%np.mean(M1)], [first_image_name, second_image_name, object_name,"Median Manders coeff","%.3f"%np.median(M1)], [first_image_name, second_image_name, object_name,"Min Manders coeff","%.3f"%np.min(M1)], [first_image_name, second_image_name, object_name,"Max Manders coeff","%.3f"%np.max(M1)]] result += [[second_image_name, first_image_name, object_name,"Mean Manders coeff","%.3f"%np.mean(M2)], [second_image_name, first_image_name, object_name,"Median Manders coeff","%.3f"%np.median(M2)], [second_image_name, first_image_name, object_name,"Min Manders coeff","%.3f"%np.min(M2)], [second_image_name, first_image_name, object_name,"Max Manders coeff","%.3f"%np.max(M2)]] # RWC Coefficient RWC1 = np.zeros(len(lrange)) RWC2 = np.zeros(len(lrange)) [Rank1] = np.lexsort(([labels], [first_pixels])) [Rank2] = np.lexsort(([labels], [second_pixels])) Rank1_U = np.hstack([[False], first_pixels[Rank1[:-1]] != first_pixels[Rank1[1:]]]) Rank2_U = np.hstack([[False], second_pixels[Rank2[:-1]] != second_pixels[Rank2[1:]]]) Rank1_S = np.cumsum(Rank1_U) Rank2_S = np.cumsum(Rank2_U) Rank_im1 = np.zeros(first_pixels.shape, dtype=int) Rank_im2 = np.zeros(second_pixels.shape, dtype=int) Rank_im1[Rank1] = Rank1_S Rank_im2[Rank2] = Rank2_S R = max(Rank_im1.max(), Rank_im2.max()) + 1 Di = abs(Rank_im1 - Rank_im2) weight = (R - Di) * 1.0 / R weight_thresh = weight[combined_thresh] if np.any(combined_thresh_c): RWC1 = np.array(scind.sum(fi_thresh * weight_thresh, labels[combined_thresh], lrange)) / np.array( tot_fi_thr) RWC2 = np.array(scind.sum(si_thresh * weight_thresh, labels[combined_thresh], lrange)) / np.array( tot_si_thr) result += [[first_image_name, second_image_name, object_name, "Mean RWC coeff", "%.3f" % np.mean(RWC1)], [first_image_name, second_image_name, object_name, "Median RWC coeff", "%.3f" % np.median(RWC1)], [first_image_name, second_image_name, object_name, "Min RWC coeff", "%.3f" % np.min(RWC1)], [first_image_name, second_image_name, object_name, "Max RWC coeff", "%.3f" % np.max(RWC1)]] result += [[second_image_name, first_image_name, object_name, "Mean RWC coeff", "%.3f" % np.mean(RWC2)], [second_image_name, first_image_name, object_name, "Median RWC coeff", "%.3f" % np.median(RWC2)], [second_image_name, first_image_name, object_name, "Min RWC coeff", "%.3f" % np.min(RWC2)], [second_image_name, first_image_name, object_name, "Max RWC coeff", "%.3f" % np.max(RWC2)]] # Costes Automated Threshold C1 = np.zeros(len(lrange)) C2 = np.zeros(len(lrange)) if np.any(combined_thresh_c): C1 = np.array(scind.sum(fi_thresh_c,labels[combined_thresh_c],lrange)) / np.array(tot_fi_thr_c) C2 = np.array(scind.sum(si_thresh_c,labels[combined_thresh_c],lrange)) / np.array(tot_si_thr_c) result += [[first_image_name, second_image_name, object_name,"Mean Manders coeff (Costes)","%.3f"%np.mean(C1)], [first_image_name, second_image_name, object_name,"Median Manders coeff (Costes)","%.3f"%np.median(C1)], [first_image_name, second_image_name, object_name,"Min Manders coeff (Costes)","%.3f"%np.min(C1)], [first_image_name, second_image_name, object_name,"Max Manders coeff (Costes)","%.3f"%np.max(C1)] ] result += [[second_image_name, first_image_name, object_name,"Mean Manders coeff (Costes)","%.3f"%np.mean(C2)], [second_image_name, first_image_name, object_name,"Median Manders coeff (Costes)","%.3f"%np.median(C2)], [second_image_name, first_image_name, object_name,"Min Manders coeff (Costes)","%.3f"%np.min(C2)], [second_image_name, first_image_name, object_name,"Max Manders coeff (Costes)","%.3f"%np.max(C2)] ] # Overlap Coefficient fpsq = scind.sum(first_pixels[combined_thresh] ** 2, labels[combined_thresh], lrange) spsq = scind.sum(second_pixels[combined_thresh] ** 2, labels[combined_thresh], lrange) pdt = np.sqrt(np.array(fpsq) * np.array(spsq)) if np.any(combined_thresh): overlap = fix( scind.sum(first_pixels[combined_thresh] * second_pixels[combined_thresh], labels[combined_thresh], lrange) / pdt) K1 = fix((scind.sum(first_pixels[combined_thresh] * second_pixels[combined_thresh], labels[combined_thresh], lrange)) / (np.array(fpsq))) K2 = fix( scind.sum(first_pixels[combined_thresh] * second_pixels[combined_thresh], labels[combined_thresh], lrange) / np.array(spsq)) else: overlap = K1 = K2 = np.zeros(len(lrange)) result += [ [first_image_name, second_image_name, object_name, "Mean Overlap coeff", "%.3f" % np.mean(overlap)], [first_image_name, second_image_name, object_name, "Median Overlap coeff", "%.3f" % np.median(overlap)], [first_image_name, second_image_name, object_name, "Min Overlap coeff", "%.3f" % np.min(overlap)], [first_image_name, second_image_name, object_name, "Max Overlap coeff", "%.3f" % np.max(overlap)]] measurement = ("Correlation_Correlation_%s_%s" % (first_image_name, second_image_name)) overlap_measurement = (F_OVERLAP_FORMAT % (first_image_name, second_image_name)) k_measurement_1 = (F_K_FORMAT % (first_image_name, second_image_name)) k_measurement_2 = (F_K_FORMAT % (second_image_name, first_image_name)) manders_measurement_1 = (F_MANDERS_FORMAT % (first_image_name, second_image_name)) manders_measurement_2 = (F_MANDERS_FORMAT % (second_image_name, first_image_name)) rwc_measurement_1 = (F_RWC_FORMAT % (first_image_name, second_image_name)) rwc_measurement_2 = (F_RWC_FORMAT % (second_image_name, first_image_name)) costes_measurement_1 = (F_COSTES_FORMAT % (first_image_name, second_image_name)) costes_measurement_2 = (F_COSTES_FORMAT % (second_image_name, first_image_name)) workspace.measurements.add_measurement(object_name, measurement, corr) workspace.measurements.add_measurement(object_name, overlap_measurement, overlap) workspace.measurements.add_measurement(object_name, k_measurement_1, K1) workspace.measurements.add_measurement(object_name, k_measurement_2, K2) workspace.measurements.add_measurement(object_name, manders_measurement_1, M1) workspace.measurements.add_measurement(object_name, manders_measurement_2, M2) workspace.measurements.add_measurement(object_name, rwc_measurement_1, RWC1) workspace.measurements.add_measurement(object_name, rwc_measurement_2, RWC2) workspace.measurements.add_measurement(object_name, costes_measurement_1, C1) workspace.measurements.add_measurement(object_name, costes_measurement_2, C2) if n_objects == 0: return [[first_image_name, second_image_name, object_name, "Mean correlation", "-"], [first_image_name, second_image_name, object_name, "Median correlation", "-"], [first_image_name, second_image_name, object_name, "Min correlation", "-"], [first_image_name, second_image_name, object_name, "Max correlation", "-"]] else: return result
def test_sum02(): for type in types: input = np.zeros([0, 4], type) output = ndimage.sum(input) assert_equal(output, 0.0)
def test_sum01(): for type in types: input = np.array([], type) output = ndimage.sum(input) assert_equal(output, 0.0)
def measure_zernike(self, pixels, labels, n, m): # I'll put some documentation in here to explain what it does. # If someone ever wants to call it, their editor might display # the documentation. '''Measure the intensity of the image with Zernike (N, M) pixels - the intensity image to be measured labels - the labels matrix that labels each object with an integer n, m - the Zernike coefficients. See http://en.wikipedia.org/wiki/Zernike_polynomials for an explanation of the Zernike polynomials ''' # # The strategy here is to operate on the whole array instead # of operating on one object at a time. The most important thing # is to avoid having to run the Python interpreter once per pixel # in the image and the second most important is to avoid running # it per object in case there are hundreds of objects. # # We play lots of indexing tricks here to operate on the whole image. # I'll try to explain some - hopefully, you can reuse. # # You could move the calculation of the minimum enclosing circle # outside of this function. The function gets called more than # 10 times, so the same calculation is performed 10 times. # It would make the code a little more confusing, so I'm leaving # it as-is. ########################################### # # The minimum enclosing circle (MEC) is the smallest circle that # will fit around the object. We get the centers and radii of # all of the objects at once. You'll see how that lets us # compute the X and Y position of each pixel in a label all at # one go. # # First, get an array that lists the whole range of indexes in # the labels matrix. # indexes = np.arange(1, np.max(labels)+1,dtype=np.int32) # # Then ask for the minimum_enclosing_circle for each object named # in those indexes. MEC returns the i and j coordinate of the center # and the radius of the circle and that defines the circle entirely. # centers, radius = minimum_enclosing_circle(labels, indexes) center_x = centers[:, 1] center_y = centers[:, 0] # # Make up fake values for 0 (the background). This lets us do our # indexing tricks. Really, we're going to ignore the background, # but we want to do the indexing without ignoring the background # because that's easier. # center_x = np.hstack([[0], center_x]) center_y = np.hstack([[0], center_y]) radius = np.hstack([[1], radius]) # # Now get one array that's the y coordinate of each pixel and one # that's the x coordinate. This might look stupid and wasteful, # but these "arrays" are never actually realized and made into # real memory. # y, x = np.mgrid[0:labels.shape[0], 0:labels.shape[1]] # # Get the x and y coordinates relative to the object centers. # This uses Numpy broadcasting. For each pixel, we use the # value in the labels matrix as an index into the appropriate # one-dimensional array. So we get the value for that object. # y -= center_y[labels] x -= center_x[labels] # # Zernikes take x and y values from zero to one. We scale the # integer coordinate values by dividing them by the radius of # the circle. Again, we use the indexing trick to look up the # values for each object. # y = y.astype(float) / radius[labels] x = x.astype(float) / radius[labels] # ################################# # # ZERNIKE POLYNOMIALS # # Now we can get Zernike polynomials per-pixel where each pixel # value is calculated according to its object's MEC. # # We use a mask of all of the non-zero labels so the calculation # runs a little faster. # zernike_polynomial = construct_zernike_polynomials( x, y, np.array([ [ n, m ]])) # # Multiply the Zernike polynomial by the image to dissect # the image by the Zernike basis set. # output_pixels = pixels * zernike_polynomial[:,:,0] # # The zernike polynomial is a complex number. We get a power # spectrum here to combine the real and imaginary parts # output_pixels = np.sqrt(output_pixels * output_pixels.conjugate()) # # Finally, we use Scipy to sum the intensities. Scipy has different # versions with different quirks. The "fix" function takes all # of that into account. # # The sum function calculates the sum of the pixel values for # each pixel in an object, using the labels matrix to name # the pixels in an object # result = fix(scind.sum(output_pixels.real, labels, indexes)) # # And we're done! Did you like it? Did you get it? # return result
def skeleton_length(skeleton): ''' Length finding via morphological operators. We use the differences in connectivity between 4 and 8-connected to split regions. Connections between 4 and 8-connected regions are found using a series of hit-miss operators. The inputted skeleton MUST have no intersections otherwise the returned length will not be correct! Parameters ---------- skeleton : numpy.ndarray Array containing the skeleton. Returns ------- length : float Length of the skeleton. ''' # 4-connected labels four_labels = nd.label(skeleton)[0] four_sizes = nd.sum(skeleton, four_labels, range(np.max(four_labels) + 1)) # Lengths is the number of pixels minus number of objects with more # than 1 pixel. four_length = np.sum( four_sizes[four_sizes > 1]) - len(four_sizes[four_sizes > 1]) # Find pixels which a 4-connected and subtract them off the skeleton four_objects = np.where(four_sizes > 1)[0] skel_copy = copy.copy(skeleton) for val in four_objects: skel_copy[np.where(four_labels == val)] = 0 # Remaining pixels are only 8-connected # Lengths is same as before, multiplied by sqrt(2) eight_labels = nd.label(skel_copy, eight_con())[0] eight_sizes = nd.sum( skel_copy, eight_labels, range(np.max(eight_labels) + 1)) eight_length = ( np.sum(eight_sizes) - np.max(eight_labels)) * np.sqrt(2) # If there are no 4-connected pixels, we don't need the hit-miss portion. if four_length == 0.0: conn_length = 0.0 else: store = np.zeros(skeleton.shape) # Loop through the 4 rotations of the structuring elements for k in range(0, 4): hm1 = nd.binary_hit_or_miss( skeleton, structure1=np.rot90(struct1, k=k)) store += hm1 hm2 = nd.binary_hit_or_miss( skeleton, structure1=np.rot90(struct2, k=k)) store += hm2 hm_check3 = nd.binary_hit_or_miss( skeleton, structure1=np.rot90(check3, k=k)) store -= hm_check3 if k <= 1: hm_check1 = nd.binary_hit_or_miss( skeleton, structure1=np.rot90(check1, k=k)) store -= hm_check1 hm_check2 = nd.binary_hit_or_miss( skeleton, structure1=np.rot90(check2, k=k)) store -= hm_check2 conn_length = np.sqrt(2) * \ np.sum(np.sum(store, axis=1), axis=0) # hits return conn_length + eight_length + four_length
def _fix_small_holes(mask_array, rel_size=0.1): ''' Helper function to remove only small holes within a masked region. Parameters ---------- mask_array : numpy.ndarray Array containing the masked region. rel_size : float, optional If < 1.0, sets the minimum size a hole must be relative to the area of the mask. Otherwise, this is the maximum number of pixels the hole must have to be deleted. Returns ------- mask_array : numpy.ndarray Altered array. ''' if rel_size <= 0.0: raise ValueError("rel_size must be positive.") elif rel_size > 1.0: pixel_flag = True else: pixel_flag = False # Find the region area reg_area = len(np.where(mask_array == 1)[0]) # Label the holes holes = np.logical_not(mask_array).astype(float) lab_holes, n_holes = nd.label(holes, eight_con()) # If no holes, return if n_holes == 1: return mask_array # Ignore area outside of the region. out_label = lab_holes[0, 0] # Set size to be just larger than the region. Thus it can never be # deleted. holes[np.where(lab_holes == out_label)] = reg_area + 1. # Sum up the regions and find holes smaller than the threshold. sums = nd.sum(holes, lab_holes, range(1, n_holes + 1)) if pixel_flag: # Use number of pixels delete_holes = np.where(sums < rel_size)[0] else: # Use relative size of holes. delete_holes = np.where(sums / reg_area < rel_size)[0] # Return if there is nothing to delete. if delete_holes == []: return mask_array # Add one to take into account 0 in list if object label 1. delete_holes += 1 for label in delete_holes: mask_array[np.where(lab_holes == label)] = 1 return mask_array
axis=2).astype(uint8) ## Convert to black white image. The threshold here is currently set to 100. falseImage[falseImage > 100] = 255 falseImage[falseImage < 101] = 0 ## Remove spurs from the data falseImage = ndimage.binary_opening(falseImage) falseImage = ndimage.binary_closing(falseImage).astype(uint8) ## Further clean image by applying Gaussian filter falseImage = ndimage.gaussian_filter(falseImage, sigma=1 / (2.0)) ## Label all blobs in the image label_im, nb_labels = ndimage.label(falseImage) sizes = ndimage.sum(falseImage, label_im, range(nb_labels + 1)) ## Remove all blobs that have a surface area less than 1000 pixels mask_size = sizes < max(sizes) max_id = max(enumerate(sizes), key=lambda x: x[1])[0] mask_size = sizes < 1000 remove_pixel = mask_size[label_im] ## Find the main object in the scene. This is the larges blob. main_object = sizes < max(sizes) - 1 get_pixels = main_object[label_im] label_im[remove_pixel] = 0 data[get_pixels] = [0, 0, 0] ## Highlight main object in rgb label_im[label_im > 0] = 255
def test_sum04(): for type in types: input = np.array([1, 2], type) output = ndimage.sum(input) assert_almost_equal(output, 3.0)
def run(self, workspace): objects = workspace.object_set.get_objects(self.object_name.value) dimensions = len(objects.shape) assert isinstance(objects, cpo.Objects) has_pixels = objects.areas > 0 labels = objects.small_removed_segmented kept_labels = objects.segmented neighbor_objects = workspace.object_set.get_objects( self.neighbors_name.value) assert isinstance(neighbor_objects, cpo.Objects) neighbor_labels = neighbor_objects.small_removed_segmented # # Need to add in labels touching border. # unedited_segmented = neighbor_objects.unedited_segmented touching_border = np.zeros(np.max(unedited_segmented) + 1, bool) touching_border[unedited_segmented[0, :]] = True touching_border[unedited_segmented[-1, :]] = True touching_border[unedited_segmented[:, 0]] = True touching_border[unedited_segmented[:, -1]] = True touching_border[0] = False touching_border_mask = touching_border[unedited_segmented] nobjects = np.max(labels) nkept_objects = len(objects.indices) nneighbors = np.max(neighbor_labels) if np.any(touching_border) and np.all( ~touching_border_mask[neighbor_labels != 0]): # Add the border labels if any were excluded touching_border_object_number = np.cumsum( touching_border) + np.max(neighbor_labels) touching_border_mask = touching_border_mask & (neighbor_labels == 0) neighbor_labels = neighbor_labels.copy().astype(np.int32) neighbor_labels[ touching_border_mask] = touching_border_object_number[ unedited_segmented[touching_border_mask]] _, object_numbers = objects.relate_labels(labels, kept_labels) if self.neighbors_are_objects: neighbor_numbers = object_numbers neighbor_has_pixels = has_pixels else: _, neighbor_numbers = neighbor_objects.relate_labels( neighbor_labels, neighbor_objects.segmented) neighbor_has_pixels = (np.bincount( neighbor_objects.small_removed_segmented.ravel())[1:] > 0) neighbor_count = np.zeros((nobjects, )) pixel_count = np.zeros((nobjects, )) first_object_number = np.zeros((nobjects, ), int) second_object_number = np.zeros((nobjects, ), int) first_x_vector = np.zeros((nobjects, )) second_x_vector = np.zeros((nobjects, )) first_y_vector = np.zeros((nobjects, )) second_y_vector = np.zeros((nobjects, )) angle = np.zeros((nobjects, )) percent_touching = np.zeros((nobjects, )) expanded_labels = None if self.distance_method == D_EXPAND: # Find the i,j coordinates of the nearest foreground point # to every background point if dimensions == 2: i, j = scind.distance_transform_edt(labels == 0, return_distances=False, return_indices=True) # Assign each background pixel to the label of its nearest # foreground pixel. Assign label to label for foreground. labels = labels[i, j] else: k, i, j = scind.distance_transform_edt(labels == 0, return_distances=False, return_indices=True) labels = labels[k, i, j] expanded_labels = labels # for display distance = 1 # dilate once to make touching edges overlap scale = S_EXPANDED if self.neighbors_are_objects: neighbor_labels = labels.copy() elif self.distance_method == D_WITHIN: distance = self.distance.value scale = str(distance) elif self.distance_method == D_ADJACENT: distance = 1 scale = S_ADJACENT else: raise ValueError("Unknown distance method: %s" % self.distance_method.value) if nneighbors > (1 if self.neighbors_are_objects else 0): first_objects = [] second_objects = [] object_indexes = np.arange(nobjects, dtype=np.int32) + 1 # # First, compute the first and second nearest neighbors, # and the angles between self and the first and second # nearest neighbors # ocenters = centers_of_labels( objects.small_removed_segmented).transpose() ncenters = centers_of_labels( neighbor_objects.small_removed_segmented).transpose() areas = fix( scind.sum(np.ones(labels.shape), labels, object_indexes)) perimeter_outlines = outline(labels) perimeters = fix( scind.sum(np.ones(labels.shape), perimeter_outlines, object_indexes)) i, j = np.mgrid[0:nobjects, 0:nneighbors] distance_matrix = np.sqrt((ocenters[i, 0] - ncenters[j, 0])**2 + (ocenters[i, 1] - ncenters[j, 1])**2) # # order[:,0] should be arange(nobjects) # order[:,1] should be the nearest neighbor # order[:,2] should be the next nearest neighbor # if distance_matrix.shape[1] == 1: # a little buggy, lexsort assumes that a 2-d array of # second dimension = 1 is a 1-d array order = np.zeros(distance_matrix.shape, int) else: order = np.lexsort([distance_matrix]) first_neighbor = 1 if self.neighbors_are_objects else 0 first_object_index = order[:, first_neighbor] first_x_vector = ncenters[first_object_index, 1] - ocenters[:, 1] first_y_vector = ncenters[first_object_index, 0] - ocenters[:, 0] if nneighbors > first_neighbor + 1: second_object_index = order[:, first_neighbor + 1] second_x_vector = ncenters[second_object_index, 1] - ocenters[:, 1] second_y_vector = ncenters[second_object_index, 0] - ocenters[:, 0] v1 = np.array((first_x_vector, first_y_vector)) v2 = np.array((second_x_vector, second_y_vector)) # # Project the unit vector v1 against the unit vector v2 # dot = np.sum(v1 * v2, 0) / np.sqrt( np.sum(v1**2, 0) * np.sum(v2**2, 0)) angle = np.arccos(dot) * 180.0 / np.pi # Make the structuring element for dilation if dimensions == 2: strel = strel_disk(distance) else: strel = skimage.morphology.ball(distance) # # A little bigger one to enter into the border with a structure # that mimics the one used to create the outline # if dimensions == 2: strel_touching = strel_disk(distance + 0.5) else: strel_touching = skimage.morphology.ball(distance + 0.5) # # Get the extents for each object and calculate the patch # that excises the part of the image that is "distance" # away if dimensions == 2: i, j = np.mgrid[0:labels.shape[0], 0:labels.shape[1]] min_i, max_i, min_i_pos, max_i_pos = scind.extrema( i, labels, object_indexes) min_j, max_j, min_j_pos, max_j_pos = scind.extrema( j, labels, object_indexes) min_i = np.maximum(fix(min_i) - distance, 0).astype(int) max_i = np.minimum(fix(max_i) + distance + 1, labels.shape[0]).astype(int) min_j = np.maximum(fix(min_j) - distance, 0).astype(int) max_j = np.minimum(fix(max_j) + distance + 1, labels.shape[1]).astype(int) else: k, i, j = np.mgrid[0:labels.shape[0], 0:labels.shape[1], 0:labels.shape[2]] min_k, max_k, min_k_pos, max_k_pos = scind.extrema( k, labels, object_indexes) min_i, max_i, min_i_pos, max_i_pos = scind.extrema( i, labels, object_indexes) min_j, max_j, min_j_pos, max_j_pos = scind.extrema( j, labels, object_indexes) min_k = np.maximum(fix(min_k) - distance, 0).astype(int) max_k = np.minimum(fix(max_k) + distance + 1, labels.shape[0]).astype(int) min_i = np.maximum(fix(min_i) - distance, 0).astype(int) max_i = np.minimum(fix(max_i) + distance + 1, labels.shape[1]).astype(int) min_j = np.maximum(fix(min_j) - distance, 0).astype(int) max_j = np.minimum(fix(max_j) + distance + 1, labels.shape[2]).astype(int) # # Loop over all objects # Calculate which ones overlap "index" # Calculate how much overlap there is of others to "index" # for object_number in object_numbers: if object_number == 0: # # No corresponding object in small-removed. This means # that the object has no pixels, e.g., not renumbered. # continue index = object_number - 1 if dimensions == 2: patch = labels[min_i[index]:max_i[index], min_j[index]:max_j[index]] npatch = neighbor_labels[min_i[index]:max_i[index], min_j[index]:max_j[index]] else: patch = labels[min_k[index]:max_k[index], min_i[index]:max_i[index], min_j[index]:max_j[index], ] npatch = neighbor_labels[min_k[index]:max_k[index], min_i[index]:max_i[index], min_j[index]:max_j[index], ] # # Find the neighbors # patch_mask = patch == (index + 1) extended = scind.binary_dilation(patch_mask, strel) neighbors = np.unique(npatch[extended]) neighbors = neighbors[neighbors != 0] if self.neighbors_are_objects: neighbors = neighbors[neighbors != object_number] nc = len(neighbors) neighbor_count[index] = nc if nc > 0: first_objects.append(np.ones(nc, int) * object_number) second_objects.append(neighbors) if self.neighbors_are_objects: # # Find the # of overlapping pixels. Dilate the neighbors # and see how many pixels overlap our image. Use a 3x3 # structuring element to expand the overlapping edge # into the perimeter. # if dimensions == 2: outline_patch = (perimeter_outlines[ min_i[index]:max_i[index], min_j[index]:max_j[index]] == object_number) else: outline_patch = (perimeter_outlines[ min_k[index]:max_k[index], min_i[index]:max_i[index], min_j[index]:max_j[index], ] == object_number) extended = scind.binary_dilation( (patch != 0) & (patch != object_number), strel_touching) overlap = np.sum(outline_patch & extended) pixel_count[index] = overlap if sum([len(x) for x in first_objects]) > 0: first_objects = np.hstack(first_objects) reverse_object_numbers = np.zeros( max(np.max(object_numbers), np.max(first_objects)) + 1, int) reverse_object_numbers[object_numbers] = ( np.arange(len(object_numbers)) + 1) first_objects = reverse_object_numbers[first_objects] second_objects = np.hstack(second_objects) reverse_neighbor_numbers = np.zeros( max(np.max(neighbor_numbers), np.max(second_objects)) + 1, int) reverse_neighbor_numbers[neighbor_numbers] = ( np.arange(len(neighbor_numbers)) + 1) second_objects = reverse_neighbor_numbers[second_objects] to_keep = (first_objects > 0) & (second_objects > 0) first_objects = first_objects[to_keep] second_objects = second_objects[to_keep] else: first_objects = np.zeros(0, int) second_objects = np.zeros(0, int) if self.neighbors_are_objects: percent_touching = pixel_count * 100 / perimeters else: percent_touching = pixel_count * 100.0 / areas object_indexes = object_numbers - 1 neighbor_indexes = neighbor_numbers - 1 # # Have to recompute nearest # first_object_number = np.zeros(nkept_objects, int) second_object_number = np.zeros(nkept_objects, int) if nkept_objects > (1 if self.neighbors_are_objects else 0): di = (ocenters[object_indexes[:, np.newaxis], 0] - ncenters[neighbor_indexes[np.newaxis, :], 0]) dj = (ocenters[object_indexes[:, np.newaxis], 1] - ncenters[neighbor_indexes[np.newaxis, :], 1]) distance_matrix = np.sqrt(di * di + dj * dj) distance_matrix[~has_pixels, :] = np.inf distance_matrix[:, ~neighbor_has_pixels] = np.inf # # order[:,0] should be arange(nobjects) # order[:,1] should be the nearest neighbor # order[:,2] should be the next nearest neighbor # order = np.lexsort([distance_matrix ]).astype(first_object_number.dtype) if self.neighbors_are_objects: first_object_number[has_pixels] = order[has_pixels, 1] + 1 if nkept_objects > 2: second_object_number[has_pixels] = order[has_pixels, 2] + 1 else: first_object_number[has_pixels] = order[has_pixels, 0] + 1 if order.shape[1] > 1: second_object_number[has_pixels] = order[has_pixels, 1] + 1 else: object_indexes = object_numbers - 1 neighbor_indexes = neighbor_numbers - 1 first_objects = np.zeros(0, int) second_objects = np.zeros(0, int) # # Now convert all measurements from the small-removed to # the final number set. # neighbor_count = neighbor_count[object_indexes] neighbor_count[~has_pixels] = 0 percent_touching = percent_touching[object_indexes] percent_touching[~has_pixels] = 0 first_x_vector = first_x_vector[object_indexes] second_x_vector = second_x_vector[object_indexes] first_y_vector = first_y_vector[object_indexes] second_y_vector = second_y_vector[object_indexes] angle = angle[object_indexes] # # Record the measurements # assert isinstance(workspace, cpw.Workspace) m = workspace.measurements assert isinstance(m, cpmeas.Measurements) image_set = workspace.image_set features_and_data = [ (M_NUMBER_OF_NEIGHBORS, neighbor_count), (M_FIRST_CLOSEST_OBJECT_NUMBER, first_object_number), ( M_FIRST_CLOSEST_DISTANCE, np.sqrt(first_x_vector**2 + first_y_vector**2), ), (M_SECOND_CLOSEST_OBJECT_NUMBER, second_object_number), ( M_SECOND_CLOSEST_DISTANCE, np.sqrt(second_x_vector**2 + second_y_vector**2), ), (M_ANGLE_BETWEEN_NEIGHBORS, angle), ] if self.neighbors_are_objects: features_and_data.append((M_PERCENT_TOUCHING, percent_touching)) for feature_name, data in features_and_data: m.add_measurement(self.object_name.value, self.get_measurement_name(feature_name), data) if len(first_objects) > 0: m.add_relate_measurement( self.module_num, cpmeas.NEIGHBORS, self.object_name.value, self.object_name.value if self.neighbors_are_objects else self.neighbors_name.value, m.image_set_number * np.ones(first_objects.shape, int), first_objects, m.image_set_number * np.ones(second_objects.shape, int), second_objects, ) labels = kept_labels neighbor_count_image = np.zeros(labels.shape, int) object_mask = objects.segmented != 0 object_indexes = objects.segmented[object_mask] - 1 neighbor_count_image[object_mask] = neighbor_count[object_indexes] workspace.display_data.neighbor_count_image = neighbor_count_image if self.neighbors_are_objects: percent_touching_image = np.zeros(labels.shape) percent_touching_image[object_mask] = percent_touching[ object_indexes] workspace.display_data.percent_touching_image = percent_touching_image image_set = workspace.image_set if self.wants_count_image.value: neighbor_cm_name = self.count_colormap.value neighbor_cm = get_colormap(neighbor_cm_name) sm = matplotlib.cm.ScalarMappable(cmap=neighbor_cm) img = sm.to_rgba(neighbor_count_image)[:, :, :3] img[:, :, 0][~object_mask] = 0 img[:, :, 1][~object_mask] = 0 img[:, :, 2][~object_mask] = 0 count_image = cpi.Image(img, masking_objects=objects) image_set.add(self.count_image_name.value, count_image) else: neighbor_cm_name = cpprefs.get_default_colormap() neighbor_cm = matplotlib.cm.get_cmap(neighbor_cm_name) if self.neighbors_are_objects and self.wants_percent_touching_image: percent_touching_cm_name = self.touching_colormap.value percent_touching_cm = get_colormap(percent_touching_cm_name) sm = matplotlib.cm.ScalarMappable(cmap=percent_touching_cm) img = sm.to_rgba(percent_touching_image)[:, :, :3] img[:, :, 0][~object_mask] = 0 img[:, :, 1][~object_mask] = 0 img[:, :, 2][~object_mask] = 0 touching_image = cpi.Image(img, masking_objects=objects) image_set.add(self.touching_image_name.value, touching_image) else: percent_touching_cm_name = cpprefs.get_default_colormap() percent_touching_cm = matplotlib.cm.get_cmap( percent_touching_cm_name) if self.show_window: workspace.display_data.neighbor_cm_name = neighbor_cm_name workspace.display_data.percent_touching_cm_name = percent_touching_cm_name workspace.display_data.orig_labels = objects.segmented workspace.display_data.expanded_labels = expanded_labels workspace.display_data.object_mask = object_mask workspace.display_data.dimensions = dimensions
def fluor_polarity_txy(fluor_chan, bmask, cell_tracks): """ Calculates polarity of a fluorescence signal within an object at each time point across a time series. A DataFrame containing tracking info for these objects (a unique label for each tracked object and the x-y position at each time point) needs to be included as this function does not incorporate any tracking algorithms. See https://doi.org/10.1101/457119 for detailed descriptions of the 'Distance' and 'Angular' polarity metrics. Parameters ---------- fluor_chan: ndarray A 3d image stack (t, x, y) of the fluorescence signal to be measured. bmask: ndarray A 3d image stack (t, x, y) of binary masks of the objects containing the signal in 'fluor_chan' (e.g. cell 'footprints'). The shapes of 'fluor_chan' and 'bmask' must be identical. cell_tracks: DataFrame This DataFrame summarizes the x-y positions of previously-tracked objects that each have a unique label. EXACTLY four columns need to be present and named as follows: 'Time_s' contains the timepoints of each x-y frame in a 3d time series. 'Object_id' contains a unique label for each object that was tracked in time. 'X' contains the x coordinate of the geometric center of each object at each timepoint. 'Y' contains the y coordinate of the geometric center of each object at each timepoint. Returns ------- output: DataFrame This DataFrame contains the original 'Time_s' and 'Object_id' columns, with the further addition of 'Distance_polarity_score' and 'Angular_polarity_score' columns, indicating the polarity measurement of each object at each timepoint. """ assert type(bmask) is np.ndarray, "Binary masks are not a numpy array!" assert type( fluor_chan) is np.ndarray, "Fluorescence images are not a numpy array!" assert fluor_chan.shape == bmask.shape, "Fluorescence image and binary mask are different dimensions!" assert type( cell_tracks ) is pd.DataFrame, "'cell tracks' need to be formatted as a pandas DataFrame!" assert 'Time_s' in cell_tracks, "'cell_tracks' is missing 'Time_s' column!" assert 'Object_id' in cell_tracks, "'cell_tracks' is missing 'Object_id' column!" assert 'X' in cell_tracks and 'Y' in cell_tracks, "'cell_tracks' is missing 'X' and/or 'Y' column(s)!" assert len( cell_tracks.columns ) == 4, "'cell_tracks' must contain EXACTLY five columns labeled 'Time_s', 'Object_id, 'X', and 'Y'!" #reformat input DataFrame so order of columns is consistent sortdata = pd.DataFrame(columns=[]) sortdata['Time_s'] = cell_tracks['Time_s'] sortdata['Object_id'] = cell_tracks['Object_id'] sortdata['X'] = cell_tracks['X'] sortdata['Y'] = cell_tracks['Y'] cell_tracks = None cell_tracks = sortdata sortdata = None time_intv = cell_tracks.loc[1, 'Time_s'] - cell_tracks.loc[ 0, 'Time_s'] # for determining time interval between each frame final_table = pd.DataFrame(columns=[]) img_labels = np.zeros(bmask.shape, dtype=int) for x, frame in enumerate(bmask): img_labels[x] = sk.measure.label(frame) areascol = [] labelscol = [] objs = [] intlocscol = [] cenlocscol = [] xlist = [] major_axes_col = [] # label objects in binary mask and get x-y positions for the geometric center # and weighted fluorescence intensity center for each labeled object for x, frame in enumerate(img_labels): areas = [ r.area for r in sk.measure.regionprops(frame, coordinates='rc') ] labels = [ r.label for r in sk.measure.regionprops(frame, coordinates='rc') ] major_axes = [ r.major_axis_length for r in sk.measure.regionprops(frame, coordinates='rc') ] intlocs = [ list(r.weighted_centroid) for r in sk.measure.regionprops( frame, intensity_image=fluor_chan[x, :, :], coordinates='rc') ] cenlocs = [ list(r.weighted_centroid) for r in sk.measure.regionprops( frame, intensity_image=bmask[x, :, :], coordinates='rc') ] areascol.append(areas) labelscol.append(labels) intlocscol.append(intlocs) cenlocscol.append(cenlocs) major_axes_col.append(major_axes) y = 0 while y < np.amax(frame): xlist.append(x) y += 1 # make a numpy array from all the lists generated in preceding 'for' loop flatarea = mpu.datastructures.flatten(areascol) flatlabel = mpu.datastructures.flatten(labelscol) flatmajor_axes = mpu.datastructures.flatten(major_axes_col) flatcoords = mpu.datastructures.flatten(intlocscol) flatcoords = np.reshape(np.asarray(flatcoords), (len(flatcoords) // 2, 2)) flatcencoords = mpu.datastructures.flatten(cenlocscol) flatcencoords = np.reshape(np.asarray(flatcencoords), (len(flatcencoords) // 2, 2)) objs.append(xlist) objs.append(flatlabel) objs.append(flatarea) objs.append(flatmajor_axes) objs = np.transpose(np.asarray(objs)) objs = np.concatenate((objs, flatcoords, flatcencoords), axis=1) areascol = None labelscol = None intlocscol = None cenlocscol = None # normalize distances between geometric and fluorescence center to origin to make later calcuations easier absx = objs[:, 4] - objs[:, 6] absy = objs[:, 5] - objs[:, 7] absx = np.reshape(np.asarray(absx), (len(absx), 1)) absy = np.reshape(np.asarray(absy), (len(absy), 1)) objs = np.concatenate((objs, absx, absy), axis=1) flatlabel = None flatarea = None flatcencoords = None flatcoords = None absx = None absy = None collection = pd.DataFrame(objs, columns=[ 'Timepoint', 'Reg_Props_Obj_Num', 'Area', 'Major_axis_length', 'Y_intensity', 'X_intensity', 'Y_center', 'X_center', 'Y_adj', 'X_adj' ]) collection['Timepoint'] = (collection['Timepoint'] * time_intv).astype(int) collection['Reg_Props_Obj_Num'] = collection['Reg_Props_Obj_Num'].astype( int) collection['Distance_polarity_score'] = ( (collection['X_adj']**2 + collection['Y_adj']**2)** 0.5) / (collection['Major_axis_length'] / 2) objs = None polarity_scores_final = [] assert len(collection.columns) == 11 for x, frame in enumerate(img_labels): pointslist = [] weightedpointlist = [] obj_intensitylist = [] # find all x-y positions where there is an object present for index, item in np.ndenumerate(frame): if item > 0: nextpoint = [item, index] pointslist.append(nextpoint) pointslist.sort() subcollection = (collection[collection['Timepoint'] == ( x * time_intv)]).values # find the total intensity of each object in the current image frame z = 1 while z <= np.amax(frame): obj_intensity = ndimage.sum(fluor_chan[x, :, :], img_labels[x, :, :], index=z) obj_intensitylist.append(obj_intensity) z += 1 # for each point in object, find the consine between its vector and the "polarity" vector for y, item in enumerate(pointslist): objnum = item[0] xypos = item[1] center = (subcollection[(objnum - 1), 6], subcollection[(objnum - 1), 7]) fluorcenter = (subcollection[(objnum - 1), 4], subcollection[(objnum - 1), 5]) adjxypoint = np.subtract(xypos, center) adjxyfluor = np.subtract(fluorcenter, center) cosine = getcosine(adjxypoint, adjxyfluor) pointintensity = fluor_chan[x, xypos[0], xypos[1]] weightedpoint = cosine * pointintensity weightedpointlist.append(weightedpoint) weightedpointlist = np.asanyarray(weightedpointlist).astype(int) sumweightedpoints = 0 finalweights = [] # this sums together the values for all the individual points of a given object for y, item in enumerate(weightedpointlist): if y + 1 == len(weightedpointlist): sumweightedpoints = sumweightedpoints + weightedpointlist[y] finalweights.append(sumweightedpoints) sumweightedpoints = 0 elif pointslist[y][0] - pointslist[y + 1][0] == 0: sumweightedpoints = sumweightedpoints + weightedpointlist[y] elif pointslist[y][0] - pointslist[y + 1][0] == -1: sumweightedpoints = sumweightedpoints + weightedpointlist[y] finalweights.append(sumweightedpoints) sumweightedpoints = 0 polarity_scores = np.asanyarray(finalweights) / np.asarray( obj_intensitylist) polarity_scores_final.append(list(polarity_scores)) polarity_scores_final = mpu.datastructures.flatten(polarity_scores_final) polarity_scores_final = np.transpose(np.asarray(polarity_scores_final)) collection['Angular_polarity_score'] = polarity_scores_final # Below for loop matches values from the 'polarity scores array' to those in the DataFrame # containing the labeled tracks. This is needed since polarity scores are calculated for # every object, even ones that have no associated track label. xy_coords = np.zeros((len(collection), len(collection.columns)), dtype=float) for indx, row in cell_tracks.iterrows(): time_idx = row['Time_s'] x_idx = row['X'] lookup = (collection['Timepoint'] == time_idx) & (abs(collection['X_center'] - x_idx) < 0.001) extract = collection[lookup] # Below 'if' statement for catching cases where => 2 rows have very similar x-coords. # If true, also use y-coords for further discrimination if extract.shape[0] > 1: extract = None lookup = None y_idx = row['Y'] lookup = (collection['Timepoint'] == time_idx) & ( abs(collection['X_center'] - x_idx) < 0.001) & (abs(collection['Y_center'] - y_idx) < 0.001) extract = collection[lookup] extract = extract.values xy_coords[indx, :] = extract new_coords = pd.DataFrame({ 'X_intensity_center': xy_coords[:, 5], 'Y_intensity_center': xy_coords[:, 4], 'X_object_center': xy_coords[:, 7], 'Y_object_center': xy_coords[:, 6], 'Distance_polarity_score': xy_coords[:, 10], 'Angular_polarity_score': xy_coords[:, 11] }) cell_polarity_scores = cell_tracks.join(new_coords) output = pd.DataFrame(columns=[]) output['Object_id'] = cell_polarity_scores['Object_id'].astype(int) output['Time_s'] = cell_polarity_scores['Time_s'] output['Distance_polarity_score'] = cell_polarity_scores[ 'Distance_polarity_score'] output['Angular_polarity_score'] = cell_polarity_scores[ 'Angular_polarity_score'] return output
def do_measurements(self, workspace, image_name, object_name, center_object_name, center_choice, bin_count_settings, dd): '''Perform the radial measurements on the image set workspace - workspace that holds images / objects image_name - make measurements on this image object_name - make measurements on these objects center_object_name - use the centers of these related objects as the centers for radial measurements. None to use the objects themselves. center_choice - the user's center choice for this object: C_SELF, C_CENTERS_OF_OBJECTS or C_EDGES_OF_OBJECTS. bin_count_settings - the bin count settings group d - a dictionary for saving reusable partial results returns one statistics tuple per ring. ''' assert isinstance(workspace, cpw.Workspace) assert isinstance(workspace.object_set, cpo.ObjectSet) bin_count = bin_count_settings.bin_count.value wants_scaled = bin_count_settings.wants_scaled.value maximum_radius = bin_count_settings.maximum_radius.value image = workspace.image_set.get_image(image_name, must_be_grayscale=True) objects = workspace.object_set.get_objects(object_name) labels, pixel_data = cpo.crop_labels_and_image(objects.segmented, image.pixel_data) nobjects = np.max(objects.segmented) measurements = workspace.measurements assert isinstance(measurements, cpmeas.Measurements) heatmaps = {} for heatmap in self.heatmaps: if heatmap.object_name.get_objects_name() == object_name and \ image_name == heatmap.image_name.get_image_name() and \ heatmap.get_number_of_bins() == bin_count: dd[id(heatmap)] = \ heatmaps[MEASUREMENT_ALIASES[heatmap.measurement.value]] = \ np.zeros(labels.shape) if nobjects == 0: for bin in range(1, bin_count + 1): for feature in (F_FRAC_AT_D, F_MEAN_FRAC, F_RADIAL_CV): feature_name = ( (feature + FF_GENERIC) % (image_name, bin, bin_count)) measurements.add_measurement( object_name, "_".join([M_CATEGORY, feature_name]), np.zeros(0)) if not wants_scaled: measurement_name = "_".join([M_CATEGORY, feature, image_name, FF_OVERFLOW]) measurements.add_measurement( object_name, measurement_name, np.zeros(0)) return [(image_name, object_name, "no objects", "-", "-", "-", "-")] name = (object_name if center_object_name is None else "%s_%s" % (object_name, center_object_name)) if dd.has_key(name): normalized_distance, i_center, j_center, good_mask = dd[name] else: d_to_edge = distance_to_edge(labels) if center_object_name is not None: # # Use the center of the centering objects to assign a center # to each labeled pixel using propagation # center_objects = workspace.object_set.get_objects(center_object_name) center_labels, cmask = cpo.size_similarly( labels, center_objects.segmented) pixel_counts = fix(scind.sum( np.ones(center_labels.shape), center_labels, np.arange(1, np.max(center_labels) + 1, dtype=np.int32))) good = pixel_counts > 0 i, j = (centers_of_labels(center_labels) + .5).astype(int) ig = i[good] jg = j[good] lg = np.arange(1, len(i) + 1)[good] if center_choice == C_CENTERS_OF_OTHER: # # Reduce the propagation labels to the centers of # the centering objects # center_labels = np.zeros(center_labels.shape, int) center_labels[ig, jg] = lg cl, d_from_center = propagate(np.zeros(center_labels.shape), center_labels, labels != 0, 1) # # Erase the centers that fall outside of labels # cl[labels == 0] = 0 # # If objects are hollow or crescent-shaped, there may be # objects without center labels. As a backup, find the # center that is the closest to the center of mass. # missing_mask = (labels != 0) & (cl == 0) missing_labels = np.unique(labels[missing_mask]) if len(missing_labels): all_centers = centers_of_labels(labels) missing_i_centers, missing_j_centers = \ all_centers[:, missing_labels - 1] di = missing_i_centers[:, np.newaxis] - ig[np.newaxis, :] dj = missing_j_centers[:, np.newaxis] - jg[np.newaxis, :] missing_best = lg[np.argsort((di * di + dj * dj,))[:, 0]] best = np.zeros(np.max(labels) + 1, int) best[missing_labels] = missing_best cl[missing_mask] = best[labels[missing_mask]] # # Now compute the crow-flies distance to the centers # of these pixels from whatever center was assigned to # the object. # iii, jjj = np.mgrid[0:labels.shape[0], 0:labels.shape[1]] di = iii[missing_mask] - i[cl[missing_mask] - 1] dj = jjj[missing_mask] - j[cl[missing_mask] - 1] d_from_center[missing_mask] = np.sqrt(di * di + dj * dj) else: # Find the point in each object farthest away from the edge. # This does better than the centroid: # * The center is within the object # * The center tends to be an interesting point, like the # center of the nucleus or the center of one or the other # of two touching cells. # i, j = maximum_position_of_labels(d_to_edge, labels, objects.indices) center_labels = np.zeros(labels.shape, int) center_labels[i, j] = labels[i, j] # # Use the coloring trick here to process touching objects # in separate operations # colors = color_labels(labels) ncolors = np.max(colors) d_from_center = np.zeros(labels.shape) cl = np.zeros(labels.shape, int) for color in range(1, ncolors + 1): mask = colors == color l, d = propagate(np.zeros(center_labels.shape), center_labels, mask, 1) d_from_center[mask] = d[mask] cl[mask] = l[mask] good_mask = cl > 0 if center_choice == C_EDGES_OF_OTHER: # Exclude pixels within the centering objects # when performing calculations from the centers good_mask = good_mask & (center_labels == 0) i_center = np.zeros(cl.shape) i_center[good_mask] = i[cl[good_mask] - 1] j_center = np.zeros(cl.shape) j_center[good_mask] = j[cl[good_mask] - 1] normalized_distance = np.zeros(labels.shape) if wants_scaled: total_distance = d_from_center + d_to_edge normalized_distance[good_mask] = (d_from_center[good_mask] / (total_distance[good_mask] + .001)) else: normalized_distance[good_mask] = \ d_from_center[good_mask] / maximum_radius dd[name] = [normalized_distance, i_center, j_center, good_mask] ngood_pixels = np.sum(good_mask) good_labels = labels[good_mask] bin_indexes = (normalized_distance * bin_count).astype(int) bin_indexes[bin_indexes > bin_count] = bin_count labels_and_bins = (good_labels - 1, bin_indexes[good_mask]) histogram = coo_matrix((pixel_data[good_mask], labels_and_bins), (nobjects, bin_count + 1)).toarray() sum_by_object = np.sum(histogram, 1) sum_by_object_per_bin = np.dstack([sum_by_object] * (bin_count + 1))[0] fraction_at_distance = histogram / sum_by_object_per_bin number_at_distance = coo_matrix((np.ones(ngood_pixels), labels_and_bins), (nobjects, bin_count + 1)).toarray() object_mask = number_at_distance > 0 sum_by_object = np.sum(number_at_distance, 1) sum_by_object_per_bin = np.dstack([sum_by_object] * (bin_count + 1))[0] fraction_at_bin = number_at_distance / sum_by_object_per_bin mean_pixel_fraction = fraction_at_distance / (fraction_at_bin + np.finfo(float).eps) masked_fraction_at_distance = masked_array(fraction_at_distance, ~object_mask) masked_mean_pixel_fraction = masked_array(mean_pixel_fraction, ~object_mask) # Anisotropy calculation. Split each cell into eight wedges, then # compute coefficient of variation of the wedges' mean intensities # in each ring. # # Compute each pixel's delta from the center object's centroid i, j = np.mgrid[0:labels.shape[0], 0:labels.shape[1]] imask = i[good_mask] > i_center[good_mask] jmask = j[good_mask] > j_center[good_mask] absmask = (abs(i[good_mask] - i_center[good_mask]) > abs(j[good_mask] - j_center[good_mask])) radial_index = (imask.astype(int) + jmask.astype(int) * 2 + absmask.astype(int) * 4) statistics = [] for bin in range(bin_count + (0 if wants_scaled else 1)): bin_mask = (good_mask & (bin_indexes == bin)) bin_pixels = np.sum(bin_mask) bin_labels = labels[bin_mask] bin_radial_index = radial_index[bin_indexes[good_mask] == bin] labels_and_radii = (bin_labels - 1, bin_radial_index) radial_values = coo_matrix((pixel_data[bin_mask], labels_and_radii), (nobjects, 8)).toarray() pixel_count = coo_matrix((np.ones(bin_pixels), labels_and_radii), (nobjects, 8)).toarray() mask = pixel_count == 0 radial_means = masked_array(radial_values / pixel_count, mask) radial_cv = np.std(radial_means, 1) / np.mean(radial_means, 1) radial_cv[np.sum(~mask, 1) == 0] = 0 for measurement, feature, overflow_feature in ( (fraction_at_distance[:, bin], MF_FRAC_AT_D, OF_FRAC_AT_D), (mean_pixel_fraction[:, bin], MF_MEAN_FRAC, OF_MEAN_FRAC), (np.array(radial_cv), MF_RADIAL_CV, OF_RADIAL_CV)): if bin == bin_count: measurement_name = overflow_feature % image_name else: measurement_name = feature % (image_name, bin + 1, bin_count) measurements.add_measurement(object_name, measurement_name, measurement) if feature in heatmaps: heatmaps[feature][bin_mask] = measurement[bin_labels - 1] radial_cv.mask = np.sum(~mask, 1) == 0 bin_name = str(bin + 1) if bin < bin_count else "Overflow" statistics += [(image_name, object_name, bin_name, str(bin_count), round(np.mean(masked_fraction_at_distance[:, bin]), 4), round(np.mean(masked_mean_pixel_fraction[:, bin]), 4), round(np.mean(radial_cv), 4))] return statistics
def fluor_polarity_xy(fluor_chan, bmask): """ Calculates polarity of a fluorescence signal within objects in a single image. See https://doi.org/10.1101/457119 for detailed descriptions of the 'Distance' and 'Angular' polarity metrics. Parameters ---------- fluor_chan: ndarray A 2d image (x, y) of the fluorescence signal to be measured. bmask: ndarray A 2d image (x, y) of binary masks of the objects containing the signal in 'fluor_chan' (e.g. cell 'footprints'). The shapes of 'fluor_chan' and 'bmask' must be identical. Returns ------- output: DataFrame This DataFrame contains five columns: 'Object_id': Label for each object in the binary mask. 'X_center': The x-coordinate of the geometric center of the object in the binary mask. 'Y_center': The y-coordinate of the geometric center of the object in the binary mask. 'Distance_polarity_score': Polarity score based on the distance between the center of fluorescence intensity and the geometric center of the object. 'Angular_polarity_score': Polarity score based on the angular distribution of the fluorescence signal about the geometric center of the object. """ assert type(bmask) is np.ndarray, "Binary masks are not a numpy array!" assert type( fluor_chan) is np.ndarray, "Fluorescence images are not a numpy array!" assert fluor_chan.shape == bmask.shape, "Fluorescence image and binary mask are different dimensions!" final_table = pd.DataFrame(columns=[]) img_labels = np.zeros(bmask.shape, dtype=int) img_labels = sk.measure.label(bmask) areascol = [] labelscol = [] objs = [] intlocscol = [] cenlocscol = [] xlist = [] major_axes_col = [] # label objects in binary mask and get x-y positions for the geometric center # and weighted fluorescence intensity center for each labeled object areas = [ r.area for r in sk.measure.regionprops(img_labels, coordinates='rc') ] labels = [ r.label for r in sk.measure.regionprops(img_labels, coordinates='rc') ] major_axes = [ r.major_axis_length for r in sk.measure.regionprops(img_labels, coordinates='rc') ] intlocs = [ list(r.weighted_centroid) for r in sk.measure.regionprops( img_labels, intensity_image=fluor_chan[:, :], coordinates='rc') ] cenlocs = [ list(r.weighted_centroid) for r in sk.measure.regionprops( img_labels, intensity_image=bmask[:, :], coordinates='rc') ] areascol.append(areas) labelscol.append(labels) intlocscol.append(intlocs) cenlocscol.append(cenlocs) major_axes_col.append(major_axes) # make a numpy array from all the lists generated in preceding 'for' loop flatarea = mpu.datastructures.flatten(areascol) flatlabel = mpu.datastructures.flatten(labelscol) flatmajor_axes = mpu.datastructures.flatten(major_axes_col) flatcoords = mpu.datastructures.flatten(intlocscol) flatcoords = np.reshape(np.asarray(flatcoords), (len(flatcoords) // 2, 2)) flatcencoords = mpu.datastructures.flatten(cenlocscol) flatcencoords = np.reshape(np.asarray(flatcencoords), (len(flatcencoords) // 2, 2)) objs.append(flatlabel) objs.append(flatarea) objs.append(flatmajor_axes) objs = np.transpose(np.asarray(objs)) objs = np.concatenate((objs, flatcoords, flatcencoords), axis=1) areascol = None labelscol = None intlocscol = None cenlocscol = None # normalize distances between geometric and fluorescence center to origin to make later calcuations easier absx = objs[:, 3] - objs[:, 5] absy = objs[:, 4] - objs[:, 6] absx = np.reshape(np.asarray(absx), (len(absx), 1)) absy = np.reshape(np.asarray(absy), (len(absy), 1)) objs = np.concatenate((objs, absx, absy), axis=1) flatlabel = None flatarea = None flatcencoords = None flatcoords = None absx = None absy = None collection = pd.DataFrame(objs, columns=[ 'Reg_Props_Obj_Num', 'Area', 'Major_axis_length', 'Y_intensity', 'X_intensity', 'Y_center', 'X_center', 'Y_adj', 'X_adj' ]) collection['Reg_Props_Obj_Num'] = collection['Reg_Props_Obj_Num'].astype( int) collection['Distance_polarity_score'] = ( (collection['X_adj']**2 + collection['Y_adj']**2)** 0.5) / (collection['Major_axis_length'] / 2) objs = None polarity_scores_final = [] assert len(collection.columns) == 10 pointslist = [] weightedpointlist = [] obj_intensitylist = [] # find all x-y positions where there is an object present for index, item in np.ndenumerate(img_labels): if item > 0: nextpoint = [item, index] pointslist.append(nextpoint) pointslist.sort() subcollection = collection.values # find the total intensity of each object in the current image frame z = 1 while z <= np.amax(img_labels): obj_intensity = ndimage.sum(fluor_chan[:, :], img_labels[:, :], index=z) obj_intensitylist.append(obj_intensity) z += 1 # for each point in object, find the consine between its vector and the "polarity" vector y = 0 for y, item in enumerate(pointslist): objnum = item[0] xypos = item[1] center = (subcollection[(objnum - 1), 5], subcollection[(objnum - 1), 6]) fluorcenter = (subcollection[(objnum - 1), 3], subcollection[(objnum - 1), 4]) adjxypoint = np.subtract(xypos, center) adjxyfluor = np.subtract(fluorcenter, center) cosine = getcosine(adjxypoint, adjxyfluor) pointintensity = fluor_chan[xypos[0], xypos[1]] weightedpoint = cosine * pointintensity weightedpointlist.append(weightedpoint) weightedpointlist = np.asanyarray(weightedpointlist).astype(int) sumweightedpoints = 0 finalweights = [] # this sums together the values for all the individual points of a given object for y, item in enumerate(weightedpointlist): if y + 1 == len(weightedpointlist): sumweightedpoints = sumweightedpoints + weightedpointlist[y] finalweights.append(sumweightedpoints) sumweightedpoints = 0 elif pointslist[y][0] - pointslist[y + 1][0] == 0: sumweightedpoints = sumweightedpoints + weightedpointlist[y] elif pointslist[y][0] - pointslist[y + 1][0] == -1: sumweightedpoints = sumweightedpoints + weightedpointlist[y] finalweights.append(sumweightedpoints) sumweightedpoints = 0 polarity_scores = np.asanyarray(finalweights) / np.asarray( obj_intensitylist) polarity_scores_final.append(list(polarity_scores)) polarity_scores_final = mpu.datastructures.flatten(polarity_scores_final) polarity_scores_final = np.transpose(np.asarray(polarity_scores_final)) collection['Angular_polarity_score'] = polarity_scores_final output = pd.DataFrame(columns=[]) output['Object_id'] = collection['Reg_Props_Obj_Num'].astype(int) output['X_center'] = collection['X_center'] output['Y_center'] = collection['Y_center'] output['Distance_polarity_score'] = collection['Distance_polarity_score'] output['Angular_polarity_score'] = collection['Angular_polarity_score'] return output
def count_in_circle(imagen, r, centro=None): labels = ring_mask(imagen.shape[0], 0, r, center=centro) return nd.sum(np.zeros(imagen.shape), labels, 1)
def place_seed_points(image, img, mask, n_segments, spacing, q=99.99): """ Method for placing seed points in an ROI Note: Optimal point placement problem is somewhat related to the k-center problem metric facility location (MFL) Maxmin facility location https://en.wikipedia.org/wiki/Facility_location_problem Parameters ---------- image mask n_segments spacing Returns ------- """ segments_z = np.zeros(n_segments, dtype=np.int64) segments_y = np.zeros(n_segments, dtype=np.int64) segments_x = np.zeros(n_segments, dtype=np.int64) m_inv = np.copy(mask) nz = np.nonzero(m_inv) p = [np.min(nz[0]), np.min(nz[1]), np.min(nz[2])] pend = [np.max(nz[0]), np.max(nz[1]), np.max(nz[2])] # cropping to bounding box around ROI m_inv = m_inv[p[0]:pend[0] + 1, p[1]:pend[1] + 1, p[2]:pend[2] + 1] # SEED STEP 1: n seeds are placed as far as possible from every other seed and the edge. theta = 0 for ii in range(n_segments): # distance transform dtrans = distance_transform_edt(m_inv, sampling=spacing) dtrans = gaussian_filter(dtrans, sigma=2.0) # dtransg = ndi.gaussian_gradient_magnitude(dtrans, sigma=2.0) # plt.figure() # plt.imshow(mask[0, :, :]) # plt.figure() # plt.imshow(m_inv[0, :, :]) # plt.show() perc1 = np.percentile(dtrans, q=q) mask_dtrans = dtrans > perc1 pdtrans, nb_labels = ndi.label(mask_dtrans) # plt.figure() # plt.imshow(pdtrans[0, :, :]) # plt.show() # TODO temporarily just sticking to the original method until new method has been validated. if ii < 1E6: sizes = ndi.sum(mask_dtrans, pdtrans, range(nb_labels + 1)) # Use the maximum locations for the first two points coords1 = np.nonzero(pdtrans == np.argmax(sizes)) segments_z[ii] = round(np.mean(coords1[0])) segments_x[ii] = round(np.mean(coords1[1])) segments_y[ii] = round(np.mean(coords1[2])) else: # Define a vector that is used to produce a reference frame u = np.array( [segments_x[1] - segments_x[0], segments_y[1] - segments_y[0]]) u = u / np.sqrt(np.sum(u**2)) phi = np.zeros((nb_labels, )) for vv in range(nb_labels): coords1 = np.nonzero(pdtrans == (vv + 1)) v = np.array([ np.mean(coords1[1]) - segments_x[0], np.mean(coords1[2] - segments_y[0]) ]) v = v / np.sqrt(np.sum(v**2)) # Directed angle va = np.arctan2(v[1], v[0]) if va < 0: va += 2 * np.pi ua = np.arctan2(u[1], u[0]) if ua < 0: ua += 2 * np.pi phi[vv] = va - ua if phi[vv] < 0: phi[vv] += 2 * np.pi # Difference between previous theta and current theta phidiff = phi - theta # print("phidiff 1: ", phidiff) phidiff += (phidiff < 0) * 2 * np.pi # print("phidiff 2: ", phidiff) iphi = np.argmin(phidiff) theta = phi[iphi] # print("theta: ", theta) coords1 = np.nonzero(pdtrans == (iphi + 1)) segments_z[ii] = round(np.mean(coords1[0])) segments_x[ii] = round(np.mean(coords1[1])) segments_y[ii] = round(np.mean(coords1[2])) # adding a new point m_inv[segments_z[ii], segments_x[ii], segments_y[ii]] = False # Plot: Illustrate the seed point selection method # plt.figure() # plt.imshow(img) # my_cmap = plt.cm.get_cmap('jet') # get a copy of the gray color map # my_cmap.set_bad(alpha=0) # s # d11 = dtrans[segments_z[ii], :, :] # d11[d11==0] = np.nan # plt.imshow(d11, cmap=my_cmap) # plt.contour(mask[segments_z[ii], :, :] == 1, contours=1, colors='red', linewidths=1) # plt.plot(segments_y[ii], segments_x[ii], marker='o', color='green') # plt.axis('off') # plt.show() segments_z = segments_z + p[0] segments_x = segments_x + p[1] segments_y = segments_y + p[2] segments_color = np.zeros((segments_z.shape[0], image.shape[3])) segments = np.concatenate([ segments_z[..., np.newaxis], segments_x[..., np.newaxis], segments_y[..., np.newaxis], segments_color ], axis=1) sz = np.ascontiguousarray(segments_z, dtype=np.int32) sx = np.ascontiguousarray(segments_x, dtype=np.int32) sy = np.ascontiguousarray(segments_y, dtype=np.int32) out1 = get_mpd(sz, sx, sy) step_z, step_x, step_y = out1[0], out1[1], out1[2] return segments, step_x, step_y, step_z
def objstats(args): # Open and read from image and segmentation try: img_ds = gdal.Open(args.image, gdal.GA_ReadOnly) except: logger.error('Could not open image: {}'.format(args.image)) sys.exit(1) try: seg_ds = ogr.Open(args.segment, 0) seg_layer = seg_ds.GetLayer() except: logger.error('Could not open segmentation vector file: {}'.format( args.segment)) sys.exit(1) cols, rows = img_ds.RasterXSize, img_ds.RasterYSize bands = range(1, img_ds.RasterCount + 1) if args.bands is not None: bands = args.bands # Rasterize segments logger.debug('About to rasterize segment vector file') img_srs = osr.SpatialReference() img_srs.ImportFromWkt(img_ds.GetProjectionRef()) mem_raster = gdal.GetDriverByName('MEM').Create( '', cols, rows, 1, gdal.GDT_UInt32) mem_raster.SetProjection(img_ds.GetProjection()) mem_raster.SetGeoTransform(img_ds.GetGeoTransform()) # Create artificial 'FID' field fid_layer = seg_ds.ExecuteSQL( 'select FID, * from "{l}"'.format(l=seg_layer.GetName())) gdal.RasterizeLayer(mem_raster, [1], fid_layer, options=['ATTRIBUTE=FID']) logger.debug('Rasterized segment vector file') seg = mem_raster.GetRasterBand(1).ReadAsArray() logger.debug('Read segmentation image into memory') mem_raster = None seg_ds = None # Get list of unique segments useg = np.unique(seg) # If calc is num, do only for 1 band out_bands = 0 for stat in args.stat: if stat == 'num': out_bands += 1 else: out_bands += len(bands) # Create output driver driver = gdal.GetDriverByName(args.format) out_ds = driver.Create(args.output, cols, rows, out_bands, gdal.GDT_Float32) # Loop through image bands out_b = 0 out_2d = np.empty_like(seg, dtype=np.float32) for i_b, b in enumerate(bands): img_band = img_ds.GetRasterBand(b) ndv = img_band.GetNoDataValue() band_name = img_band.GetDescription() if not band_name: band_name = 'Band {i}'.format(i=b) logger.info('Processing input band {i}, "{b}"'.format( i=b, b=band_name)) img = img_band.ReadAsArray().astype( gdal_array.GDALTypeCodeToNumericTypeCode(img_band.DataType)) logger.debug('Read image band {i}, "{b}" into memory'.format( i=b, b=band_name)) for stat in args.stat: logger.debug(' calculating {s}'.format(s=stat)) if stat == 'mean': out = ndimage.mean(img, seg, useg) elif stat == 'var': out = ndimage.variance(img, seg, useg) elif stat == 'num': # Remove from list of stats so it is only calculated once args.stat.remove('num') count = np.ones_like(seg) out = ndimage.sum(count, seg, useg) elif stat == 'sum': out = ndimage.sum(img, seg, useg) elif stat == 'min': out = ndimage.minimum(img, seg, useg) elif stat == 'max': out = ndimage.maximum(img, seg, useg) elif stat == 'mode': out = ndimage.labeled_comprehension(img, seg, useg, scipy_mode, out_2d.dtype, ndv) else: logger.error('Unknown stat. Not sure how you got here') sys.exit(1) # Transform to 2D out_2d = out[seg - seg.min()] # Fill in NDV if ndv is not None: out_2d[np.where(img == ndv)] = ndv # Write out the data out_band = out_ds.GetRasterBand(out_b + 1) out_band.SetDescription(band_name) if ndv is not None: out_band.SetNoDataValue(ndv) logger.debug(' Writing object statistic for band {b}'.format( b=b + 1)) out_band.WriteArray(out_2d, 0, 0) out_band.FlushCache() logger.debug(' Wrote out object statistic for band {b}'.format( b=b + 1)) out_b += 1 out_ds.SetGeoTransform(img_ds.GetGeoTransform()) out_ds.SetProjection(img_ds.GetProjection()) img_ds = None seg_ds = None out_ds = None logger.info('Completed object statistic calculation')
def threshold_components(A_s, shape, min_size=5, max_size=np.inf, max_perc=.3): """ Threshold components output of a CNMF algorithm (A matrices) Parameters: ---------- A_s: list list of A matrice output from CNMF min_size: int min size of the component in pixels max_size: int max size of the component in pixels max_perc: float fraction of the maximum of each component used to threshold Returns: ------- B_s: list of the thresholded components lab_imgs: image representing the components in ndimage format cm_s: center of masses of each components """ B_s = [] lab_imgs = [] cm_s = [] for A_ in A_s: print('*') max_comps = A_.max(0).todense().T tmp = [] cm = [] lim = np.zeros(shape) for idx, a in enumerate(A_.T): #create mask by thresholding to 50% of the max print(idx) mask = np.reshape(a.todense() > (max_comps[idx] * max_perc), shape) label_im, nb_labels = ndi.label(mask) sizes = ndi.sum(mask, label_im, list(range(nb_labels + 1))) l_largest = (label_im == np.argmax(sizes)) cm.append( scipy.ndimage.measurements.center_of_mass( l_largest, l_largest)) lim[l_largest] = (idx + 1) # #remove connected components that are too small mask_size = np.logical_or(sizes < min_size, sizes > max_size) if np.sum(mask_size[1:]) > 1: print(('removing ' + str(np.sum(mask_size[1:]) - 1) + ' components')) remove_pixel = mask_size[label_im] label_im[remove_pixel] = 0 label_im = (label_im > 0) * 1 tmp.append(label_im.flatten()) cm_s.append(cm) lab_imgs.append(lim) B_s.append(csc.csc_matrix(np.array(tmp)).T) return B_s, lab_imgs, cm_s
wall_centers = dict(zip([(l,r) for (l,r) in wall_cells], wall_csv_data[['wall_center_'+dim for dim in ['x','y','z']]].values)) wall_normals = dict(zip([(l,r) for (l,r) in wall_cells], wall_csv_data[['wall_normal_'+dim for dim in ['x','y','z']]].values)) # -- oriented labelpairs dict (ie. dict[i,j]==dict[j,i]): pin1_orientations = dict(zip([(l,r) for (l,r) in wall_cells], wall_csv_data[sig_ch+'_orientation'].values)) pin1_intensities = dict(zip([(l,r) for (l,r) in wall_cells], wall_csv_data[sig_ch+'_signal'].values)) # pin1_shifts = dict(zip([(l,r) for (l,r) in wall_cells],wall_csv_data['pin1_shift'].values)) pin1_intensities_left = dict(zip([(l,r) for (l,r) in wall_cells], wall_csv_data[sig_ch+'_signal_left'].values)) pin1_intensities_right = dict(zip([(l,r) for (l,r) in wall_cells] wall_csv_data[sig_ch+'_signal_right'].values)) wall_pin1_vectors={} for left_label, right_label in wall_cells: wall_pin1_vectors[(left_label, right_label)] = pin1_orientations[(left_label, right_label)] * wall_normals[(left_label, right_label)] # cell_centers = dict(zip(np.unique(wall_cells),[p_img.image_property('barycenter')[c][:2] for c in np.unique(wall_cells)])) cell_centers = dict(zip(np.unique(wall_cells), np.transpose([nd.sum(wall_csv_data['wall_center_'+dim].values ,wall_cells[:,0], index=np.unique(wall_cells)) for dim in ['x','y']])/nd.sum(np.ones_like(wall_cells[:,0]),wall_cells[:,0],index=np.unique(wall_cells))[:,np.newaxis])) weights = np.ones_like(wall_cells[:,0]).astype(float) weights *= np.array([wall_areas[(l,r)] for (l,r) in wall_cells]) # weights *= np.array([pin1_intensities[(l,r)] for (l,r) in wall_cells]) weights *= np.array([np.abs(pin1_intensities_left[(l,r)]-pin1_intensities_right[(l,r)]) for (l,r) in wall_cells]) wall_weights = dict(zip([(l,r) for (l,r) in wall_cells],weights)) cell_pin1_vectors = dict(zip(np.unique(wall_cells),np.transpose([nd.sum(np.array([wall_weights[(l,r)]*wall_pin1_vectors[(l,r)][k] for (l,r) in wall_cells]),wall_cells[:,0],index=np.unique(wall_cells)) for k in xrange(2)])/nd.sum(np.array([wall_weights[(l,r)] for (l,r) in wall_cells]),wall_cells[:,0],index=np.unique(wall_cells))[:,np.newaxis])) cell_labels = p_img.image_property('barycenter').keys()[p_img.image_property('layer').values()==1] X = microscope_orientation*p_img.image_property('barycenter').values()[:,0][p_img.image_property('layer').values()==1] Y = microscope_orientation*p_img.image_property('barycenter').values()[:,1][p_img.image_property('layer').values()==1] Z = microscope_orientation*p_img.image_property('barycenter').values()[:,2][p_img.image_property('layer').values()==1]
def sum_in_circle(imagen, r, centro=None): labels = ring_mask(imagen.shape[0], 0, r, center=centro) return nd.sum(imagen, labels, 1)
def test_sum05(): for type in types: input = np.array([[1, 2], [3, 4]], type) output = ndimage.sum(input) assert_almost_equal(output, 10.0)
def run(self, workspace): if self.show_window: workspace.display_data.col_labels = ("Image", "channel", "Object", "Feature", "Mean", "Median", "STD") workspace.display_data.statistics = statistics = [] for im in self.images: image_name = im.name image = workspace.image_set.get_image(image_name.value, must_be_grayscale=False) nchan = im.nchannels.value for channel in range(nchan): for object_name in [obj.name for obj in self.objects]: # Need to refresh image after each iteration... if nchan == 1: img = image.pixel_data.copy() else: img = image.pixel_data[:, :, channel].squeeze().copy() if image.has_mask: masked_image = img.copy() masked_image[~image.mask] = 0 else: masked_image = img objects = workspace.object_set.get_objects( object_name.value) nobjects = objects.count integrated_intensity = np.zeros((nobjects, )) integrated_intensity_edge = np.zeros((nobjects, )) mean_intensity = np.zeros((nobjects, )) mean_intensity_edge = np.zeros((nobjects, )) std_intensity = np.zeros((nobjects, )) std_intensity_edge = np.zeros((nobjects, )) min_intensity = np.zeros((nobjects, )) min_intensity_edge = np.zeros((nobjects, )) max_intensity = np.zeros((nobjects, )) max_intensity_edge = np.zeros((nobjects, )) mass_displacement = np.zeros((nobjects, )) lower_quartile_intensity = np.zeros((nobjects, )) median_intensity = np.zeros((nobjects, )) mad_intensity = np.zeros((nobjects, )) upper_quartile_intensity = np.zeros((nobjects, )) cmi_x = np.zeros((nobjects, )) cmi_y = np.zeros((nobjects, )) max_x = np.zeros((nobjects, )) max_y = np.zeros((nobjects, )) for labels, lindexes in objects.get_labels(): lindexes = lindexes[lindexes != 0] labels, img = cpo.crop_labels_and_image(labels, img) _, masked_image = cpo.crop_labels_and_image( labels, masked_image) outlines = cpmo.outline(labels) if image.has_mask: _, mask = cpo.crop_labels_and_image( labels, image.mask) masked_labels = labels.copy() masked_labels[~mask] = 0 masked_outlines = outlines.copy() masked_outlines[~mask] = 0 else: masked_labels = labels masked_outlines = outlines lmask = masked_labels > 0 & np.isfinite( img) # Ignore NaNs, Infs has_objects = np.any(lmask) if has_objects: limg = img[lmask] llabels = labels[lmask] mesh_y, mesh_x = np.mgrid[0:masked_image.shape[0], 0:masked_image.shape[1]] mesh_x = mesh_x[lmask] mesh_y = mesh_y[lmask] lcount = fix( nd.sum(np.ones(len(limg)), llabels, lindexes)) integrated_intensity[lindexes - 1] = \ fix(nd.sum(limg, llabels, lindexes)) mean_intensity[lindexes - 1] = \ integrated_intensity[lindexes - 1] / lcount std_intensity[lindexes - 1] = np.sqrt( fix( nd.mean((limg - mean_intensity[llabels - 1])**2, llabels, lindexes))) min_intensity[lindexes - 1] = fix( nd.minimum(limg, llabels, lindexes)) max_intensity[lindexes - 1] = fix( nd.maximum(limg, llabels, lindexes)) # Compute the position of the intensity maximum max_position = np.array(fix( nd.maximum_position(limg, llabels, lindexes)), dtype=int) max_position = np.reshape( max_position, (max_position.shape[0], )) max_x[lindexes - 1] = mesh_x[max_position] max_y[lindexes - 1] = mesh_y[max_position] # The mass displacement is the distance between the center # of mass of the binary image and of the intensity image. The # center of mass is the average X or Y for the binary image # and the sum of X or Y * intensity / integrated intensity cm_x = fix(nd.mean(mesh_x, llabels, lindexes)) cm_y = fix(nd.mean(mesh_y, llabels, lindexes)) i_x = fix(nd.sum(mesh_x * limg, llabels, lindexes)) i_y = fix(nd.sum(mesh_y * limg, llabels, lindexes)) cmi_x[lindexes - 1] = i_x / integrated_intensity[lindexes - 1] cmi_y[lindexes - 1] = i_y / integrated_intensity[lindexes - 1] diff_x = cm_x - cmi_x[lindexes - 1] diff_y = cm_y - cmi_y[lindexes - 1] mass_displacement[lindexes - 1] = \ np.sqrt(diff_x * diff_x + diff_y * diff_y) # # Sort the intensities by label, then intensity. # For each label, find the index above and below # the 25%, 50% and 75% mark and take the weighted # average. # order = np.lexsort((limg, llabels)) areas = lcount.astype(int) indices = np.cumsum(areas) - areas for dest, fraction in ((lower_quartile_intensity, 1.0 / 4.0), (median_intensity, 1.0 / 2.0), (upper_quartile_intensity, 3.0 / 4.0)): qindex = indices.astype( float) + areas * fraction qfraction = qindex - np.floor(qindex) qindex = qindex.astype(int) qmask = qindex < indices + areas - 1 qi = qindex[qmask] qf = qfraction[qmask] dest[lindexes[qmask] - 1] = (limg[order[qi]] * (1 - qf) + limg[order[qi + 1]] * qf) # # In some situations (e.g. only 3 points), there may # not be an upper bound. # qmask = (~qmask) & (areas > 0) dest[lindexes[qmask] - 1] = limg[order[qindex[qmask]]] # # Once again, for the MAD # madimg = np.abs(limg - median_intensity[llabels - 1]) order = np.lexsort((madimg, llabels)) qindex = indices.astype(float) + areas / 2.0 qfraction = qindex - np.floor(qindex) qindex = qindex.astype(int) qmask = qindex < indices + areas - 1 qi = qindex[qmask] qf = qfraction[qmask] mad_intensity[lindexes[qmask] - 1] = (madimg[order[qi]] * (1 - qf) + madimg[order[qi + 1]] * qf) qmask = (~qmask) & (areas > 0) mad_intensity[lindexes[qmask] - 1] = madimg[order[qindex[qmask]]] emask = masked_outlines > 0 eimg = img[emask] elabels = labels[emask] has_edge = len(eimg) > 0 if has_edge: ecount = fix( nd.sum(np.ones(len(eimg)), elabels, lindexes)) integrated_intensity_edge[lindexes - 1] = \ fix(nd.sum(eimg, elabels, lindexes)) mean_intensity_edge[lindexes - 1] = \ integrated_intensity_edge[lindexes - 1] / ecount std_intensity_edge[lindexes - 1] = \ np.sqrt(fix(nd.mean( (eimg - mean_intensity_edge[elabels - 1]) ** 2, elabels, lindexes))) min_intensity_edge[lindexes - 1] = fix( nd.minimum(eimg, elabels, lindexes)) max_intensity_edge[lindexes - 1] = fix( nd.maximum(eimg, elabels, lindexes)) m = workspace.measurements for category, feature_name, measurement in \ ((INTENSITY, INTEGRATED_INTENSITY, integrated_intensity), (INTENSITY, MEAN_INTENSITY, mean_intensity), (INTENSITY, STD_INTENSITY, std_intensity), (INTENSITY, MIN_INTENSITY, min_intensity), (INTENSITY, MAX_INTENSITY, max_intensity), (INTENSITY, INTEGRATED_INTENSITY_EDGE, integrated_intensity_edge), (INTENSITY, MEAN_INTENSITY_EDGE, mean_intensity_edge), (INTENSITY, STD_INTENSITY_EDGE, std_intensity_edge), (INTENSITY, MIN_INTENSITY_EDGE, min_intensity_edge), (INTENSITY, MAX_INTENSITY_EDGE, max_intensity_edge), (INTENSITY, MASS_DISPLACEMENT, mass_displacement), (INTENSITY, LOWER_QUARTILE_INTENSITY, lower_quartile_intensity), (INTENSITY, MEDIAN_INTENSITY, median_intensity), (INTENSITY, MAD_INTENSITY, mad_intensity), (INTENSITY, UPPER_QUARTILE_INTENSITY, upper_quartile_intensity), (C_LOCATION, LOC_CMI_X, cmi_x), (C_LOCATION, LOC_CMI_Y, cmi_y), (C_LOCATION, LOC_MAX_X, max_x), (C_LOCATION, LOC_MAX_Y, max_y)): measurement_name = "%s_%s_%s_c%s" % ( category, feature_name, image_name.value, str(channel + 1)) m.add_measurement(object_name.value, measurement_name, measurement) if self.show_window and len(measurement) > 0: statistics.append( (image_name.value, 'c' + str(channel + 1), object_name.value, feature_name, np.round(np.mean(measurement), 3), np.round(np.median(measurement), 3), np.round(np.std(measurement), 3)))
def test_sum07(): labels = np.ones([0, 4], bool) for type in types: input = np.zeros([0, 4], type) output = ndimage.sum(input, labels=labels) assert_equal(output, 0.0)
def test_sum08(): labels = np.array([1, 0], bool) for type in types: input = np.array([1, 2], type) output = ndimage.sum(input, labels=labels) assert_equal(output, 1.0)
def canny(image, sigma=1., low_threshold=None, high_threshold=None, mask=None, use_quantiles=False): """Edge filter an image using the Canny algorithm. Parameters ----------- image : 2D array Grayscale input image to detect edges on; can be of any dtype. sigma : float, optional Standard deviation of the Gaussian filter. low_threshold : float, optional Lower bound for hysteresis thresholding (linking edges). If None, low_threshold is set to 10% of dtype's max. high_threshold : float, optional Upper bound for hysteresis thresholding (linking edges). If None, high_threshold is set to 20% of dtype's max. mask : array, dtype=bool, optional Mask to limit the application of Canny to a certain area. use_quantiles : bool, optional If True then treat low_threshold and high_threshold as quantiles of the edge magnitude image, rather than absolute edge magnitude values. If True then the thresholds must be in the range [0, 1]. Returns ------- output : 2D array (image) The binary edge map. See also -------- skimage.sobel Notes ----- The steps of the algorithm are as follows: * Smooth the image using a Gaussian with ``sigma`` width. * Apply the horizontal and vertical Sobel operators to get the gradients within the image. The edge strength is the norm of the gradient. * Thin potential edges to 1-pixel wide curves. First, find the normal to the edge at each point. This is done by looking at the signs and the relative magnitude of the X-Sobel and Y-Sobel to sort the points into 4 categories: horizontal, vertical, diagonal and antidiagonal. Then look in the normal and reverse directions to see if the values in either of those directions are greater than the point in question. Use interpolation to get a mix of points instead of picking the one that's the closest to the normal. * Perform a hysteresis thresholding: first label all points above the high threshold as edges. Then recursively label any point above the low threshold that is 8-connected to a labeled point as an edge. References ----------- .. [1] Canny, J., A Computational Approach To Edge Detection, IEEE Trans. Pattern Analysis and Machine Intelligence, 8:679-714, 1986 :DOI:`10.1109/TPAMI.1986.4767851` .. [2] William Green's Canny tutorial https://en.wikipedia.org/wiki/Canny_edge_detector Examples -------- >>> from skimage import feature >>> # Generate noisy image of a square >>> im = np.zeros((256, 256)) >>> im[64:-64, 64:-64] = 1 >>> im += 0.2 * np.random.rand(*im.shape) >>> # First trial with the Canny filter, with the default smoothing >>> edges1 = feature.canny(im) >>> # Increase the smoothing for better results >>> edges2 = feature.canny(im, sigma=3) """ # # The steps involved: # # * Smooth using the Gaussian with sigma above. # # * Apply the horizontal and vertical Sobel operators to get the gradients # within the image. The edge strength is the sum of the magnitudes # of the gradients in each direction. # # * Find the normal to the edge at each point using the arctangent of the # ratio of the Y sobel over the X sobel - pragmatically, we can # look at the signs of X and Y and the relative magnitude of X vs Y # to sort the points into 4 categories: horizontal, vertical, # diagonal and antidiagonal. # # * Look in the normal and reverse directions to see if the values # in either of those directions are greater than the point in question. # Use interpolation to get a mix of points instead of picking the one # that's the closest to the normal. # # * Label all points above the high threshold as edges. # * Recursively label any point above the low threshold that is 8-connected # to a labeled point as an edge. # # Regarding masks, any point touching a masked point will have a gradient # that is "infected" by the masked point, so it's enough to erode the # mask by one and then mask the output. We also mask out the border points # because who knows what lies beyond the edge of the image? # check_nD(image, 2) dtype_max = dtype_limits(image, clip_negative=False)[1] if low_threshold is None: low_threshold = 0.1 elif use_quantiles: if not (0.0 <= low_threshold <= 1.0): raise ValueError("Quantile thresholds must be between 0 and 1.") else: low_threshold = low_threshold / dtype_max if high_threshold is None: high_threshold = 0.2 elif use_quantiles: if not (0.0 <= high_threshold <= 1.0): raise ValueError("Quantile thresholds must be between 0 and 1.") else: high_threshold = high_threshold / dtype_max if mask is None: mask = np.ones(image.shape, dtype=bool) def fsmooth(x): return img_as_float(gaussian(x, sigma, mode='constant')) smoothed = smooth_with_function_and_mask(image, fsmooth, mask) jsobel = ndi.sobel(smoothed, axis=1) isobel = ndi.sobel(smoothed, axis=0) abs_isobel = np.abs(isobel) abs_jsobel = np.abs(jsobel) magnitude = np.hypot(isobel, jsobel) # # Make the eroded mask. Setting the border value to zero will wipe # out the image edges for us. # s = generate_binary_structure(2, 2) eroded_mask = binary_erosion(mask, s, border_value=0) eroded_mask = eroded_mask & (magnitude > 0) # #--------- Find local maxima -------------- # # Assign each point to have a normal of 0-45 degrees, 45-90 degrees, # 90-135 degrees and 135-180 degrees. # local_maxima = np.zeros(image.shape, bool) #----- 0 to 45 degrees ------ pts_plus = (isobel >= 0) & (jsobel >= 0) & (abs_isobel >= abs_jsobel) pts_minus = (isobel <= 0) & (jsobel <= 0) & (abs_isobel >= abs_jsobel) pts = pts_plus | pts_minus pts = eroded_mask & pts # Get the magnitudes shifted left to make a matrix of the points to the # right of pts. Similarly, shift left and down to get the points to the # top right of pts. c1 = magnitude[1:, :][pts[:-1, :]] c2 = magnitude[1:, 1:][pts[:-1, :-1]] m = magnitude[pts] w = abs_jsobel[pts] / abs_isobel[pts] c_plus = c2 * w + c1 * (1 - w) <= m c1 = magnitude[:-1, :][pts[1:, :]] c2 = magnitude[:-1, :-1][pts[1:, 1:]] c_minus = c2 * w + c1 * (1 - w) <= m local_maxima[pts] = c_plus & c_minus #----- 45 to 90 degrees ------ # Mix diagonal and vertical # pts_plus = (isobel >= 0) & (jsobel >= 0) & (abs_isobel <= abs_jsobel) pts_minus = (isobel <= 0) & (jsobel <= 0) & (abs_isobel <= abs_jsobel) pts = pts_plus | pts_minus pts = eroded_mask & pts c1 = magnitude[:, 1:][pts[:, :-1]] c2 = magnitude[1:, 1:][pts[:-1, :-1]] m = magnitude[pts] w = abs_isobel[pts] / abs_jsobel[pts] c_plus = c2 * w + c1 * (1 - w) <= m c1 = magnitude[:, :-1][pts[:, 1:]] c2 = magnitude[:-1, :-1][pts[1:, 1:]] c_minus = c2 * w + c1 * (1 - w) <= m local_maxima[pts] = c_plus & c_minus #----- 90 to 135 degrees ------ # Mix anti-diagonal and vertical # pts_plus = (isobel <= 0) & (jsobel >= 0) & (abs_isobel <= abs_jsobel) pts_minus = (isobel >= 0) & (jsobel <= 0) & (abs_isobel <= abs_jsobel) pts = pts_plus | pts_minus pts = eroded_mask & pts c1a = magnitude[:, 1:][pts[:, :-1]] c2a = magnitude[:-1, 1:][pts[1:, :-1]] m = magnitude[pts] w = abs_isobel[pts] / abs_jsobel[pts] c_plus = c2a * w + c1a * (1.0 - w) <= m c1 = magnitude[:, :-1][pts[:, 1:]] c2 = magnitude[1:, :-1][pts[:-1, 1:]] c_minus = c2 * w + c1 * (1.0 - w) <= m local_maxima[pts] = c_plus & c_minus #----- 135 to 180 degrees ------ # Mix anti-diagonal and anti-horizontal # pts_plus = (isobel <= 0) & (jsobel >= 0) & (abs_isobel >= abs_jsobel) pts_minus = (isobel >= 0) & (jsobel <= 0) & (abs_isobel >= abs_jsobel) pts = pts_plus | pts_minus pts = eroded_mask & pts c1 = magnitude[:-1, :][pts[1:, :]] c2 = magnitude[:-1, 1:][pts[1:, :-1]] m = magnitude[pts] w = abs_jsobel[pts] / abs_isobel[pts] c_plus = c2 * w + c1 * (1 - w) <= m c1 = magnitude[1:, :][pts[:-1, :]] c2 = magnitude[1:, :-1][pts[:-1, 1:]] c_minus = c2 * w + c1 * (1 - w) <= m local_maxima[pts] = c_plus & c_minus # #---- If use_quantiles is set then calculate the thresholds to use # if use_quantiles: high_threshold = np.percentile(magnitude, 100.0 * high_threshold) low_threshold = np.percentile(magnitude, 100.0 * low_threshold) # #---- Create two masks at the two thresholds. # high_mask = local_maxima & (magnitude >= high_threshold) low_mask = local_maxima & (magnitude >= low_threshold) # # Segment the low-mask, then only keep low-segments that have # some high_mask component in them # strel = np.ones((3, 3), bool) labels, count = label(low_mask, strel) if count == 0: return low_mask sums = (np.array(ndi.sum(high_mask, labels, np.arange(count, dtype=np.int32) + 1), copy=False, ndmin=1)) good_label = np.zeros((count + 1, ), bool) good_label[1:] = sums > 0 output_mask = good_label[labels] return output_mask
import matplotlib.pyplot as plt np.random.seed(1) n = 10 l = 256 im = np.zeros((l, l)) points = l * np.random.random((2, n**2)) im[(points[0]).astype(np.int), (points[1]).astype(np.int)] = 1 im = ndimage.gaussian_filter(im, sigma=l / (4. * n)) mask = im > im.mean() label_im, nb_labels = ndimage.label(mask) # Find the largest connected component sizes = ndimage.sum(mask, label_im, range(nb_labels + 1)) mask_size = sizes < 1000 remove_pixel = mask_size[label_im] label_im[remove_pixel] = 0 labels = np.unique(label_im) label_im = np.searchsorted(labels, label_im) # Now that we have only one connected component, extract it's bounding box slice_x, slice_y = ndimage.find_objects(label_im == 4)[0] roi = im[slice_x, slice_y] plt.figure(figsize=(4, 2)) plt.axes([0, 0, 1, 1]) plt.imshow(roi) plt.axis('off')