def segment_image(plate_image: ndarray, plate_mask: ndarray = None, plate_noise_mask: ndarray = None, area_min: float = 1) -> ndarray: """ Attempts to separate and label all colonies on a plate :param plate_image: an image containing colonies :param plate_mask: a boolean image mask to remove from the original image :param plate_noise_mask: a black and white image as a numpy array :param area_min: the minimum area for a colony, in pixels :returns: a segmented and labelled image as a numpy array """ from numpy import unique, isin from skimage.measure import regionprops, label from skimage.morphology import remove_small_objects, binary_erosion from skimage.segmentation import clear_border plate_image = imaging.remove_background_mask(plate_image, smoothing=0.5) if plate_mask is not None: # Remove mask from image plate_image = plate_image & plate_mask # Remove objects touching the mask border plate_image = clear_border(plate_image, bgval=0, mask=binary_erosion(plate_mask)) else: # Remove objects touching the image border plate_image = clear_border(plate_image, buffer_size=2, bgval=0) plate_image = label(plate_image, connectivity=2) # Remove background noise if len(unique(plate_image)) > 1: plate_image = remove_small_objects(plate_image, min_size=area_min) # Remove colonies that have grown on top of image artefacts or static objects if plate_noise_mask is not None: plate_noise_image = imaging.remove_background_mask(plate_noise_mask, smoothing=0.5) if len(unique(plate_noise_mask)) > 1: noise_mask = remove_small_objects(plate_noise_image, min_size=area_min) # Remove all objects where there is an existing static object exclusion = unique(plate_image[noise_mask]) exclusion_mask = isin(plate_image, exclusion[exclusion > 0]) plate_image[exclusion_mask] = 0 return plate_image
def segment_image(plate_image, plate_mask, plate_noise_mask, area_min = 5): """ Finds all colonies on a plate and returns an array of co-ordinates If a co-ordinate is occupied by a colony, it contains that colonies labelled number :param plate_image: a black and white image as a numpy array :param mask: a black and white image as a numpy array :param plate_noise_mask: a black and white image as a numpy array :returns: a segmented and labelled image as a numpy array """ from math import pi from scipy.ndimage.morphology import binary_fill_holes from skimage.morphology import remove_small_objects from skimage.measure import regionprops, label plate_image = imaging.remove_background_mask(plate_image, plate_mask) plate_noise_mask = imaging.remove_background_mask(plate_noise_mask, plate_mask) # Subtract an image of the first (i.e. empty) plate to remove static noise plate_image[plate_noise_mask] = 0 # Fill any small gaps plate_image = binary_fill_holes(plate_image) # Remove background noise plate_image = remove_small_objects(plate_image, min_size = area_min) colonies = label(plate_image) # Remove colonies that are on the edge of the plate # versions <0.16 do not allow for a mask # colonies = clear_border(pl_th, buffer_size = 1, mask = plate_mask) # Exclude objects that are too eccentric rps = regionprops(colonies) for rp in rps: # Eccentricity of zero is a perfect circle # Circularity of 1 is a perfect circle circularity = (4 * pi * rp.area) / (rp.perimeter * rp.perimeter) if rp.eccentricity > 0.6 or circularity < 0.85: colonies[colonies == rp.label] = 0 return colonies
def test_remove_background(self, image_segmented_local, image_mask): image_ref = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 0, 0], [0, 1, 1, 0, 1, 1, 1, 1, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0]]) result = remove_background_mask(image_segmented_local, image_mask) assert result.shape == image_segmented_local.shape assert (result == image_ref).all()
def test_mask_empty(self, image_segmented_local): with pytest.raises(ValueError): remove_background_mask(image_segmented_local, np.array([]))
def test_image_empty(self, image_mask): with pytest.raises(ValueError): remove_background_mask(np.array([]), image_mask)
def test_image_blank(self): image_blank = np.zeros((3, 3)) result = remove_background_mask(image_blank, image_blank > 0) assert (result == image_blank).all()
def test_size_mismatch(self): with pytest.raises(ValueError): remove_background_mask(np.ones((5, 5), dtype=np.uint8), np.ones((3, 5), dtype=np.uint8))