Counting cells manually is a tedious error prone process for humans. Given a large data set of microscopy images this task can be achieved much faster by means of basic computer vision techniques. In this tutorial we will segment cells from an image following a method similar to the one presented by Yongming Chen in 1999. The method uses basic morphological operations and the watershed algorithm to segment the cells. Nowadays better methods for cell segmentation exist. This method was chosen for its simplicity and ease of implementation.
We start with an image of cell-like structures by Anna-Katerina Hadjantonakis and Virginia E Papaioannou.
A = imread('cells.jpg');
We convert the image to grayscale:
I = rgb2gray(A);
To be able to extract the dimmer cells, it is necessary to perform some local contrast adjustments
Objects on the borders can be caused by noise and other artifacts. We can eliminate objects on the borders of the image like this:
I = imclearborder(I);
Additionally, we can remove noise by adaptive filtering, using a small window (in this case 5x5 pixels). The changes are barely noticeable to the human eye, but they greatly reduce the number of incorrect cells found:
I = wiener2(I, [5 5]);
Next we will perform some steps to extract the perimeters of cell or cell groups following a binarization technique. We will use some basic morphological operations to further remove objects that are too small to be cells. We find a global threshold using Otsu's method, and we use it to convert the greyscale image to binary:
bw = im2bw(I, graythresh(I));
We then fill image regions and holes. This is necessary when the cell have varying contrast within themselves. In our example image we could skip this step without loss of accuracy.
bw3 = imopen(bw2, strel('disk',2));
Next we remove from the binary image all connected components (cells) that have fewer than 10 pixels.
bw4 = bwareaopen(bw3, 100);
We can see that many cells are grouped. We can visualize the groups by finding their perimeter and overlaying it over the grayscale image. To overlay the perimeters the imoverlay function written by Steven L. Eddins was used.
bw4_perim = bwperim(bw4);
overlay1 = imoverlay(I, bw4_perim, [1 .3 .3]);
Finally, we can apply the watershed algorithm:
L = watershed(I_mod);
labeledImage = label2rgb(L);
We can then easily count the number of discovered cells. We have detected 185 cells in this image.
[L, num] = bwlabel(L);
Let's overlay the detected cells over the original grayscale image to visually evaluate the performance of the algorithm:
mask = im2bw(L, 1);
overlay3 = imoverlay(I, mask, [1 .3 .3]);
While many cells have been correctly detected, there are several that our algorithm does not detect. Additionally we have detected several cells in the center of the image, but even a human eye could hardly count their number as they appear as a dust cloud. Some cells are fragmented into two distinct cells. Finally, the detected cells appear smaller than the real ones, which means that we were not sufficiently careful when applying the morphological operations.
The algorithm completes in 0.3s on a 3.4GHz i7-3770 CPU for an input image of dimensions 540x512 pixels.
Below I present additional results, using different images featuring cells under different imaging conditions and bubbles. The point of these results is to show that this algorithm was optimized to segment and count cells in a single type of images, and will perform poorly in the rest. Several parameters had to be changed to improve the performance in the different scenarios. Images with dark cells and bright background had to be color inverted before processing.
In this tutorial we have presented a relatively simple method to segment and count cells in an image. Some tweaking could improve the results of the segmentation, and improve the detection accuracy. Also, we could add a merging method to merge cells that are over-fragmented.
I = wiener2(I, [5 5]);
Next we will perform some steps to extract the perimeters of cell or cell groups following a binarization technique. We will use some basic morphological operations to further remove objects that are too small to be cells. We find a global threshold using Otsu's method, and we use it to convert the greyscale image to binary:
bw = im2bw(I, graythresh(I));
We then fill image regions and holes. This is necessary when the cell have varying contrast within themselves. In our example image we could skip this step without loss of accuracy.
bw2 = imfill(bw,'holes');
We then perform morphological opening using a disc kernel:
bw3 = imopen(bw2, strel('disk',2));
Next we remove from the binary image all connected components (cells) that have fewer than 10 pixels.
bw4 = bwareaopen(bw3, 100);
bw4_perim = bwperim(bw4);
overlay1 = imoverlay(I, bw4_perim, [1 .3 .3]);
Counting the number of groups will not yield accurate counts of cells, because several cells overlap. For this reason, we are going to apply the watershed algorithm on the image, which is able to partially divide the groups into distinct cells. The watershed algorithm interprets the gray level of pixels as the altitude of a relief. For this reason we need to modify our image so that the cell borders have the highest intensity and the background is clearly marked (we mark is as negative infinity). We achieve this by first finding the maxima which should approximately correspond to the cell nuclei and then we transform the image such that the background pixels and these maxima are the only local minima in the image:
% Discover putative cell centroids
maxs = imextendedmax(I, 5);
maxs = imclose(maxs, strel('disk',3));
maxs = imfill(maxs, 'holes');
maxs = bwareaopen(maxs, 2);
overlay2 = imoverlay(I, bw4_perim | maxs, [1 .3 .3]);
% modify the image so that the background pixels and the extended maxima pixels are forced to be the only local minima in the image.
Jc = imcomplement(I);
I_mod = imimposemin(Jc, ~bw4 | maxs);
Finally, we can apply the watershed algorithm:
L = watershed(I_mod);
labeledImage = label2rgb(L);
We can then easily count the number of discovered cells. We have detected 185 cells in this image.
[L, num] = bwlabel(L);
Let's overlay the detected cells over the original grayscale image to visually evaluate the performance of the algorithm:
mask = im2bw(L, 1);
overlay3 = imoverlay(I, mask, [1 .3 .3]);
While many cells have been correctly detected, there are several that our algorithm does not detect. Additionally we have detected several cells in the center of the image, but even a human eye could hardly count their number as they appear as a dust cloud. Some cells are fragmented into two distinct cells. Finally, the detected cells appear smaller than the real ones, which means that we were not sufficiently careful when applying the morphological operations.
The algorithm completes in 0.3s on a 3.4GHz i7-3770 CPU for an input image of dimensions 540x512 pixels.
Below I present additional results, using different images featuring cells under different imaging conditions and bubbles. The point of these results is to show that this algorithm was optimized to segment and count cells in a single type of images, and will perform poorly in the rest. Several parameters had to be changed to improve the performance in the different scenarios. Images with dark cells and bright background had to be color inverted before processing.
The algorithm performs best on this image, because it was used to guide the design of the algorithm. |
The algorithm has difficulties correctly segmenting the shapes of the cells because of the high contrast difference within them. |
The low contrast cell boundaries and large overlapping cause the algorithm to perform poorly on this image. |
The detection of bubbles could be improved by tweaking the morphological operations performed on the image. |
The varying intensities within the cells cause the algorithm to perform poorly. |
In this tutorial we have presented a relatively simple method to segment and count cells in an image. Some tweaking could improve the results of the segmentation, and improve the detection accuracy. Also, we could add a merging method to merge cells that are over-fragmented.
Manually adjusting the threshold results in much better detection of the bright spots? Regard Telkom University
ReplyDelete