I am trying to do a little image segmentation on some cells taken from microscopy images that have uneven illumination. I am having trouble getting the background of the image cleaned up after applying LocalAdaptiveBinarize so I can end up with closed white foreground cells on a plain black background.

Here is the original image:

enter image description here

Due to the uneven illumination (darker in the upper left part of image and brighter in the lower right) I couldn't find a simple threshold that worked well across the whole image so I tried to use LocalAdaptiveBinarize by doing LocalAdaptiveBinarize[image, 10]. Here is the result:

enter image description here

The cells aren't bad but there are a couple annoying defects I would like to improve: One of the two cells in the middle at the top has a chunk of it missing on its lower edge and the cell beneath it has a hole inside it. I can fill in the hole of the bottom cell by using FillingTransform[localBinImage,1] and get this:

enter image description here

I would be interested if anyone has any suggestions on how to improve the cell at the top with the chunk missing from its lower border.

At any rate, with the exception of the top middle cell the rest of the cells now look close to passable but the background is still a mess from using LocalAdaptiveBinarize. I would like to have a way to clean up the background so I just have the cells by themselves on a plain black background that makes it easy to use MorphologicalComponents or something similar to segment the cells.

Any suggestions on how to clean up this background?

  • $\begingroup$ You may try something like ImageMultiply[#, Binarize@DeleteSmallComponents@Erosion[EntropyFilter[Blur[#, 5], 1], 2]] &@ Import@"http://i.stack.imgur.com/5ni1u.jpg" $\endgroup$ – Dr. belisarius Oct 13 '14 at 5:13
  • $\begingroup$ You could try Import["http://i.stack.imgur.com/5ni1u.jpg"] // EdgeDetect // DeleteSmallComponents // FillingTransform. The cell at the top has an "invisible" left boundary, so it doesn't get filled in. $\endgroup$ – Stephen Luttrell Oct 13 '14 at 10:04
  • $\begingroup$ I don't have Mathematica 9 on my work computer but you could try a HighpassFilter to remove the gradual (low frequency) changes in the background illumination while preserving the high frequency changes in the cells. $\endgroup$ – s0rce Oct 13 '14 at 18:03

A clunky way of doing this, and I can't resolve the smaller cells, but here it goes. Taking from my answer here

img  = Import["http://i.stack.imgur.com/5ni1u.jpg"];
imgd = ImageData@ImageAdjust@ColorConvert[img, "GrayScale"];
imgp = Flatten[
   Table[{x, y, imgd[[x, y]]}, {x, 1, 346}, {y, 1, 462}], {2, 1}];
imgbg = Table[imgd[[x, y]] - nlm[x, y], {x, 346}, {y, 462}]; (* takes a while *)

Here, I am fitting the image data to a plane, assuming that the lighting effect is linear. One may improve the fit with a different function in the NonlinearModelFit if desired. The results can be seen by plotting the ImageData:


Mathematica graphics


Mathematica graphics

We got rid of the slant, which is what this question is asking for. As for whether or not this improves the cell isolation, I'm not sure:

MorphologicalComponents[ImageConvolve[Image[imgbg], DiskMatrix[1]], 
   0.3] // Colorize // DeleteSmallComponents

Mathematica graphics

It does separate the two cells of interest to the OP, but I couldn't find a decent set of parameters or filters to isolate the two cells to the left or at the bottom of the image. The original microscope image, if it is of a higher resolution, may be needed at this point.

  • $\begingroup$ This looks quite nice and I like the idea of looking at and detrending the surface. Thanks. $\endgroup$ – user13999 Oct 14 '14 at 12:28

I believe that you should use a high-pass filter. The built-in HighpassFilter seems very slow and produces artifacts. Here is an approximate method that is faster and appears to yield a better result:

hp[img_Image, w_Integer] := Image[0.5 + Subtract @@ ImageData /@ {img, Blur[img, w]}]


img = Import["http://i.stack.imgur.com/5ni1u.jpg"];

img2 = hp[img, 30]

enter image description here

Threshold[img2, 0.52]

enter image description here

p.s. It is shorter to use ImageApply but also slower:

hp2[img_Image, w_Integer] := ImageApply[# - #2 + 0.5 &, {img, Blur[img, w]}]
  • $\begingroup$ This is what I thought would work but my Mathematica 8 didn't have the HighpassFilter and I didn't feel like figuring out how to implement it. +1. Free ImageJ also has a good Bandpass filter built in. $\endgroup$ – s0rce Oct 13 '14 at 20:04
  • $\begingroup$ @s0rce I figured out how to implement a Photoshop-like HPF and it is backward-compatible with v8. Take a look. $\endgroup$ – Mr.Wizard Oct 13 '14 at 20:50
  • $\begingroup$ Mr. Wizard, the result looks good and should be simple to fill and clean up. Just to understand the code, are you just generating a new image that is the difference of the original image with a blurred version of the image? So is this essentially unsharp masking without the step where you add the unsharp mask back to the original image? $\endgroup$ – user13999 Oct 14 '14 at 12:37
  • $\begingroup$ @user13999 Yes, that is correct. $\endgroup$ – Mr.Wizard Oct 20 '14 at 21:06

Detect cells:

cellEdges = 
 GradientFilter[ImageAdjust[img], 2] // ImageAdjust // Binarize
cells = Closing[cellEdges, 10]

Separate detected cells:

MorphologicalComponents[cells] // Colorize

Mathematica graphics

  • 2
    $\begingroup$ BTW, I discovered a pretty neat way to generate psychedelic images: mc = MorphologicalComponents[LocalAdaptiveBinarize[MeanShiftFilter[ImageAdjust@img, 4, .05, MaxIterations -> 100], 5]]; Colorize[mc /. MapThread[Rule, {#, RandomSample[#]}] &@ Range[1, Max[mc]]] produces this image: i.stack.imgur.com/ECYev.png $\endgroup$ – shrx Oct 13 '14 at 19:55
  • 1
    $\begingroup$ shrx, the people at my lab will get a kick out of seeing your image of the cells on acid. Thanks! $\endgroup$ – user13999 Oct 14 '14 at 12:39

I guess it depends on exactly what you are looking for, but here is one way to approach this: smooth with a RangeFilter and then Binarize.

img = Import["http://i.stack.imgur.com/5ni1u.jpg"];
Binarize[RangeFilter[img, 1], 0.1]

enter image description here

  • $\begingroup$ Thanks for the suggestion, however ideally I would like to arrive at a closed and/or filled contour for each cell so I can get their area and also use the resulting segmentation as a mask overlay for other color channels (e.g. for fluorescence channel images taken from these cells). Any ideas? $\endgroup$ – user13999 Oct 13 '14 at 4:04
  • $\begingroup$ Your question was about cleaning up the background. Try different binarization values, or try using the above binarization as a mask (to multiply by the original). $\endgroup$ – bill s Oct 13 '14 at 4:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.