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 get_well_mask(image): small_image = pyramid.pyr_down(image, 4) smoothed, gradient, sobel = canny.prepare_canny(small_image, 2) local_maxima = canny.canny_local_maxima(gradient, sobel) # well outline has ~6000 px full-size = 1500 px at 4x downsampled # So find the intensity value of the 2000th-brightest pixel, via percentile: highp = 100 * (1-2000/local_maxima.sum()) highp = max(highp, 90) low_edge, high_edge = numpy.percentile(gradient[local_maxima], [90, highp]) # Do canny edge-finding starting with gradient pixels as bright or brighter # than the 2000th-brightest pixel, and spread out to the 90th percentile # intensity: well_edge = canny.canny_hysteresis(local_maxima, gradient, low_edge, high_edge) # connect nearby edges and remove small unconnected bits well_edge = ndimage.binary_closing(well_edge, structure=S) well_edge = mask.remove_small_area_objects(well_edge, 300, structure=S) # Get map of distances and directions to nearest edge to use for contour-fitting distances, nearest_edge = active_contour.edge_direction(well_edge) # initial curve is the whole image less one pixel on the outside initial = numpy.ones(well_edge.shape, dtype=bool) initial[:,[0,-1]] = 0 initial[[0,-1],:] = 0 # Now evolve the curve inward until it contacts the canny well edges. gac = active_contour.GAC(initial, nearest_edge, advection_mask=(distances < 10), balloon_direction=-1) stopper = active_contour.StoppingCondition(gac, max_iterations=200) while stopper.should_continue(): # otherwise evolve the curve by shrinking, advecting toward edges, and smoothing gac.balloon_force(iters=3) gac.advect(iters=2) gac.smooth() gac.smooth(depth=3) # now erode everywhere the contour edge is right on a canny edge: gac.move_to_outside(well_edge[tuple(gac.inside_border_indices.T)]) gac.smooth() well_mask = gac.mask if well_mask.sum() / well_mask.size < 0.25: # if the well mask is too small, something went very wrong well_mask = None return small_image, well_mask