def _smooth_instance(image, radius): """Apply a median filter to smooth instance boundaries. Parameters ---------- image : np.ndarray, np.int64 or bool Labelled or masked image with shape (y, x). radius : int Radius of the kernel for the median filter. The higher the smoother. Returns ------- image_cleaned : np.ndarray, np.int64 or bool Cleaned image with shape (y, x). """ # smooth instance boundaries for a binary mask if image.dtype == bool: image_cleaned = image.astype(np.uint8) image_cleaned = stack.median_filter(image_cleaned, "disk", radius) image_cleaned = image_cleaned.astype(bool) # smooth instance boundaries for a labelled image else: if image.max() <= 65535 and image.min() >= 0: image_cleaned = image.astype(np.uint16) image_cleaned = stack.median_filter(image_cleaned, "disk", radius) image_cleaned = image_cleaned.astype(np.int64) else: raise ValueError("Segmentation boundaries can't be smoothed " "because more than 65535 has been detected in " "the image. Smoothing is performed with 16-bit " "unsigned integer images.") return image_cleaned
def test_median_filter(): # np.uint8 filtered_x = stack.median_filter(x, kernel_shape="square", kernel_size=3) expected_x = np.array([[2, 2, 0, 0, 0], [2, 1, 0, 0, 0], [1, 1, 1, 0, 0], [0, 0, 0, 0, 0], [0, 0, 1, 0, 0]], dtype=np.uint8) assert_array_equal(filtered_x, expected_x) assert filtered_x.dtype == np.uint8 # np.uint16 filtered_x = stack.median_filter(x.astype(np.uint16), kernel_shape="square", kernel_size=3) expected_x = expected_x.astype(np.uint16) assert_array_equal(filtered_x, expected_x) assert filtered_x.dtype == np.uint16
def cyt_watershed(relief, nuc_labelled, mask, smooth=None): """Apply watershed algorithm on the cytoplasm to segment cell instances. Parameters ---------- relief : np.ndarray, np.uint Relief image of the cytoplasm with shape (y, x). nuc_labelled : np.ndarray, np.int64 Result of the nuclei segmentation with shape (y, x). mask : np.ndarray, bool Binary mask of the cytoplasm with shape (y, x). smooth : int Smooth the final boundaries applying a median filter on the mask (kernel_size=smooth). Returns ------- cyt_segmented_final : np.ndarray, np.int64 Segmentation of the cytoplasm with instance differentiation and shape (y, x). """ # TODO how to be sure nucleus label corresponds to cell label? # check parameters stack.check_array(relief, ndim=2, dtype=[np.uint8, np.uint16]) stack.check_array(nuc_labelled, ndim=2, dtype=[np.uint8, np.uint16, np.int64]) stack.check_array(mask, ndim=2, dtype=[bool]) stack.check_parameter(smooth=(int, type(None))) # get markers markers = np.zeros_like(relief) for r in regionprops(nuc_labelled): markers[tuple(map(int, r.centroid))] = r.label markers = markers.astype(np.int64) # segment cytoplasm cyt_segmented = watershed(relief, markers, mask=mask) # smooth boundaries if smooth is not None: cyt_segmented = stack.median_filter(cyt_segmented.astype(np.uint16), kernel_shape="disk", kernel_size=smooth) cyt_segmented = remove_small_objects(cyt_segmented, 3000) cyt_segmented = cyt_segmented.astype(np.int64) # be sure to remove potential small disjoint part of the mask cyt_segmented_final = np.zeros_like(cyt_segmented) for id_cell in range(1, cyt_segmented.max() + 1): cell = cyt_segmented == id_cell cell_cc = label(cell) # one mask for the cell if cell_cc.max() == 1: mask = cell # multiple masks for the cell - we keep the larger one else: cell_properties = regionprops(cell_cc) m = 0 mask = np.zeros_like(cyt_segmented).astype(bool) for cell_properties_ in cell_properties: area = cell_properties_.area if area > m: m = area mask = cell_cc == cell_properties_.label cyt_segmented_final[mask] = id_cell return cyt_segmented_final