Exemplo n.º 1
0
 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 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
Exemplo n.º 3
0
    def run(self, workspace):
        objects = workspace.object_set.get_objects(self.objects_name.value)
        assert isinstance(objects, cpo.Objects)
        labels = objects.segmented
        half_window_size = self.window_size.value
        threshold = self.threshold.value
        output_labels = objects.segmented.copy()
        max_objects = np.max(output_labels)
        indices = np.arange(max_objects+1)

        # Get object slices
        object_slices = morph.fixup_scipy_ndimage_result(scind.measurements.find_objects(output_labels))

        # Calculate perimeters
        perimeters = morph.calculate_perimeters(output_labels, indices)

        # Find the neighbors
        neighbors = np.zeros((max_objects+1, max_objects+1), bool) # neighbors[i,j] = True when object j is a neighbor of object i.
        lmax = scind.grey_dilation(output_labels, footprint=np.ones((3,3), bool)) # lower pixel values will be replaced by adjacent larger pixel values
        lbig = output_labels.copy()
        lbig[lbig == 0] = np.iinfo(output_labels.dtype).max # set the background to be large so that it is ignored next
        lmin = scind.grey_erosion(lbig, footprint=np.ones((3,3), bool)) # larger pixel values will be replaced by adjacent smaller pixel values
        
        for i in range(1, max_objects+1):
            object_bounds = (object_slices[i-1][0],object_slices[i-1][1])
            object_map = output_labels[object_bounds] == i # The part of the slice that contains the object
            
            lower_neighbors = np.unique(lmin[object_bounds][object_map])
            higher_neighbors = np.unique(lmax[object_bounds][object_map])

            for neighbor_list in [lower_neighbors, higher_neighbors]:
                for j in range(0, len(neighbor_list)):
                    neighbors[i,neighbor_list[j]] = True
            neighbors[i,i] = False
        
        # Generate window dimensions for each location (necessary for the edges)
        dim1_window = np.ones(output_labels.shape, int) * half_window_size
        dim2_window = np.ones(output_labels.shape, int) * half_window_size
        for i in range(0, half_window_size):
            dim1_window[i:output_labels.shape[0]-i,0:output_labels.shape[1]] += 1
            dim2_window[0:output_labels.shape[0],i:output_labels.shape[1]-i] += 1
        
        # Loop over all objects
        for k in range(1, max_objects+1):
            if (perimeters[k] == 0) or not np.max(neighbors[k]): # Has been removed by a merge or has no neighbors
                continue

            k_bounds_array = object_slices[k-1]
            k_dim1_bounds = k_bounds_array[0].indices(output_labels.shape[0])
            k_dim2_bounds = k_bounds_array[1].indices(output_labels.shape[1])
            
            # Loop over all neighbors of object k
            l = 0
            while l < max_objects:
                l += 1
                
                if k == l or (perimeters[l] == 0) or not neighbors[k,l]: # Has been removed by a merge or is not a neighbor of object k
                    continue

                l_bounds_array = object_slices[l-1]
                l_dim1_bounds = l_bounds_array[0].indices(output_labels.shape[0])
                l_dim2_bounds = l_bounds_array[1].indices(output_labels.shape[1])

                kl_dim1_min_bound = min(k_dim1_bounds[0], l_dim1_bounds[0])
                kl_dim1_max_bound = max(k_dim1_bounds[1], l_dim1_bounds[1])
                kl_dim2_min_bound = min(k_dim2_bounds[0], l_dim2_bounds[0])
                kl_dim2_max_bound = max(k_dim2_bounds[1], l_dim2_bounds[1])

                kl_bounds = (slice(kl_dim1_min_bound, kl_dim1_max_bound), slice(kl_dim2_min_bound, kl_dim2_max_bound))
                kl_shape = (kl_dim1_max_bound - kl_dim1_min_bound, kl_dim2_max_bound - kl_dim2_min_bound)
                
                #
                # Find the vertices of object pair k, l
                #
                vertices = np.zeros(kl_shape, bool)
                isAdjacent = np.zeros(output_labels.shape, bool)
                isBorder = np.zeros(output_labels.shape, bool)
                
                for i in range(-1,2):
                    ki_min = 0
                    ki_max = 0
                    li_min = 0
                    li_max = 0
                    if k_dim1_bounds[0] + i < 0:
                        ki_min = -i
                    else:
                        ki_min = k_dim1_bounds[0]

                    if l_dim1_bounds[0] + i < 0:
                        li_min = -i
                    else:
                        li_min = l_dim1_bounds[0]

                    if k_dim1_bounds[1] + i > output_labels.shape[0]:
                        ki_max = output_labels.shape[0] - i
                    else:
                        ki_max = k_dim1_bounds[1]

                    if l_dim1_bounds[1] + i > output_labels.shape[0]:
                        li_max = output_labels.shape[0] - i
                    else:
                        li_max = l_dim1_bounds[1]

                    ki_slice = slice(ki_min, ki_max)
                    li_slice = slice(li_min, li_max)
                    ki_test_slice = slice(ki_min+i, ki_max+i)
                    li_test_slice = slice(li_min+i, li_max+i)
                    
                    for j in range(-1,2):
                        if i == j == 0:
                            continue

                        kj_min = 0
                        kj_max = 0
                        lj_min = 0
                        lj_max = 0
                        if k_dim2_bounds[0] + j < 0:
                            kj_min = -j
                        else:
                            kj_min = k_dim2_bounds[0]

                        if l_dim2_bounds[0] + j < 0:
                            lj_min = -j
                        else:
                            lj_min = l_dim2_bounds[0]

                        if k_dim2_bounds[1] + j > output_labels.shape[0]:
                            kj_max = output_labels.shape[0] - j
                        else:
                            kj_max = k_dim2_bounds[1]

                        if l_dim2_bounds[1] + j > output_labels.shape[0]:
                            lj_max = output_labels.shape[0] - j
                        else:
                            lj_max = l_dim2_bounds[1]

                        if (kj_min == kj_max or ki_min == ki_max) and (lj_min == lj_max or li_min == li_max):
                            continue

                        kj_slice = slice(kj_min, kj_max)
                        lj_slice = slice(lj_min, lj_max)
                        kj_test_slice = slice(kj_min+j, kj_max+j)
                        lj_test_slice = slice(lj_min+j, lj_max+j)

                        k_bounds = (ki_slice, kj_slice)
                        l_bounds = (li_slice, lj_slice)
                        k_test_bounds = (ki_test_slice, kj_test_slice)
                        l_test_bounds = (li_test_slice, lj_test_slice)
                        kl_mod_bounds = (slice(min(ki_min, li_min), max(ki_max, li_max)), slice(min(kj_min, lj_min), max(kj_max, lj_max)))

                        isAdjacentSlice = np.zeros(output_labels.shape, bool)
                        isAdjacentSlice[k_bounds] = np.logical_and(output_labels[k_bounds] == k,
                                                                   output_labels[k_test_bounds] == l)
                        isAdjacentSlice[l_bounds] = np.logical_or(isAdjacentSlice[l_bounds],
                                                                  np.logical_and(output_labels[l_bounds] == l,
                                                                                 output_labels[l_test_bounds] == k))
                        isAdjacent[kl_mod_bounds] = np.logical_or(isAdjacent[kl_mod_bounds],
                                                                  isAdjacentSlice[kl_mod_bounds])

                        isBorderSlice = np.zeros(output_labels.shape, bool)
                        isBorderSlice[k_bounds] = np.logical_and(output_labels[k_bounds] == k,
                                                                 np.logical_and(output_labels[k_test_bounds] != k,
                                                                                output_labels[k_test_bounds] != l))
                        isBorderSlice[l_bounds] = np.logical_or(isBorderSlice[l_bounds],
                                                                np.logical_and(output_labels[l_bounds] == l,
                                                                               np.logical_and(output_labels[l_test_bounds] != k,
                                                                                              output_labels[l_test_bounds] != l)))
                        isBorder[kl_mod_bounds] = np.logical_or(isBorder[kl_mod_bounds],
                                                                isBorderSlice[kl_mod_bounds])
                
                vertices = np.logical_and(isAdjacent[kl_bounds], isBorder[kl_bounds])

                #
                # Calculate the maximum vertex score for the pair
                #
                '''+1 for every non-I/J labeled pixel in the window
                +1 more for every non-I/J labeled pixel adjacent to it

                Divided by the score that would result if the objects were flat

                e.g. (vertex is starred)

                0  0  0  0  0  0  0
                0  0  0  0  0  0  0
                1  1  0  0  0  0  0
                1  1  1 *1* 2  2  0
                1  1  1  1  2  2  2
                1  1  1  1  2  2  2
                1  1  1  2  2  2  2

                If it were flat, it would be 7 * 3 = 21, so the divisor 7 * 6 = 42.
                In the case that the window is rectangular (such as at the image edge),
                we split the difference.
                Generalized:

                Vertex Score = (Sum of NotIJ) / [A * B - 0.5 * (A + B)]
                '''
                
                sum_not_IJ = np.zeros(kl_shape, int)
                for i in range(0, kl_shape[0]):
                    for j in range(0,kl_shape[1]):
                        if not vertices[i,j]:
                            continue

                        window_bounds = (slice(i - half_window_size + kl_dim1_min_bound, i + half_window_size + kl_dim1_min_bound),
                                         slice(j - half_window_size + kl_dim2_min_bound, j + half_window_size + kl_dim2_min_bound))

                        window_slice = output_labels[window_bounds]

                        sum_not_IJ[i,j] = count_nonzero(np.logical_and(window_slice != k,
                                                                     window_slice != l))
                        # Should be np.count_nonzero, but it doesn't exist in the version that comes with
                        # CellProfiler 2.0 r11710

                vertex_scores = sum_not_IJ / (dim1_window[kl_bounds] * dim2_window[kl_bounds] - 0.5 * (dim1_window[kl_bounds] + dim2_window[kl_bounds]))

                merged = np.zeros(kl_shape, int)
                merged[output_labels[kl_bounds] == k] = 1
                merged[output_labels[kl_bounds] == l] = 1

                merged_perimeter = morph.calculate_perimeters(merged, [1])

                seam_length = (perimeters[k] + perimeters[l] - merged_perimeter) / 2
                min_external_perimeter = min(perimeters[k], perimeters[l]) - seam_length
                adjusted_min_external_perimeter = min_external_perimeter / np.pi

                seam_fraction = seam_length / (adjusted_min_external_perimeter + seam_length)
                
                max_vertex_score = np.max(vertex_scores)

                score = (max_vertex_score + seam_fraction) / 2

                #
                # Join object pair with score above the threshold
                #
                if score >= threshold:
                    output_labels[output_labels == l] = k
                    
                    # Calculate new perimeters
                    perimeters[k] = merged_perimeter
                    perimeters[l] = 0

                    # Reconfigure neighbors
                    neighbors[k] = np.logical_or(neighbors[k], neighbors[l])
                    for x in range(1, max_objects+1): # Is there an array method to do this?
                        if neighbors[x,l]:
                            neighbors[x,k] = True
                    neighbors[0:max_objects,l] = False

                    # Recalculate bounds
                    k_dim1_bounds = kl_bounds[0].indices(output_labels.shape[0])
                    k_dim2_bounds = kl_bounds[1].indices(output_labels.shape[1])
                    
                    # Reset l to 0 so that we check all the neighbors against this new object
                    l = 0
                else:
                    pass
                        
        output_objects = cpo.Objects()
        output_objects.segmented = output_labels
        if objects.has_small_removed_segmented:
            output_objects.small_removed_segmented = \
                copy_labels(objects.small_removed_segmented, output_labels)
        if objects.has_unedited_segmented:
            output_objects.unedited_segmented = \
                copy_labels(objects.unedited_segmented, output_labels)
        output_objects.parent_image = objects.parent_image
        workspace.object_set.add_objects(output_objects, self.output_objects_name.value)
        
        measurements = workspace.measurements
        add_object_count_measurements(measurements,
                                      self.output_objects_name.value,
                                      np.max(output_objects.segmented))
        add_object_location_measurements(measurements,
                                         self.output_objects_name.value,
                                         output_objects.segmented)
        
        #
        # Relate the output objects to the input ones and record
        # the relationship.
        #
        children_per_parent, parents_of_children = \
            objects.relate_children(output_objects)
        measurements.add_measurement(self.objects_name.value,
                                     FF_CHILDREN_COUNT % 
                                     self.output_objects_name.value,
                                     children_per_parent)
        measurements.add_measurement(self.output_objects_name.value,
                                     FF_PARENT%self.objects_name.value,
                                     parents_of_children)
        if self.wants_outlines:
            outlines = cellprofiler.cpmath.outline.outline(output_labels)
            outline_image = cpi.Image(outlines.astype(bool))
            workspace.image_set.add(self.outlines_name.value,
                                    outline_image)
                    
        if workspace.frame is not None:
            workspace.display_data.orig_labels = objects.segmented
            workspace.display_data.output_labels = output_objects.segmented