4
$\begingroup$

Due to issues with the microscope I have the following image which is inherently dark. At the center of the image is an aggregate (a cluster of cells). I can get a binarized mask using the mask draw capability in mathematica. But how can it be automated? What scheme should I use to segment it. I have not worked with such poorly illuminated image (Figure 1) before. GradientOrientationFilter?

enter image description here

If you play with brightness contrast of the image you can find the aggregate

enter image description here

As per @SquareOne's recommendation: LocalAdaptiveBinarize seems to work pretty well. still not a complete solution.

enter image description here

$\endgroup$
3
  • 1
    $\begingroup$ To automize it I think more Information are needed E.g. how should a Software now that the bright spot in the top left are not the cells. The best way would be to fix the microscope. But if you come up with additional Information about expected size of the cell, app. Position... . Then I could think of an iterative scheme to find the Cluster. $\endgroup$
    – Eisbär
    Mar 1, 2017 at 7:41
  • 1
    $\begingroup$ See LocalAdaptiveBinarize $\endgroup$
    – SquareOne
    Mar 1, 2017 at 15:18
  • $\begingroup$ @SquareOne LocalAdaptiveBinarize seems to work pretty well. I have the picture posted in the question and credited you with it. Do you have a strategy to extract a Morphological Perimeter of the cluster inside in the image after the adaptive binarize $\endgroup$
    – Ali Hashmi
    Mar 1, 2017 at 18:31

1 Answer 1

6
$\begingroup$

If I assume that the cells always have similar size (~100-200 pixel diameter), I think I can do this.

First step: Load the image

img = ColorConvert[Import["https://i.stack.imgur.com/pFvT7.png"], 
  "Grayscale"]

I'm going to use the model

  • that your image is the inhomogeneous brightness due to the microscope times the "actual" image content (including the cell borders)
  • that the inhomogeneous brightness is varies very slowly and thus can be approximated using GaussianFilter[img,50]

so $img / GaussianFilter[img,50]$ is a good approximation of the "actual" image content:

eps = 10^-10; (* add a small value to the denominator to prevent division by zero *)
equalBrightness = 
 Image[Rescale[
   ImageData[img]/(GaussianFilter[ImageData[img], 50] + eps)]]

enter image description here

Next, I'll want the gradient of this preprocessed image:

gradient = 
  GaussianFilter[ImageData[equalBrightness], 10, #] & /@ 
   IdentityMatrix[2];

...as an array of complex numbers, for convenience:

gradientC = {1, I}.gradient;

I'm going to use template matching to find "something roughly circular" in this gradient image. The template will simply consist of complex numbers pointing "outwards" from the center, times some window function. That's the gradients we're looking for:

template = Array[
   (#1 + I*#2)*HammingWindow[#1]*HammingWindow[#2] &, {256, 
    256}, {{-.5, .5}, {-.5, .5}}];

ListVectorPlot[ReIm[template]]

enter image description here

Export["F:\\imgs\\so2.png", %]

The correlation between this template and the gradients found above is highest in the cell's center. (I have no idea how reliable this is. You'll have to check with other images, and play with filter sizes, preprocessing functions above):

corr = Image[
  Rescale[Re[ListCorrelate[template^2, Conjugate@gradientC^2]]]]

enter image description here

Finding the point of highest correlation is easy:

maxPos = PixelValuePositions[corr, "Max"][[1]] + Dimensions[template]/2;
HighlightImage[equalBrightness, {maxPos}]

enter image description here

Now we can use this position to create a marker mask for WatershedComponents:

markers = 
 Binarize@Rasterize[
   Graphics[{White, Point[maxPos], Circle[maxPos, 256]}, 
    PlotRange -> {{0, 0}, ImageDimensions[img]}\[Transpose], 
    ImageSize -> ImageDimensions[img], Background -> Black]]

enter image description here

This mask basically says: I'm looking for two components. One component contains the center point, the other component contains all points on the circle radius. Other than that, choose the components so that they best "fit" to the gradient strengths:

components = 
  WatershedComponents[Image[Rescale[Abs[gradientC]]], markers];

HighlightImage[equalBrightness, Image[UnitStep[-components]]]

enter image description here

This is the simplest way I can come up with to get a mask from a center a point and an estimate of the radius. If you want more control over the mask border, you'll probably have to implement a more advanced algorithm, like adaptive contours

$\endgroup$
5
  • $\begingroup$ thanks this looks brilliant and promising. i will continue from where you left. +1 for your approach $\endgroup$
    – Ali Hashmi
    Mar 1, 2017 at 20:21
  • $\begingroup$ Great technique. Could you give a reference to read in for the method of using the Gradient and the correlation to find the cell? $\endgroup$
    – Eisbär
    Mar 3, 2017 at 11:21
  • $\begingroup$ @Eisbär: I don't really have one. The basic idea is "rotate the gradients in complex space so that gradients pointing to the center add up while other gradients cancel each other out". I think I've got the idea from the article linked in this question, but they're using the phase angle to encode radius, not orientation. In this case, the shape doesn't have constant radius, so looking at the orientation makes more sense. $\endgroup$ Mar 3, 2017 at 11:52
  • $\begingroup$ (But honestly, there have been a lot of "detect a circle" questions here on MMA and elsewhere, so I might have gotten the idea from somewhere else and simply forgotten it.) $\endgroup$ Mar 3, 2017 at 11:53
  • $\begingroup$ Thanks, I lack Knowledge on this Topics, so your answer gives a lot to Digest. I will look for those articles. $\endgroup$
    – Eisbär
    Mar 3, 2017 at 12:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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