def test_02_02_one_worm(self): '''Find a single worm''' image = np.zeros((20, 20), bool) index, count, i, j = get_line_pts( np.array([1,6,19,14]), np.array([5,0,13,18]), np.array([6,19,14,1]), np.array([0,13,18,5])) image[i,j] = True image = binary_fill_holes(image) workspace, module = self.make_workspace(image) module.worm_length.value = 12 module.worm_width.value = 5 module.angle_count.value = 16 module.run(workspace) m = workspace.measurements self.assertTrue(isinstance(m, cpmeas.Measurements)) count = m.get_current_image_measurement( '_'.join((ID.I.C_COUNT, OBJECTS_NAME))) self.assertEqual(count, 1) x = m.get_current_measurement(OBJECTS_NAME, ID.I.M_LOCATION_CENTER_X) self.assertEqual(len(x), 1) self.assertAlmostEqual(x[0], 9., 1) y = m.get_current_measurement(OBJECTS_NAME, ID.I.M_LOCATION_CENTER_Y) self.assertEqual(len(y), 1) self.assertAlmostEqual(y[0], 10., 1) a = m.get_current_measurement(OBJECTS_NAME, ID.M_ANGLE) self.assertEqual(len(a), 1) self.assertAlmostEqual(a[0], 135, 0)
def test_02_02_one_worm(self): '''Find a single worm''' image = np.zeros((20, 20), bool) index, count, i, j = get_line_pts(np.array([1, 6, 19, 14]), np.array([5, 0, 13, 18]), np.array([6, 19, 14, 1]), np.array([0, 13, 18, 5])) image[i, j] = True image = binary_fill_holes(image) workspace, module = self.make_workspace(image) module.worm_length.value = 12 module.worm_width.value = 5 module.angle_count.value = 16 module.run(workspace) m = workspace.measurements self.assertTrue(isinstance(m, cpmeas.Measurements)) count = m.get_current_image_measurement('_'.join( (ID.I.C_COUNT, OBJECTS_NAME))) self.assertEqual(count, 1) x = m.get_current_measurement(OBJECTS_NAME, ID.I.M_LOCATION_CENTER_X) self.assertEqual(len(x), 1) self.assertAlmostEqual(x[0], 9., 1) y = m.get_current_measurement(OBJECTS_NAME, ID.I.M_LOCATION_CENTER_Y) self.assertEqual(len(y), 1) self.assertAlmostEqual(y[0], 10., 1) a = m.get_current_measurement(OBJECTS_NAME, ID.M_ANGLE) self.assertEqual(len(a), 1) self.assertAlmostEqual(a[0], 135, 0)
def test_02_03_crossing_worms(self): '''Find two worms that cross''' image = np.zeros((20, 20), bool) index, count, i, j = get_line_pts( np.array([1,4,19,16]), np.array([3,0,15,18]), np.array([4,19,16,1]), np.array([0,15,18,3])) image[i,j] = True index, count, i, j = get_line_pts( np.array([0,3,18,15]), np.array([16,19,4,1]), np.array([3,18,15,0]), np.array([19,4,1,16]) ) image[i,j] = True image = binary_fill_holes(image) workspace, module = self.make_workspace(image) module.worm_length.value = 17 module.worm_width.value = 5 module.angle_count.value = 16 module.run(workspace) m = workspace.measurements self.assertTrue(isinstance(m, cpmeas.Measurements)) count = m.get_current_image_measurement( '_'.join((ID.I.C_COUNT, OBJECTS_NAME))) self.assertEqual(count, 2) a = m.get_current_measurement(OBJECTS_NAME, ID.M_ANGLE) self.assertEqual(len(a), 2) if a[0] > 90: order = np.array([0,1]) else: order = np.array([1,0]) self.assertAlmostEqual(a[order[0]], 135, 0) self.assertAlmostEqual(a[order[1]], 45, 0) x = m.get_current_measurement(OBJECTS_NAME, ID.I.M_LOCATION_CENTER_X) self.assertEqual(len(x), 2) self.assertAlmostEqual(x[order[0]], 9., 0) self.assertAlmostEqual(x[order[1]], 10., 0) y = m.get_current_measurement(OBJECTS_NAME, ID.I.M_LOCATION_CENTER_Y) self.assertEqual(len(y), 2) self.assertAlmostEqual(y[order[0]], 10., 0) self.assertAlmostEqual(y[order[1]], 9., 0)
def get_diamond(self, angle): """Get a diamond-shaped structuring element angle - angle at which to tilt the diamond returns a binary array that can be used as a footprint for the erosion """ worm_width = self.worm_width.value worm_length = self.worm_length.value # # The shape: # # + x1,y1 # # x0,y0 + + x2, y2 # # + x3,y3 # x0 = int(np.sin(angle) * worm_length / 2) x1 = int(np.cos(angle) * worm_width / 2) x2 = -x0 x3 = -x1 y2 = int(np.cos(angle) * worm_length / 2) y1 = int(np.sin(angle) * worm_width / 2) y0 = -y2 y3 = -y1 xmax = np.max(np.abs([x0, x1, x2, x3])) ymax = np.max(np.abs([y0, y1, y2, y3])) strel = np.zeros((ymax * 2 + 1, xmax * 2 + 1), bool) index, count, i, j = get_line_pts( np.array([y0, y1, y2, y3]) + ymax, np.array([x0, x1, x2, x3]) + xmax, np.array([y1, y2, y3, y0]) + ymax, np.array([x1, x2, x3, x0]) + xmax, ) strel[i, j] = True strel = binary_fill_holes(strel) return strel
def get_diamond(self, angle): '''Get a diamond-shaped structuring element angle - angle at which to tilt the diamond returns a binary array that can be used as a footprint for the erosion ''' worm_width = self.worm_width.value worm_length = self.worm_length.value # # The shape: # # + x1,y1 # # x0,y0 + + x2, y2 # # + x3,y3 # x0 = int(np.sin(angle) * worm_length / 2) x1 = int(np.cos(angle) * worm_width / 2) x2 = -x0 x3 = -x1 y2 = int(np.cos(angle) * worm_length / 2) y1 = int(np.sin(angle) * worm_width / 2) y0 = -y2 y3 = -y1 xmax = np.max(np.abs([x0, x1, x2, x3])) ymax = np.max(np.abs([y0, y1, y2, y3])) strel = np.zeros((ymax * 2 + 1, xmax * 2 + 1), bool) index, count, i, j = get_line_pts( np.array([y0, y1, y2, y3]) + ymax, np.array([x0, x1, x2, x3]) + xmax, np.array([y1, y2, y3, y0]) + ymax, np.array([x1, x2, x3, x0]) + xmax) strel[i, j] = True strel = binary_fill_holes(strel) return strel
def filter_using_image(self, workspace, mask): '''Filter out connections using local intensity minima between objects workspace - the workspace for the image set mask - mask of background points within the minimum distance ''' # # NOTE: This is an efficient implementation and an improvement # in accuracy over the Matlab version. It would be faster and # more accurate to eliminate the line-connecting and instead # do the following: # * Distance transform to get the coordinates of the closest # point in an object for points in the background that are # at most 1/2 of the max distance between objects. # * Take the intensity at this closest point and similarly # label the background point if the background intensity # is at least the minimum intensity fraction # * Assume there is a connection between objects if, after this # labeling, there are adjacent points in each object. # # As it is, the algorithm duplicates the Matlab version but suffers # for cells whose intensity isn't high in the centroid and clearly # suffers when two cells touch at some point that's off of the line # between the two. # objects = workspace.object_set.get_objects(self.objects_name.value) labels = objects.segmented image = self.get_image(workspace) if self.show_window: # Save the image for display workspace.display_data.image = image # # Do a distance transform into the background to label points # in the background with their closest foreground object # i, j = scind.distance_transform_edt(labels==0, return_indices=True, return_distances=False) confluent_labels = labels[i,j] confluent_labels[~mask] = 0 if self.where_algorithm == CA_CLOSEST_POINT: # # For the closest point method, find the intensity at # the closest point in the object (which will be the point itself # for points in the object). # object_intensity = image[i,j] * self.minimum_intensity_fraction.value confluent_labels[object_intensity > image] = 0 count, index, c_j = morph.find_neighbors(confluent_labels) if len(c_j) == 0: # Nobody touches - return the labels matrix return labels # # Make a row of i matching the touching j # c_i = np.zeros(len(c_j)) # # Eliminate labels without matches # label_numbers = np.arange(1,len(count)+1)[count > 0] index = index[count > 0] count = count[count > 0] # # Get the differences between labels so we can use a cumsum trick # to increment to the next label when they change # label_numbers[1:] = label_numbers[1:] - label_numbers[:-1] c_i[index] = label_numbers c_i = np.cumsum(c_i).astype(int) if self.where_algorithm == CA_CENTROIDS: # # Only connect points > minimum intensity fraction # center_i, center_j = morph.centers_of_labels(labels) indexes, counts, i, j = morph.get_line_pts( center_i[c_i-1], center_j[c_i-1], center_i[c_j-1], center_j[c_j-1]) # # The indexes of the centroids at pt1 # last_indexes = indexes+counts-1 # # The minimum of the intensities at pt0 and pt1 # centroid_intensities = np.minimum( image[i[indexes],j[indexes]], image[i[last_indexes], j[last_indexes]]) # # Assign label numbers to each point so we can use # scipy.ndimage.minimum. The label numbers are indexes into # "connections" above. # pt_labels = np.zeros(len(i), int) pt_labels[indexes[1:]] = 1 pt_labels = np.cumsum(pt_labels) minima = scind.minimum(image[i,j], pt_labels, np.arange(len(indexes))) minima = morph.fixup_scipy_ndimage_result(minima) # # Filter the connections using the image # mif = self.minimum_intensity_fraction.value i = c_i[centroid_intensities * mif <= minima] j = c_j[centroid_intensities * mif <= minima] else: i = c_i j = c_j # # Add in connections from self to self # unique_labels = np.unique(labels) i = np.hstack((i, unique_labels)) j = np.hstack((j, unique_labels)) # # Run "all_connected_components" to get a component # for # objects identified as same. # new_indexes = morph.all_connected_components(i, j) new_labels = np.zeros(labels.shape, int) new_labels[labels != 0] = new_indexes[labels[labels != 0]] return new_labels
def rebuild_worm_from_control_points_approx(self, control_coords, worm_radii, labels, idx): '''Rebuild a worm from its control coordinates Given a worm specified by some control points along its spline, reconstructs an approximate binary image representing the worm. Specifically, this function generates an image where successive control points have been joined by line segments, and then dilates that by a certain (specified) radius. Inputs: control_coords: A N x 2 double array, where each column contains the x and y coordinates for a control point. worm_radius: Scalar double. Approximate radius of a typical worm; the radius by which the reconstructed worm spline is dilated to form the final worm. Outputs: The coordinates of all pixels in the worm in an N x 2 array''' index, count, i, j = morph.get_line_pts(control_coords[:-1,0], control_coords[:-1,1], control_coords[1:,0], control_coords[1:,1]) # # Get rid of the last point for the middle elements - these are # duplicated by the first point in the next line # i = np.delete(i,index[1:]) j = np.delete(j,index[1:]) index = index - np.arange(len(index)) count -= 1 # # Find the control point and within-control-point index of each point # label = np.zeros(len(i), int) label[index[1:]] = 1 label = np.cumsum(label) order = np.arange(len(i)) - index[label] frac = order.astype(float) / count[label].astype(float) radius = (worm_radii[label] * (1-frac) + worm_radii[label+1] * frac) iworm_radius = int(np.max(np.ceil(radius))) # # Get dilation coordinates # ii, jj = np.mgrid[-iworm_radius:iworm_radius+1, -iworm_radius:iworm_radius+1] dd = np.sqrt((ii*ii + jj*jj).astype(float)) mask = ii*ii + jj*jj <= iworm_radius * iworm_radius ii = ii[mask] jj = jj[mask] dd = dd[mask] # # All points (with repeats) # i = (i[:,np.newaxis] + ii[np.newaxis, :]).flatten() j = (j[:,np.newaxis] + jj[np.newaxis, :]).flatten() # # We further mask out any dilation coordinates outside of # the radius at our point in question # m = (radius[:,np.newaxis] >= dd[np.newaxis, :]).flatten() i = i[m] j = j[m] # # Find repeats by sorting and comparing against next # order = np.lexsort((i,j)) i = i[order] j = j[order] mask = np.hstack([[True], (i[:-1] != i[1:]) | (j[:-1] != j[1:])]) i = i[mask] j = j[mask] mask = (i >= 0) & (j >= 0) & (i < labels.shape[0]) & (j < labels.shape[1]) labels[i[mask], j[mask]] = idx
def filter_using_image(self, workspace, mask): '''Filter out connections using local intensity minima between objects workspace - the workspace for the image set mask - mask of background points within the minimum distance ''' # # NOTE: This is an efficient implementation and an improvement # in accuracy over the Matlab version. It would be faster and # more accurate to eliminate the line-connecting and instead # do the following: # * Distance transform to get the coordinates of the closest # point in an object for points in the background that are # at most 1/2 of the max distance between objects. # * Take the intensity at this closest point and similarly # label the background point if the background intensity # is at least the minimum intensity fraction # * Assume there is a connection between objects if, after this # labeling, there are adjacent points in each object. # # As it is, the algorithm duplicates the Matlab version but suffers # for cells whose intensity isn't high in the centroid and clearly # suffers when two cells touch at some point that's off of the line # between the two. # objects = workspace.object_set.get_objects(self.objects_name.value) labels = objects.segmented image = self.get_image(workspace) if self.show_window: # Save the image for display workspace.display_data.image = image # # Do a distance transform into the background to label points # in the background with their closest foreground object # i, j = scind.distance_transform_edt(labels == 0, return_indices=True, return_distances=False) confluent_labels = labels[i, j] confluent_labels[~mask] = 0 if self.where_algorithm == CA_CLOSEST_POINT: # # For the closest point method, find the intensity at # the closest point in the object (which will be the point itself # for points in the object). # object_intensity = image[i, j] * self.minimum_intensity_fraction.value confluent_labels[object_intensity > image] = 0 count, index, c_j = morph.find_neighbors(confluent_labels) if len(c_j) == 0: # Nobody touches - return the labels matrix return labels # # Make a row of i matching the touching j # c_i = np.zeros(len(c_j)) # # Eliminate labels without matches # label_numbers = np.arange(1, len(count) + 1)[count > 0] index = index[count > 0] count = count[count > 0] # # Get the differences between labels so we can use a cumsum trick # to increment to the next label when they change # label_numbers[1:] = label_numbers[1:] - label_numbers[:-1] c_i[index] = label_numbers c_i = np.cumsum(c_i).astype(int) if self.where_algorithm == CA_CENTROIDS: # # Only connect points > minimum intensity fraction # center_i, center_j = morph.centers_of_labels(labels) indexes, counts, i, j = morph.get_line_pts(center_i[c_i - 1], center_j[c_i - 1], center_i[c_j - 1], center_j[c_j - 1]) # # The indexes of the centroids at pt1 # last_indexes = indexes + counts - 1 # # The minimum of the intensities at pt0 and pt1 # centroid_intensities = np.minimum( image[i[indexes], j[indexes]], image[i[last_indexes], j[last_indexes]]) # # Assign label numbers to each point so we can use # scipy.ndimage.minimum. The label numbers are indexes into # "connections" above. # pt_labels = np.zeros(len(i), int) pt_labels[indexes[1:]] = 1 pt_labels = np.cumsum(pt_labels) minima = scind.minimum(image[i, j], pt_labels, np.arange(len(indexes))) minima = morph.fixup_scipy_ndimage_result(minima) # # Filter the connections using the image # mif = self.minimum_intensity_fraction.value i = c_i[centroid_intensities * mif <= minima] j = c_j[centroid_intensities * mif <= minima] else: i = c_i j = c_j # # Add in connections from self to self # unique_labels = np.unique(labels) i = np.hstack((i, unique_labels)) j = np.hstack((j, unique_labels)) # # Run "all_connected_components" to get a component # for # objects identified as same. # new_indexes = morph.all_connected_components(i, j) new_labels = np.zeros(labels.shape, int) new_labels[labels != 0] = new_indexes[labels[labels != 0]] return new_labels
def rebuild_worm_from_control_points_approx(self, control_coords, worm_radii, labels, idx): '''Rebuild a worm from its control coordinates Given a worm specified by some control points along its spline, reconstructs an approximate binary image representing the worm. Specifically, this function generates an image where successive control points have been joined by line segments, and then dilates that by a certain (specified) radius. Inputs: control_coords: A N x 2 double array, where each column contains the x and y coordinates for a control point. worm_radius: Scalar double. Approximate radius of a typical worm; the radius by which the reconstructed worm spline is dilated to form the final worm. Outputs: The coordinates of all pixels in the worm in an N x 2 array''' index, count, i, j = morph.get_line_pts(control_coords[:-1, 0], control_coords[:-1, 1], control_coords[1:, 0], control_coords[1:, 1]) # # Get rid of the last point for the middle elements - these are # duplicated by the first point in the next line # i = np.delete(i, index[1:]) j = np.delete(j, index[1:]) index = index - np.arange(len(index)) count -= 1 # # Find the control point and within-control-point index of each point # label = np.zeros(len(i), int) label[index[1:]] = 1 label = np.cumsum(label) order = np.arange(len(i)) - index[label] frac = order.astype(float) / count[label].astype(float) radius = (worm_radii[label] * (1 - frac) + worm_radii[label + 1] * frac) iworm_radius = int(np.max(np.ceil(radius))) # # Get dilation coordinates # ii, jj = np.mgrid[-iworm_radius:iworm_radius + 1, -iworm_radius:iworm_radius + 1] dd = np.sqrt((ii * ii + jj * jj).astype(float)) mask = ii * ii + jj * jj <= iworm_radius * iworm_radius ii = ii[mask] jj = jj[mask] dd = dd[mask] # # All points (with repeats) # i = (i[:, np.newaxis] + ii[np.newaxis, :]).flatten() j = (j[:, np.newaxis] + jj[np.newaxis, :]).flatten() # # We further mask out any dilation coordinates outside of # the radius at our point in question # m = (radius[:, np.newaxis] >= dd[np.newaxis, :]).flatten() i = i[m] j = j[m] # # Find repeats by sorting and comparing against next # order = np.lexsort((i, j)) i = i[order] j = j[order] mask = np.hstack([[True], (i[:-1] != i[1:]) | (j[:-1] != j[1:])]) i = i[mask] j = j[mask] mask = (i >= 0) & (j >= 0) & (i < labels.shape[0]) & (j < labels.shape[1]) labels[i[mask], j[mask]] = idx