def regionProps(mask, image=None): """Calculates some basic properties of ROIs in a mask. Properties calculated: centroids, boxes, areas. If the original image is passed in, this also calcluate the mean value in each region. Keys of returned dictionary: 'centroid', 'boundingBox', 'area', and optionally, 'meanIntensity' :param mask: a 2d labeled image :param image: an optional 2p numpy array, original image, for calculation of mean intensity values :returns: a dictionary of lists, each containing property values """ if image is not None: if len(image.shape) >= 3: meanImage = np.mean(image, axis=2) else: meanImage = image min = meanImage.min() if min < 0: meanImage -= meanImage.min() means = pymorph.grain(meanImage, labels=mask, measurement='mean', option='data') else: means = None numLabels = mask.max() centroids = pymorph.blob(mask, measurement='centroid', output='data') boxes = pymorph.blob(mask, measurement='boundingbox', output='data') area = pymorph.blob(mask, measurement='area', output='data') coms = [] for i in range(1, numLabels + 1): coms.append(nd.measurements.center_of_mass(meanImage, labels=mask == i)) if means is not None: props = [{ 'meanIntensity': means[i], 'centroid': centroids[i], 'com': coms[i], 'boundingBox': boxes[i], 'area': area[i] } for i in range(numLabels)] else: props = [{ 'centroid': centroids[i], 'com': coms[i], 'boundingBox': boxes[i], 'area': area[i] } for i in range(numLabels)] return props
def post_processing(image): image_post = image.copy() # get texte pixel illu = 255 * (np.sum( (image - [0, 0, 255])**2, axis=2) < 10).astype(np.uint8) # fill holes illu_out = binary_fill_holes(illu) image_post[illu_out > 0, :] = [0, 0, 255] # get illustration pixel illu = 255 * (np.sum( (image - [255, 0, 0])**2, axis=2) < 10).astype(np.uint8) # get bounding-box of connected components bbox = pymorph.blob(measure.label(illu), 'boundingbox', 'data') illu_out = illu.copy() # transform connected components into englobing rectangles for l in bbox: x1 = l[0] y1 = l[1] x2 = l[2] y2 = l[3] if ((y2 - y1) < image.shape[0] / 2) | ((x2 - x1) < image.shape[1] / 2): illu_out[y1:y2, x1:x2] = 255 image_post[illu_out > 0, :] = [255, 0, 0] return image_post
def post_processing(image): image_post = image.copy() # get texte pixel illu = 255*(np.sum((image -[0,0,255])**2,axis=2)<10).astype(np.uint8) # fill holes illu_out = binary_fill_holes(illu) image_post[illu_out>0,:] = [0,0,255] # get illustration pixel illu = 255*(np.sum((image -[255,0,0])**2,axis=2)<10).astype(np.uint8) # get bounding-box of connected components bbox = pymorph.blob(measure.label(illu),'boundingbox','data') illu_out = illu.copy() # transform connected components into englobing rectangles for l in bbox: x1 = l[0] y1 = l[1] x2 = l[2] y2 = l[3] if ((y2-y1)<image.shape[0]/2) | ((x2-x1)<image.shape[1]/2): illu_out[y1:y2,x1:x2]=255 image_post[illu_out>0,:] = [255,0,0] return image_post
def corrMaskWithSourcePreConv(imageSeriesSmoothed, dilatedBinaryMask, sourceSmoothed): corrImage = np.zeros((imageSeries.shape[0], imageSeries.shape[1])) bounds = np.squeeze(pymorph.blob(dilatedMask, 'boundingbox', output='data')) for x in range(bounds[1], bounds[3]): for y in range(bounds[0], bounds[2]): if dilatedBinaryMask[x,y]>0: corr = stats.pearsonr(sourceSmoothed[1:-1], imageSeriesSmoothed[x,y,:])[0] corrImage[x,y] = corr return corrImage
def regionProps(mask, image=None): """Calculates some basic properties of ROIs in a mask. Properties calculated: centroids, boxes, areas. If the original image is passed in, this also calcluate the mean value in each region. Keys of returned dictionary: 'centroid', 'boundingBox', 'area', and optionally, 'meanIntensity' :param mask: a 2d labeled image :param image: an optional 2p numpy array, original image, for calculation of mean intensity values :returns: a dictionary of lists, each containing property values """ if image is not None: if len(image.shape) >= 3: meanImage = np.mean(image, axis=2) else: meanImage = image min = meanImage.min() if min < 0: meanImage -= meanImage.min() means = pymorph.grain(meanImage, labels=mask, measurement='mean', option='data') else: means = None numLabels = mask.max() centroids = pymorph.blob(mask, measurement='centroid', output='data') boxes = pymorph.blob(mask, measurement='boundingbox', output='data') area = pymorph.blob(mask, measurement='area', output='data') coms = [] for i in range(1, numLabels+1): coms.append(nd.measurements.center_of_mass(meanImage, labels=mask==i)) if means is not None: props = [{'meanIntensity':means[i], 'centroid':centroids[i], 'com':coms[i], 'boundingBox':boxes[i], 'area':area[i]} for i in range(numLabels)] else: props = [{'centroid':centroids[i], 'com':coms[i], 'boundingBox':boxes[i], 'area':area[i]} for i in range(numLabels)] return props
def test_blob(): F = np.array([ [0,1,1,0,0], [0,2,2,0,0], [0,3,3,3,0], [4,4,4,4,4], ]) M = pymorph.blob(F, 'area', 'data') MI = pymorph.blob(F, 'area', 'image') assert np.all( M == np.array([2,2,3,5])) for m,i in zip(MI.ravel(), F.ravel()): if i == 0: assert m == 0 else: assert m == M[i-1] B = pymorph.blob(F, 'boundingbox', 'data') BI = pymorph.blob(F, 'boundingbox') assert BI.max() == 1 C = pymorph.blob(F, 'centroid') assert np.all(C.sum(1) == 1)
def test_blob(): F = np.array([ [0, 1, 1, 0, 0], [0, 2, 2, 0, 0], [0, 3, 3, 3, 0], [4, 4, 4, 4, 4], ]) M = pymorph.blob(F, 'area', 'data') MI = pymorph.blob(F, 'area', 'image') assert np.all(M == np.array([2, 2, 3, 5])) for m, i in zip(MI.ravel(), F.ravel()): if i == 0: assert m == 0 else: assert m == M[i - 1] B = pymorph.blob(F, 'boundingbox', 'data') BI = pymorph.blob(F, 'boundingbox') assert BI.max() == 1 C = pymorph.blob(F, 'centroid') assert np.all(C.sum(1) == 1)
def tracker(frames, event, thresh_amp, thresh_dis, blob_ext, direction='forward', plots=False, verbose=False): """ Track the blob in a dynamic intervall forward or backward in time, as long as its amplitude is over a given threshold and the peak has detected less than a given threshold over consecutive frames. The maximum number of frames the blob is tracked for, is given by dim0 of frames Input: tau: Maximum number of frames to track blob event: ndarray, [I0, t0, R0, z0] Index of original feature to track direction: Traverse frames 'forward' or 'backward' in dimension 0 thresh_amp: Threshold for amplitude decay relative to frame0 thresh_dis: Threshold for blob movement relative to previous frame blob_ext: Extend of the blob used for determining its average shape Returns: numframes: Number of frames the blob was tracked xycom: COM position of the blob in each frame amp: Amplitude at COM in each frame fwhm_rad_idx: Indices that mark left and right FWHM of the blob fwhm_pol_idx: Indices that mark the lower and upper FWHM of the blob blob: Array that stores the blob extend """ if (verbose is True): print 'Called tracker with ' print '\tevent = ', event print '\tthresh_amp = ', thresh_amp print '\tthresh_dis = ', thresh_dis print '\tblob_ext = ', blob_ext print '\tplots = ', plots assert (direction in ['forward', 'backward']) assert (blob_ext % 2 == 0) # Maximum number of frames the blob is tracked for is given by # dimension 0 of frames # tau_max = np.shape(frames)[0] tau_max = frames.shape[0] I0, z0_last, R0_last = event[0], event[2], event[3] # I0 is the threshold amplitude we use for detecting blobs # i.e. for blob tracking, we identify later all connected regions that are larger than # I0 * thresh_amp if (direction is 'forward'): f_idx = 0 # Index used to access frames tau = 0 # Start with zero offset elif (direction is 'backward'): f_idx = -1 # Start at the second to last frame, 0 based indexing tau = 1 # Start with one frame offset if (verbose): print 'Tracking blob %s, t_idx %d x = %d, y = %d, I0 = %f' %\ (direction, tau, R0_last, z0_last, I0) print 'thresh_amp = %f, thresh_dis = %f' %\ (thresh_amp * I0, thresh_dis) xycom = np.zeros([tau_max, 2]) # Return values: COM position of peak xymax = np.zeros([tau_max, 2]) # Position of the blob peak fwhm_pol_idx = np.zeros([tau_max, 2], dtype='int') # Poloidal FWHM fwhm_rad_idx = np.zeros([tau_max, 2], dtype='int') # Radial FWHM amp = np.zeros([tau_max]) # Amplitude at COM position good_blob = True while (good_blob and tau < tau_max): if (verbose): print 'f_idx %d, blob from x = %d, y = %d, I0 = %f' %\ (f_idx, R0_last, z0_last, frames[f_idx, z0_last, R0_last]) event_frame = frames[f_idx, :, :] #plt.figure() #plt.contourf(event_frame, 64) #plt.title('direction: %s, fidx=%d' % (direction, f_idx)) #plt.colorbar() # Label all contiguous regions with ore than 60% of the original intensity labels = pm.label(event_frame > thresh_amp * I0) # Get the area of all contiguous regions blob_area = pm.blob(labels, 'area', output='data') # Get the controid of all contiguous regions blob_cent = pm.blob(labels, 'centroid', output='data') if (verbose): print 'Centroid analysis:' print ' -> blob_cent = ', blob_cent print ' -> shape = ', blob_cent.shape if (blob_cent.size < 1): # No peak here, quit tracking good_blob = False print 'Frame %d, %ss: lost track of blob' % (f_idx, direction) break # We now have a bunch of contiguous regions. # Loop over the regions that are at least 10% of the largest region # and find the one, whose centroid is closest to the last known position # of the blob loop_area = np.where(blob_area > 0.1 * blob_area.max())[0] min_idx = -1 # min_dist_frame = np.sqrt(event_frame.shape[0] * event_frame.shape[1]) # Maximal distance on a 64x64 grid for d_idx, i in enumerate(loop_area): dist = np.sqrt((blob_cent[i, 1] - R0_last) ** 2 + (blob_cent[i, 0] - z0_last) ** 2) if (verbose): print 'Region %d, distance to last peak: %f' % (d_idx, dist) if (dist < min_dist_frame and dist < thresh_dis): min_dist_frame = dist min_idx = i if (verbose): print 'Accepted' # If min_dist_frame is still sqrt(8192), no area was selected and the # blob could not be tracked successfully if (min_dist_frame is np.sqrt(event_frame.shape[0] * event_frame.shape[1])): print 'No peak satisfying criteria.' print '\tFound: dist = %f, Stopping %s tracking after %d frames' %\ (min_dist_frame, direction, tau) break if (min_idx is -1): print 'This should not happen' raise ValueError # Compute the x and y COM coordinates of the blob, store blob_mask = labels != (min_idx + 1) event_masked = np.ma.MaskedArray(event_frame, mask=blob_mask, fill_value=0) # When used to index frames[:,:,:]: # xymax[tau,:] = [index for axis 2, index for axis 1] # Maximum in the blob mask xymax[tau, :] = np.unravel_index(event_masked.argmax(), np.shape(labels)) # When used to index frames[:,:,:]: # xycom[tau,:] = [index for axis 1, index for axis 2] # To be consistent with indexing from xymax, flip this array # COM returns com along second dimension at index 0 xycom[tau, ::-1] = com(event_masked) ycom_off, xcom_off = xycom[tau, :].round().astype('int') if (verbose): print 'Peak at (%d,%d), COM at (%d,%d)' %\ (xymax[tau, 0], xymax[tau, 1], xycom[tau, 0], xycom[tau, 1]) amp[tau] = event_frame[z0_last, R0_last] # Follow the peak z0_last, R0_last = xymax[tau, :].astype('int') if (plots): plt.figure() plt.title('%s, frame %d' % (direction, f_idx)) plt.contour(event_frame, 16, colors='k', linewidth=0.5) plt.contourf(event_frame, 16, cmap=plt.cm.hot) plt.plot(xycom[tau, 1], xycom[tau, 0], 'wo') plt.plot(xymax[tau, 1], xymax[tau, 0], 'w^') plt.colorbar() plt.xlabel('x / px') plt.ylabel('y / px') if (direction is 'forward'): tau += 1 # Frame accepted, advance indices f_idx += 1 elif (direction is 'backward'): # We started at tau=1, subtract one to return correct number of frame # tracked in one direction, ignoring the starting frame # Ignore this for forward frame as we count the original frame here tau -= 1 f_idx -= 1 if (plots): plt.show() return tau, amp, xycom, xymax, fwhm_rad_idx, fwhm_pol_idx
count = 0 for file_mask in list_mask: file_image = glob.glob(file_mask[:-6] + '.*')[0] print "currently processing " + file_image dummy, filename = os.path.split(file_image) mask = cv2.imread(file_mask) mask = cv2.resize( mask, (mask.shape[1] / resizing_factor, mask.shape[0] / resizing_factor)) height, width, c = mask.shape ground_truth_ = np.zeros((height, width)) # get text bounding boxes ground_truth_text = ground_truth_ + np.where( np.linalg.norm(mask - [255, 0, 0], axis=2) < 10, 1, 0) label_text = label(ground_truth_text) bbox_text = blob(label_text, measurement='boundingbox', output="data") if bbox_text.size > 0: anno_text_x1 = bbox_text[:, 0].tolist() anno_text_y1 = bbox_text[:, 1].tolist() anno_text_x2 = bbox_text[:, 2].tolist() anno_text_y2 = bbox_text[:, 3].tolist() anno_text_label = list('t' * len(anno_text_x1)) else: anno_text_x1 = [] anno_text_y1 = [] anno_text_x2 = [] anno_text_y2 = [] anno_text_label = [] # get illustration bounding boxes ground_truth_illu = ground_truth_ + np.where( np.linalg.norm(mask - [0, 0, 255], axis=2) < 10, 1, 0)
def find_closest_region(frame, thresh_amp, x0, max_dist=2.0, verbose=False): """ Returns the contiguous area above a threshold in a frame, whose centroid coordinates are closest to x0 Input: frame : Input frame thresh_amp : Threshold for area separation x0 : Distance to centroid max_dist : Maximal distance to centroid Output: Binary image with contiguous area marked with 1 """ # Label all contiguous regions with ore than 60% of the original intensity labels = pm.label(frame > thresh_amp) # Get the area of all contiguous regions blob_area = pm.blob(labels, 'area', output='data') # Get the controid of all contiguous regions blob_cent = pm.blob(labels, 'centroid', output='data') if (verbose): print 'x0 = (%f, %f)' % (x0[0], x0[1]) print 'Labelling found %d regions: ' % labels.max() for i in np.arange(labels.max()): print 'Region: %d, centroid at %d, %d, area: %d' % (i, blob_cent[i, 1], blob_cent[i, 0], blob_area[i]) if (blob_cent.size < 1): raise TrackingError # We now have a bunch of contiguous regions. # Loop over the regions that are at least 10% of the largest region # and find the one, whose centroid is closest to the last known position # of the blob min_idx = -1 min_dist_frame = np.sqrt(frame.shape[0] * frame.shape[1]) # Maximal distance on a 64x64 grid for d_idx, i in enumerate(blob_area): # Compute distance of current areas centroid to the last centroids position dist = np.sqrt((blob_cent[d_idx, 1] - x0[1]) ** 2 + (blob_cent[d_idx, 0] - x0[0]) ** 2) if (verbose): print 'Region %d, center: x=%d, y=%d, A=%f, distance to last centroid: %f' %\ (d_idx, blob_cent[d_idx, 0], blob_cent[d_idx, 1], i, dist) # Skip areas who are less than 10% of the original if (i < 0.1 * blob_area.max()): if(verbose): print 'passing blob with area %f, d_idx = %d' % (i, d_idx) continue if (dist < min(max_dist, min_dist_frame)): min_dist_frame = dist min_idx = d_idx if (verbose): print 'Accepted' # If min_dist_frame is still sqrt(8192), no area was selected and the # blob could not be tracked successfully if (min_idx is -1): print 'No peak satisfying criteria.' raise TrackingError x_centroid = blob_cent[min_idx] # Compute the x and y COM coordinates of the blob, store blob_mask = labels != (min_idx + 1) event_masked = np.ma.MaskedArray(frame, mask=blob_mask, fill_value=0) # plt.figure() # plt.subplot(131) # plt.contourf(labels) # plt.colorbar() # # plt.subplot(132) # plt.contourf(frame, 64) # plt.colorbar() # # plt.subplot(133) # plt.contourf(event_masked) # plt.colorbar() # # plt.show() return (x_centroid, event_masked)