7

I have the following image where bright circular shaped objects are seen. The image has usually an inhomogeneous background and the objects have different sizes and brightness's.

enter image description here

The aim is to remove the background noise and to detect all object's positions.

How can that be done best?

My solution for the example image is:

fileNameOpen = SystemDialogInput["FileOpen"];
image = Import[fileNameOpen]

background = ImageConvolve[image, GaussianMatrix[7]];
subImage = Image[ImageData[image] - ImageData[background]];

t = FindThreshold[subImage, Method -> "Entropy"];
binImg = Binarize[subImage, t];
binImg = DeleteSmallComponents[binImg, 1];

resultImage = 
 Show[image, 
  Graphics[{Red, Thick, 
    Circle[#[[1]], #[[2]]] & /@ 
     ComponentMeasurements[
       ImageMultiply[image, binImg], {"Centroid", 
        "EquivalentDiskRadius"}][[All, 2]]}]]

fileNameSave = SystemDialogInput["FileSave"];
Export[fileNameSave, resultImage];

That gives as output:

enter image description here

The problem is that I have to adjust manually the radius of GaussianMatrix and the number in DeleteSmallComponents. Their optimum values depend also on the Method used in FindThreshold (e.g. "MinimumError" can also be used") .

mrz
  • 11,686
  • 2
  • 25
  • 81

2 Answers2

9

A better way of removing the background might be to use a TotalVariationFilter, which seems to be less sensitive to the parameter it is given compared to convolution. In fact, I've not even used any parameters.

Your code can also be simplified since Binarize takes a Method option. In fact, you don't actually need it, the automatic method works just as well.

The size option of DeleteSmallComponents[] depends on the minimum feature size in your image, so using a value of 1 is a good assumption in this example, but may vary for other images.

image = Import["https://i.stack.imgur.com/czhuI.png"];
background = TotalVariationFilter[image];
subImage = ImageSubtract[image, background];

(* Or use Binarize[subImage, Method -> "Entropy"]; *)
binImg = DeleteSmallComponents[Binarize@subImage, 1];

resultImage = Show[image, 
                   Graphics[{Red, Disk[#, 2] & /@ 
                   ComponentMeasurements[binImg, "Centroid"][[All, 2]]}]
              ]

enter image description here

dr.blochwave
  • 8,768
  • 3
  • 42
  • 76
  • Great ... only at the lower left and right corners you detect objects which are not seen in the image. But when I use TotalVariationFilter[image, MaxIterations -> 100]; your code produces perfect results. – mrz Mar 10 '16 at 17:47
7

BilateralFilter replaces each pixel by a weighted average of its neighbors, using normalized Gaussian matrices as weights.

so

BilateralFilter[i, 4, 1]

Mathematica graphics

is "almost" your background.

If you have troubles with fine-tuning for other images, you may "calculate" the 4 as

Round[Sqrt[Total@Flatten@ImageData@SelectComponents[MaxDetect[i], "Area", -1]/Pi] // N]
(* 4 *)

Use it like this:

pts = ComponentMeasurements[Binarize@ImageSubtract[i, BilateralFilter[i, 4, 1]], 
                           "Centroid"];

Show[i, Graphics[{Red, Point[pts[[All, 2]]]}]]

enter image description here

Dr. belisarius
  • 115,881
  • 13
  • 203
  • 453