How to automatically detect blobs and count the cells inside

analyze-particles
imagej
segmentation
fiji
macro
Tags: #<Tag:0x00007fd542398a18> #<Tag:0x00007fd5423988d8> #<Tag:0x00007fd542398720> #<Tag:0x00007fd542398568> #<Tag:0x00007fd5423983d8>

#1

I have the following raw data (JPG)

What I want to do is automatically identify the blobs and count the number of cells (blue dots) inside those blobs.
What’s the way to do it in FIJI?
In reality I have hundreds of such image to process and count cells in each blob.

In the figure above for example, I manually identified 11 blobs (red box). What I want to do is to count cells inside every boxes.

I’m stuck with the following Macro, not sure how to go from here.

open("myimage_raw.jpg");
run("8-bit");
run("Gaussian Blur...", "sigma=5");
run("Enhance Contrast...", "saturated=0.3");
run("Despeckle");
run("Find Edges");

Counting cell groups
#2

I’m not sure why I just spent 2 hours doing this but try this macro (It’s a bit clunky but does what you want I think):

setForegroundColor(255, 255, 255);
setBackgroundColor(0, 0, 0);
run("Duplicate...", "title=ImageCOPY");
makeOval(-4, 17, 3428, 3428);
run("Clear Outside");
run("Select None");
run("Duplicate...", "title=Clusters");
// Color Thresholder 2.0.0-rc-59/1.51j
// Autogenerated macro, single images only!
min=newArray(3);
max=newArray(3);
filter=newArray(3);
a=getTitle();
run("HSB Stack");
run("Convert Stack to Images");
selectWindow("Hue");
rename("0");
selectWindow("Saturation");
rename("1");
selectWindow("Brightness");
rename("2");
min[0]=125;
max[0]=255;
filter[0]="pass";
min[1]=0;
max[1]=255;
filter[1]="pass";
min[2]=65;
max[2]=255;
filter[2]="pass";
for (i=0;i<3;i++){
  selectWindow(""+i);
  setThreshold(min[i], max[i]);
  run("Convert to Mask");
  if (filter[i]=="stop")  run("Invert");
}
imageCalculator("AND create", "0","1");
imageCalculator("AND create", "Result of 0","2");
for (i=0;i<3;i++){
  selectWindow(""+i);
  close();
}
selectWindow("Result of 0");
close();
selectWindow("Result of Result of 0");
rename(a);
// Colour Thresholding-------------
setOption("BlackBackground", true);
run("Make Binary");
run("Options...", "iterations=6 count=3 black do=Dilate");
run("Options...", "iterations=20 count=1 black do=Close");
run("Remove Outliers...", "radius=20 threshold=50 which=Bright");
// HERE a good time to count groups of cells
run("Options...", "iterations=9 count=1 do=Dilate");
setThreshold(1, 255);
run("Analyze Particles...", "size=5000-Infinity show=Nothing exclude clear include summarize add");
//SELECT ORIGINAL OR BLACK-BACKGROUNDED IMAGE HERE
selectWindow("ImageCOPY");
//TRY LOOPING THROUGH THE ROI MANAGER HERE
for (z=0;z<roiManager("count"); z++){
  run("Duplicate...", "title="+z);
  roiManager("Select", z);
  run("Clear Outside");
  run("Select None");
  // Color Thresholder 2.0.0-rc-59/1.51j
  // Autogenerated macro, single images only!
  min=newArray(3);
  max=newArray(3);
  filter=newArray(3);
  a=getTitle();
  run("HSB Stack");
  run("Convert Stack to Images");
  selectWindow("Hue");
  rename("0");
  selectWindow("Saturation");
  rename("1");
  selectWindow("Brightness");
  rename("2");
  min[0]=115;
  max[0]=255;
  filter[0]="pass";
  min[1]=0;
  max[1]=255;
  filter[1]="pass";
  min[2]=0;
  max[2]=82;
  filter[2]="pass";
  for (i=0;i<3;i++){
    selectWindow(""+i);
    setThreshold(min[i], max[i]);
    run("Convert to Mask");
    if (filter[i]=="stop")  run("Invert");
  }
  imageCalculator("AND create", "0","1");
  imageCalculator("AND create", "Result of 0","2");
  for (i=0;i<3;i++){
    selectWindow(""+i);
    close();
  }
  selectWindow("Result of 0");
  close();
  selectWindow("Result of Result of 0");
  rename(a);
  // Colour Thresholding-------------
  run("Analyze Particles...", "size=0-Infinity show=Outlines display exclude include    summarize");
  selectWindow(""+z);
  close();
  //ORIGINAL IMAGE REFERENCE HERE
  selectWindow("ImageCOPY");
}
close();

Kind regards,
Antinos


How to combine masked sub images of ROI Manager into one image
#3

Sorry, probably bad practice to reply to your own reply but I rushed the previous macro out as I was making myself late for an appointment. Now that I’ve had a little more time, the following is an alternative thresholding approach that partially corrects the issue the previous macro encountered in one of the very dense cell clusters, whilst also cleaning up the workspace a bit by dumping some of the output masks into a new slice of the original image:

