Labeled images are integer images where the values correspond to different regions. I.e., region 1 is all of the pixels which have value 1, region two is the pixels with value 2, and so on. By convention, region 0 is the background and often handled differently.
New in version 0.6.5.
The first step is obtaining a labeled function from a binary function:
import mahotas as mh
import numpy as np
from pylab import imshow, show
regions = np.zeros((8,8), bool)
regions[:3,:3] = 1
regions[6:,6:] = 1
labeled, nr_objects = mh.label(regions)
imshow(labeled, interpolation='nearest')
show()
This results in an image with 3 values:
There is an extra argument to label: the structuring element, which defaults to a 3x3 cross (or, 4-neighbourhood). This defines what it means for two pixels to be in the same region. You can use 8-neighbourhoods by replacing it with a square:
labeled,nr_objects = mh.label(regions, np.ones((3,3), bool))
New in version 0.7: labeled_size and labeled_sum were added in version 0.7
We can now collect a few statistics on the labeled regions. For example, how big are they?
sizes = mh.labeled.labeled_size(labeled)
print 'Background size', sizes[0]
print 'Size of first region', sizes[1]
This size is measured simply as the number of pixels in each region. We can instead measure the total weight in each area:
array = np.random.random_sample(regions.shape)
sums = mh.labeled_sum(array, labeled)
print 'Sum of first region', sums[1]
New in version 0.9.6: remove_regions & relabel were only added in version 0.9.6
Here is a slightly more complex example. This is in demos directory as nuclear.py. We are going to use this image, a fluorescent microscopy image from a nuclear segmentation benchmark
First we perform a bit of Gaussian filtering and thresholding:
f = mh.gaussian_filter(f, 4)
f = (f> f.mean())
(Without the Gaussian filter, the resulting thresholded image has very noisy edges. You can get the image in the demos/ directory and try it out.)
Labeling gets us all of the nuclei:
labeled, n_nucleus = mh.label(f)
print('Found {} nuclei.'.format(n_nucleus))
42 nuclei were found. None were missed, but, unfortunately, we also get some aggregates. In this case, we are going to assume that we wanted to perform some measurements on the real nuclei, but are willing to filter out anything that is not a complete nucleus or that is a lump on nuclei. So we measure sizes and filter:
sizes = mh.labeled.labeled_size(labeled)
too_big = np.where(sizes > 10000)
labeled = mh.labeled.remove_regions(labeled, too_big)
We can also remove the region touching the border:
labeled = mh.labeled.remove_bordering(labeled)
This array, labeled now has values in the range 0 to n_nucleus, but with some values missing (e.g., if region 7 was one of the ones touching the border, then 7 is not used in the labeling). We can relabel to get a cleaner version:
relabeled, n_left = mh.labeled.relabel(labeled)
print('After filtering and relabeling, there are {} nuclei left.'.format(n_left))
Now, we have 24 nuclei and relabeled goes from 0 (background) to 24.
A border pixel is one where there is more than one region in its neighbourhood (one of those regions can be the background).
You can retrieve border pixels with either the borders() function, which gets all the borders or the border() (note the singular) which gets only the border between a single pair of regions. As usual, what neighbour means is defined by a structuring element, defaulting to a 3x3 cross.
The mahotas.labeled submodule contains the functions mentioned above. label() is also available as mahotas.label.
Compute border pixels
A pixel is on a border if it has value i and a pixel in its neighbourhood (defined by Bc) has value j, with i != j.
Parameters : | labeled : ndarray of integer type
Bc : structure element, optional out : ndarray of same shape as labeled, dtype=bool, optional
mode : {‘reflect’, ‘nearest’, ‘wrap’, ‘mirror’, ‘constant’ [default], ‘ignore’}
|
---|---|
Returns : | border_img : boolean ndarray
|
Compute the border region between i and j regions.
A pixel is on the border if it has value i (or j) and a pixel in its neighbourhood (defined by Bc) has value j (or i).
Parameters : | labeled : ndarray of integer type
i : integer j : integer Bc : structure element, optional out : ndarray of same shape as labeled, dtype=bool, optional
always_return : bool, optional
|
---|---|
Returns : | border_img : boolean ndarray
|
Find the perimeter of objects in binary images.
A pixel is part of an object perimeter if its value is one and there is at least one zero-valued pixel in its neighborhood.
By default the neighborhood of a pixel is 4 nearest pixels, but if n is set to 8 the 8 nearest pixels will be considered.
Parameters : | bw : ndarray
n : int, optional
mode : {‘reflect’, ‘nearest’, ‘wrap’, ‘mirror’, ‘constant’ [default], ‘ignore’}
|
---|---|
Returns : | perim : ndarray
|
See also
Label the array, which is interpreted as a binary array
This is also called connected component labeled, where the connectivity is defined by the structuring element Bc.
See: http://en.wikipedia.org/wiki/Connected-component_labeling
Parameters : | array : ndarray
Bc : ndarray, optional
out : ndarray, optional
|
---|---|
Returns : | labeled : ndarray
nr_objects : int
|
Labeled sum. sum will be an array of size labeled.max() + 1, where sum[i] is equal to np.sum(array[labeled == i]).
Parameters : | array : ndarray of any type labeled : int ndarray
|
---|---|
Returns : | sums : 1-d ndarray of array.dtype |
Labeled minimum. mins will be an array of size labeled.max() + 1, where mins[i] is equal to np.min(array[labeled == i]).
Parameters : | array : ndarray of any type labeled : int ndarray
|
---|---|
Returns : | mins : 1-d ndarray of array.dtype |
Equivalent to:
for i in range(...):
sizes[i] = np.sum(labeled == i)
but, naturally, much faster.
Parameters : | labeled : int ndarray |
---|---|
Returns : | sizes : 1-d ndarray of int |
Relabeling ensures that relabeled is a labeled image such that every label from 1 to relabeled.max() is used (0 is reserved for the background and is passed through).
Example:
labeled,n = label(some_binary_map)
for region in xrange(n):
if not good_region(labeled, region + 1):
# This deletes the region:
labeled[labeled == (region + 1)] = 0
relabel(labeled, inplace=True)
Parameters : | relabeled : ndarray of int
inplace : boolean, optional
|
---|---|
Returns : | relabeled: ndarray : nr_objs : int
|
See also
Checks whether labeled0 and labeled1 represent the same labeling (i.e., whether they are the same except for a possible change of label values).
Note that the background (value 0) is treated differently. Namely
is_same_labeling(a, b) implies np.all( (a == 0) == (b == 0) )
Parameters : | labeled0 : ndarray of int
labeled1 : ndarray of int
|
---|---|
Returns : | same : bool
|
See also
Remove objects that are touching the border.
Pass im as out to achieve in-place operation.
Parameters : | labeled : ndarray
rsize : int, optional
out : ndarray, optional
|
---|---|
Returns : | slabeled : ndarray
|
removed = remove_regions(labeled, regions, inplace=False):
Removes the regions in regions. If an elementwise in operator existed, this would be equivalent to the following:
labeled[ labeled element-wise-in regions ] = 0
This function does not relabel its arguments. You can use the relabel function for that:
removed = relabel(remove_regions(labeled, regions))
Or, saving one image allocation:
removed = relabel(remove_regions(labeled, regions), inplace=True)
This is the same, but reuses the memory in the relabeling operation.
Parameters : | relabeled : ndarray of int
regions : sequence of int
inplace : boolean, optional
|
---|---|
Returns : | removed : ndarray |
See also