def refine_worm(image, initial_area, candidate_edges): # find strong worm edges (roughly equivalent to the edges found by find_initial_worm, # which are in candidate_edges): smooth the image, do canny edge-finding, and # then keep only those edges near candidate_edges smooth_image = restoration.denoise_tv_bregman(image, 140).astype(numpy.float32) smoothed, gradient, sobel = canny.prepare_canny(smooth_image, 8, initial_area) local_maxima = canny.canny_local_maxima(gradient, sobel) candidate_edge_region = ndimage.binary_dilation(candidate_edges, iterations=4) strong_edges = local_maxima & candidate_edge_region # Now threshold the image to find dark blobs as our initial worm region # First, find areas in the initial region unlikely to be worm pixels mean, std = mcd.robust_mean_std(smooth_image[initial_area][::4], 0.85) non_worm = (smooth_image > mean - std) & initial_area # now fit a smoothly varying polynomial to the non-worm pixels in the initial # region of interest, and subtract that from the actual image to generate # an image with a flat illumination field background = polyfit.fit_polynomial(smooth_image, mask=non_worm, degree=2) minus_bg = smooth_image - background # now recalculate a threshold from the background-subtracted pixels mean, std = mcd.robust_mean_std(minus_bg[initial_area][::4], 0.85) initial_worm = (minus_bg < mean - std) & initial_area # Add any pixels near the strong edges to our candidate worm position initial_worm |= ndimage.binary_dilation(strong_edges, iterations=3) initial_worm = mask.fill_small_radius_holes(initial_worm, 5) # Now grow/shrink the initial_worm region so that as many of the strong # edges from the canny filter are in contact with the region edges as possible. ac = active_contour.EdgeClaimingAdvection(initial_worm, strong_edges, max_region_mask=initial_area) stopper = active_contour.StoppingCondition(ac, max_iterations=100) while stopper.should_continue(): ac.advect(iters=1) ac.smooth(iters=1, depth=2) worm_mask = mask.fill_small_radius_holes(ac.mask, 7) # Now, get edges from the image at a finer scale smoothed, gradient, sobel = canny.prepare_canny(smooth_image, 0.3, initial_area) local_maxima = canny.canny_local_maxima(gradient, sobel) strong_sum = strong_edges.sum() highp = 100 * (1 - 1.5*strong_sum/local_maxima.sum()) lowp = max(100 * (1 - 3*strong_sum/local_maxima.sum()), 0) low_worm, high_worm = numpy.percentile(gradient[local_maxima], [lowp, highp]) fine_edges = canny.canny_hysteresis(local_maxima, gradient, low_worm, high_worm) # Expand out the identified worm area to include any of these finer edges closed_edges = ndimage.binary_closing(fine_edges, structure=S) worm = ndimage.binary_propagation(worm_mask, mask=worm_mask|closed_edges, structure=S) worm = ndimage.binary_closing(worm, structure=S, iterations=2) worm = mask.fill_small_radius_holes(worm, 5) worm = ndimage.binary_opening(worm) worm = mask.get_largest_object(worm) # Last, smooth the shape a bit to reduce sharp corners, but not too much to # sand off the tail ac = active_contour.CurvatureMorphology(worm, max_region_mask=initial_area) ac.smooth(depth=2, iters=2) return strong_edges, ac.mask
def find_initial_worm(small_image, well_mask): # plan here is to find known good worm edges with Canny using a stringent threshold, then # relax the threshold in the vicinity of the good edges. # back off another pixel from the well edge to avoid gradient from the edge shrunk_mask = ndimage.binary_erosion(well_mask, structure=S) smoothed, gradient, sobel = canny.prepare_canny(small_image, 2, shrunk_mask) local_maxima = canny.canny_local_maxima(gradient, sobel) # Calculate stringent and medium-stringent thresholds. The stringent threshold # is the 200th-brightest edge pixel, and the medium is the 450th-brightest pixel highp = 100 * (1-200/local_maxima.sum()) highp = max(highp, 94) mediump = 100 * (1-450/local_maxima.sum()) mediump = max(mediump, 94) low_worm, medium_worm, high_worm = numpy.percentile(gradient[local_maxima], [94, mediump, highp]) stringent_worm = canny.canny_hysteresis(local_maxima, gradient, low_worm, high_worm) # Expand out 20 pixels from the stringent worm edges to make our search space stringent_area = ndimage.binary_dilation(stringent_worm, mask=well_mask, iterations=20) # now use the relaxed threshold but only in the stringent area relaxed_worm = canny.canny_hysteresis(local_maxima, gradient, low_worm, medium_worm) & stringent_area # join very close-by objects, and remove remaining small objects candidate_worm = ndimage.binary_dilation(relaxed_worm, structure=S) candidate_worm = ndimage.binary_erosion(candidate_worm) candidate_worm = mask.remove_small_area_objects(candidate_worm, 30, structure=S) # Now figure out the biggest blob of nearby edges, and call that the worm region glommed_candidate = ndimage.binary_dilation(candidate_worm, structure=S, iterations=2) glommed_candidate = ndimage.binary_erosion(glommed_candidate, iterations=2) # get just outline, not any regions filled-in due to closing glommed_candidate ^= ndimage.binary_erosion(glommed_candidate) glommed_candidate = mask.get_largest_object(glommed_candidate, structure=S) worm_area = ndimage.binary_dilation(glommed_candidate, mask=well_mask, structure=S, iterations=12) worm_area = mask.fill_small_radius_holes(worm_area, max_radius=15) candidate_edges = relaxed_worm & candidate_worm & worm_area return candidate_edges, worm_area
def clean_mask(img): '''Clean spurious edges/unwanted things from the masks Parameters: ------------ img: array_like (cast to booleans) shape (n,m) Binary image of the worm mask Returns: ----------- mask: array_like (cast to booleans) shape (n,m) Binary image of the worm mask without spurious edges ''' #clean up holes in the mask img = mask.fill_small_radius_holes(img, 2) #dilate/erode to get rid of spurious edges for the mask curve_morph = active_contour.CurvatureMorphology(mask=img) #TODO: multiple iterations of erode curve_morph.erode() curve_morph.dilate() curve_morph.smooth(iters=2) return mask.get_largest_object(curve_morph.mask)
def find_worm_from_fluorescence(image, low_pct=99.2, high_pct=99.99, max_hole_radius=12): low_thresh, high_thresh = numpy.percentile(image, [low_pct, high_pct]) worm_mask = mask.hysteresis_threshold(image, low_thresh, high_thresh) worm_mask = mask.fill_small_radius_holes(worm_mask, max_hole_radius) worm_mask = mask.get_largest_object(worm_mask) return worm_mask