setForegroundColor(255, 255, 255);
setBackgroundColor(0, 0, 0);
run("Duplicate...", "title=ImageCOPY");
makeOval(-4, 17, 3428, 3428);
run("Clear Outside");
run("Select None");
run("Duplicate...", "title=Clusters");
// Color Thresholder 2.0.0-rc-59/1.51j
// Autogenerated macro, single images only!
min=newArray(3);
max=newArray(3);
filter=newArray(3);
a=getTitle();
run("HSB Stack");
run("Convert Stack to Images");
selectWindow("Hue");
rename("0");
selectWindow("Saturation");
rename("1");
selectWindow("Brightness");
rename("2");
min[0]=125;
max[0]=255;
filter[0]="pass";
min[1]=0;
max[1]=255;
filter[1]="pass";
min[2]=65;
max[2]=255;
filter[2]="pass";
for (i=0;i<3;i++){
  selectWindow(""+i);
  setThreshold(min[i], max[i]);
  run("Convert to Mask");
  if (filter[i]=="stop")  run("Invert");
}
imageCalculator("AND create", "0","1");
imageCalculator("AND create", "Result of 0","2");
for (i=0;i<3;i++){
  selectWindow(""+i);
  close();
}
selectWindow("Result of 0");
close();
selectWindow("Result of Result of 0");
rename(a);
// Colour Thresholding-------------
setOption("BlackBackground", true);
run("Make Binary");
run("Options...", "iterations=6 count=3 black do=Dilate");
run("Options...", "iterations=20 count=1 black do=Close");
run("Remove Outliers...", "radius=20 threshold=50 which=Bright");
// HERE a good time to count groups of cells
run("Options...", "iterations=9 count=1 do=Dilate");
setThreshold(1, 255);
run("Analyze Particles...", "size=5000-Infinity show=Nothing exclude clear include summarize add");
//SELECT ORIGINAL OR BLACK-BACKGROUNDED IMAGE HERE
selectWindow("ImageCOPY");
run("Add Slice");
run("Select All");
run("Fill", "slice");
run("Select None");
//TRY LOOPING THROUGH THE ROI MANAGER HERE
for (z=0;z<roiManager("count"); z++){
  setSlice(1);
  run("Duplicate...", "title="+z);
  roiManager("Select", z);
  run("Clear Outside");
  run("Select None");
  // Color Thresholder 2.0.0-rc-59/1.51j
  // Autogenerated macro, single images only!
  min=newArray(3);
  max=newArray(3);
  filter=newArray(3);
  a=getTitle();
  run("HSB Stack");
  run("Convert Stack to Images");
  selectWindow("Hue");
  rename("0");
  selectWindow("Saturation");
  rename("1");
  selectWindow("Brightness");
  rename("2");
  min[0]=115;
  max[0]=255;
  filter[0]="pass";
  min[1]=0;
  max[1]=255;
  filter[1]="pass";
  min[2]=0;
  max[2]=82;
  filter[2]="pass";
  for (i=0;i<3;i++){
    selectWindow(""+i);
    setThreshold(min[i], max[i]);
    run("Convert to Mask");
    if (filter[i]=="stop")  run("Invert");
  }
  imageCalculator("AND create", "0","1");
  imageCalculator("AND create", "Result of 0","2");
  for (i=0;i<3;i++){
    selectWindow(""+i);
    close();
  }
  selectWindow("Result of 0");
  close();
  selectWindow("Result of Result of 0");
  rename(a);
  // Colour Thresholding-------------
  run("Make Binary");
  run("Watershed");
  run("Make Binary");
  run("Watershed");
  setThreshold(1, 255);
  run("Analyze Particles...", "size=0-Infinity show=Masks display exclude include    summarize");
  selectWindow("Mask of "+z);
  run("Copy");
  close();
  selectWindow(""+z);
  close();
  //ORIGINAL IMAGE REFERENCE HERE
  selectWindow("ImageCOPY");
  setSlice(2);
  setPasteMode("Transparent-white");
  run("Paste");
}
run("Select None");
rename("Composite Image");

Still not perfect but maybe something for you to work with.

Regards.


#4

Dear Antinos,

Thanks so much. Your last code works so well on my JPG image.
But it cannot detect the cells and cluster my orignal image on TIF format (downloadable here).

run("Set Scale...", "distance=200 known=2000 pixel=1 unit=µm global");

How can I modify your code to deal with it so that it produce same result as JPG version.

I have hundreds of TIF images to process using your code.
Thanks and hope to hear from you again.

P.D.


#5

Hi again,

I’m glad I could help. The reason the .tiff is not working is because the first ‘analyze particles’ function was set up to reject objects less than 5000 units^2. This was fine with the .jpeg since the default units were pixels, however, the .tiff has scaling information, so the default unit for this image is an actual unit of distance. To get the macro to work as before just replace the first ‘analyze particles’ command with the following:

run("Analyze Particles...", "size=5000-Infinity pixel exclude clear include summarize add");

The above specifies pixel units rather than scaled units.

With regards you having very many images to process, I am afraid I didn’t consider this in too much depth when designing the macro. Without a re-write, one little innovation which may speed things up a lot would be to use batch mode: Process>Batch>Macro OR add “setBatchMode(true);” to the start of the macro. It may not work too well to process multiple images the way the macro is written, but even using it for a single image will hide a lot of the displayed windows and therefore speed things up. If you also specify an ‘output’ location you’ll get the final composite image saved for validation purposes aswell.

Best regards.


#6

One more question. The loop you make here.
Is it done manually, or generated using Record?
If the latter can you explain the step to do it


#7

Hi Peverall,

I’m not sure the moderators are going to appreciate this question perpetually being bumped like this, but in answer to your question, the ‘for loop’ is basic syntax for iterating over code in multiple programming languages. It wasn’t auto generated but I did google to find the “roiManager(“count”)” command. For loops are super useful so maybe worth familiarising yourself with by google searching and/or experimentation.

Very briefly, “z” is created as a temporary number variable, beginning at ‘0’, and increases by one (the ‘z++’ part) for every loop until the maximum specified value is reached (in this case defined by the total number of entries in the roiManager table). The changing values of ‘z’ can be used by code within the loop.

Best,
Ant


#8

@Peverall_Dubois, it’s very common to record the basic commands with the recorder, and then add loops and variables like @antinos did to make the macro more efficient and versatile. This page has a nice intro to the macro language: