Image preprocessing - Automatically defining a ROI

fiji
imagej

#1

Hello!

I’ve been trying to find a way to pre-process my images, but to no avail… Maybe someone here has any suggestion?

I have a large amount of microfluidics chip images where I am tracking flowing cells. I have used TrackMate successfully to do the tracking, but the characteristics of the chip required me to create a mask in order to constrain the analysis only to what is inside the channels. At the moment, I’m doing the cropping and masking individually per file, but it’s a time consuming process. The location (and sometimes orientation) of the chip changes slightly per image, making cropping macros useless.

This is a typical, direct from the microscope image:

And this is the type of cropping that I would like to do:

Is there anything I could use that would recognize the boundaries of the region I want to crop per image, so that I could then use the same mask and image calculation for all of the images?

Thank you!


#2

Good day,

you may generate the vertical and horizontal projection of your image.
If you study the results, you willl get an idea of how to find the relevant borders of your device.

Regarding rotation things are a bit more involved but you may use the projections as well. If you’ve found a criterion for the optimum orientation angle, you may use e.g. the “Golden Section Search” to find it and align your image.

BTW, why didin’t you position your device in the center of field of your microscope?
Furthermore there is a considerable vertical illumination gradient. Are you sure your microscope is correctly set up?

Regards

Herbie


#3

Hello Herbie,

Thank you for the suggestions. I will attempt to use the projections to find the borders.

The chips were not uniformized, and are thus difficult to position in the same spot consistently. The field of view used was actually centered in the original program used to capture the images (PCC v3.1). Regarding the illumination gradient, I believe that may be caused by a slight bend on the chip created by the microfluidics tubing (even after fixing the chip in place with tape!).

Thank you!


#4

Here is an ImageJ-macro that does what you wish, at least for the provided sample image and all of its flipped versions:

requires( "1.52g" );
setBatchMode(true);
setOption("BlackBackground", true);
setAutoThreshold("Default dark");
run("Convert to Mask");
run("Select All");
p=getProfile();
val=0;
do {min=Array.findMinima(p,val); val++;} while (min.length>6);
x=0;
val=0;
for ( i=2; i<min.length; i++ ) { 
   cond = min[i]<(p.length-20) && min[i]>20;
   if (x<1 && cond) x=min[i];
   if (x>0 && cond) val=min[i];
}
w=20+abs(x-val);
if (x>val) x=val-10; else x-=10;
makeRectangle(x,0,w,getHeight());
run("Duplicate...", "title=temp");
run("Select All");
setKeyDown("alt");
p=getProfile();
setKeyDown("none");
close();
Array.getStatistics(p,mi);
min=Array.findMinima(p, mi*0.8);
Array.getStatistics(min,mi,mx);
run("Revert");
makeRectangle(x,mi,w,mx-mi);
run("Crop");
setBatchMode(false);
exit();

Paste the above macro code to an empty macro window (Plugins >> New >> Macro) and run it with the image open in ImageJ.

The macro does no angular alignment.

Regards

Herbie


#5

Hi PLM!

Here’s another take on a macro to perform the task you requested. It relies on the highly structured region of the microchannels. Depending on the different chambers that you might use in the future, either this macro or @Herbie’s could be more suitable, or easier to adapt.

title=getTitle();

setBatchMode(true);
run("Duplicate...", " ");

//detection using variance
run("32-bit");
run("Variance...", "radius=60");
setAutoThreshold("Default dark no-reset");

//mask creation & single selection - only one big area expected
run("Analyze Particles...", "size=100000-Infinity show=Masks");
setAutoThreshold("Default");
run("Create Selection");

//returning to the original image
selectWindow(title);
run("Restore Selection");
setBatchMode(false);

//making a cropped version
run("Crop");  //you can change this line to make a duplicate instead of cropping
run("Select None");

Cheers!
Nico


#6

Hi Herbie and NicoDF,

Thank you both very much for your input! Unfortunately, I’m running into some trouble with both macros, and I don’t quite know where to modify them:

@Herbie:

This macro works perfectly on ~70% of the images. However, on the remainder, smaller sections are cropped instead of the channel section (e.g. below). I assume this is caused by the projection’s minima being set out of order by features of the chip? (in this case, a mote of dust on one of the edges). Could I change anything to offset this effect?

@NicoDF:

This macro works really well on getting rid of the extra vertical space. However, it seems to keep the horizontal space on most of the images. Is there any way to reduce the area while keeping the structure requirement so as to force the selection to be reduced to the microchannel area?

Thank you in advance! (And of course, I’ll be happy to cite if/when this data is published)


#7

Good day,

could you please post an image that shows such wrong cropping?

What about rotational alignment. What are the expected maximum angles?

Regards

Herbie


#8

Hi Herbie,

This image, for instance:

Results in this cropped image:

The maximum angle in all the images is less than 2° in either direction from the horizontal line.

Thank you!


#9

Sorry but please don’t use JPG-compressed images!
JPG-format introduces artifacts that can’t be removed
(Converting a JPG-compressed image to TIFF- or PNG-format doesn’t make sense.)

Please repost the above image either in PNG- or in TIF-format as ZIP-archive.

Regards

Herbie


#10

Hi Herbie,

Sorry, I didn’t notice the full image was saved in .jpeg (strange, as I saved both in .png and the cropped one was uploaded as .png)… I’ve now edited the previous post with a .png image.


#11

Image3 is partly out of focus (lower half) and it has lower contrast than your first sample image. Furthermore, the vertical line on the right is strongly disrupted by cells.

It’s always the same problem with sample images that aren’t typical for the set of images to be processed and it is not very funny to have to rework code due to this situation.

I shall see what can be done.

Herbie


#12

Here is a slightly change ImageJ-macro that works with the problematic sample image “Image3”:

  1. Revision for more robustness
  2. Revision for nicer code
requires( "1.52h" );
setBatchMode(true);
setOption("BlackBackground", true);
setBackgroundColor(255,255,255);
/* place for rotation alignment code */
run("Duplicate...", "title=copy");
setAutoThreshold("IsoData dark");
run("Convert to Mask");
run("Select All");
p=getProfile();
min=Array.findMinima(p,0);
w=abs(min[0]-min[1])-160;
if (min[0]<min[1]) x=min[0]+80; else x=min[1]+80;
p=Array.slice(p,x,x+w);
makeRectangle(x,0,w,getHeight());
run("Clear Outside");
min=Array.findMinima(p,0);
w=20+abs(min[0]-min[1]);
if (min[0]<min[1]) x+=min[0]-10; else x+=min[1]-10;
run("Select All");
setKeyDown("alt");
p=getProfile();
setKeyDown("none");
Array.getStatistics(p,mi);
min=Array.findMinima(p,mi*0.2);
Array.getStatistics(min,mi,mx);
close();
makeRectangle(x,mi,w,mx-mi);
run("Crop");
setBatchMode(false);
exit();

Not sure if the code change will be effective for other images.

HTH

Herbie


#13

Hi Herbie and NicoDF,

The variability of the images made it so that a significant percentage still was not recognized by the macros.
However, a colleague of mine has implemented a template matching plugin (https://sites.google.com/site/qingzongtseng/template-matching-ij-plugin) in a macro which is working beautifully, as the channels stay constant in all the images and the contrast of their edges is quite strong, meaning the pattern can be quickly and consistently recognized. It also allows for recognizing changes in rotation, and thus it has solved all of my problems (or so it seems for now).

Thank you both very much for all your help!


#14

Good to hear you’ve found a solution, however template matching per se doesn’t cope with rotation and size.

BTW, please make sure that your microscope is adjusted correctly. The out-of-focus situation shows that image acquisition is clearly sub-optimum. Optimum image acquisition is superior to all attempts of post hoc image processing and post hoc contrast enhancement and image sharpening will never compensate for sub-optimum image acquisition.

The macro posted before doesn’t provide correct results for objects that are rotated more than about 0.05 deg but, as you can see from the code, a section for rotational alignment can be included. Actually, it aligns with high precision and consists of a few lines of code using the “Golden Section Search” as pointed out in my first post of this thread.

Good luck

Herbie


#15

Hi ,
assuming all images have the same magnification and orientation, you could use a template matching plugin like the FeatureFinder
http://imagejdocu.tudor.lu/doku.php?id=plugin:analysis:feature_finder:start
For the template (prototype), you can take an image of the whole array (or maybe the average of a few such images), then take the position of the best match (e.g. use the deviation map output and take the position of the minimum, using ‘find Maxima’ with a huge value for the tolerance).
If the scale and orientation are not perfectly equal, but still almost the same, take an image of one of the cells as a template, and get a list of matching positions from the Feature Finder. You can then take the average of maximum and minimum x and y as the center.


#16

Dear Michael,

as the OP told us, (s)he is already using template matching.

However, I’m totally unclear how (s)he manages to treat rotated and scaled images.

My approach is scale agnostic and corrects rotations with great precision.
(Rotation alignment not posted yet.)

In the end the OP decides what (s)he prefers and I hope that (s)he will remedy the image acquisition deficiencies.

Best

Herbie


#17

Here’s a little macro that corrects much of the rotation.
//-----------------begin macro
imagename=getTitle;

makeRectangle(224, 60, 209, 138); //The angle is measured in the yellow box
run(“OrientationJ Dominant Direction”);
IJ.renameResults("Dominant Direction of "+imagename,“Results”);
tiltAngle=getResult(“Orientation [Degrees]”);
print("Original Tilt Angle = "+tiltAngle);

run(“Grid…”, “grid=Lines area=2000 color=Cyan”); //A first Look before rotation correction
selectWindow(“Log”); //Show the before angle in the yellow box
waitForUser(“Check out the Grid allignment prior to rotation”);

run(“Select All”); //Rotate the entire image
run(“Rotate… “, “angle=”+tiltAngle+” grid=0 interpolation=Bicubic enlarge”);
close(“Results”);
run(“Select None”);
run(“Grid…”, “grid=Lines area=2000 color=Cyan”); //The after rotation look

makeRectangle(224, 60, 209, 138); //Shown for reference
run(“OrientationJ Dominant Direction”); //Measureing the angle after rotation
IJ.renameResults("Dominant Direction of "+imagename,“Results”);
tiltAngle=getResult(“Orientation [Degrees]”);
print("Corrected Tilt Angle= "+tiltAngle);
close(“Results”);
selectWindow(“Log”); //Show the before and after yellow box angles

//-----------------end macro

I also noticed that the lighting is very non uniform…and not in a linear way.


#18

Apparently, there is a certain interest in automatic rotation alignment of the images in question …

Here is the missing macro code that is to be inserted at

/* place for rotation alignment code */

of the above posted macro code for the automatic image cropping:

//•••• start of rotation alignment ••••
List.setCommands;
if ( List.get("golden orientation")=="") {
   showMessage("Required Plugin", "<html><h3>Macro requires ImageJ-PlugIn \"golden orientation\"!</h3>"
     +"<a href=\"https://www.gluender.de/Miscellanea/MiscTexts/UtilitiesText.html\">Download</a>"); exit();
}
run("Duplicate...", "title=copy");
setAutoThreshold("IsoData dark");
run("Convert to Mask");
run("Select All");
p=getProfile();
val=0;
do {min=Array.findMinima(p,val); val++;} while (min.length>4);
val = 0;
while (min[val]<50 || min[val]>0.5*p.length) {val++;}
makeRectangle(min[val]-40,0,80,getHeight());
run("Crop");
run("Rotate 90 Degrees Right");
run("golden orientation", "lower=-2 upper=2 accuracy=0.01 limit=20");
phi=split(call("golden_orientation.macroReturn"), " ");
close();
run("Rotate... ", "angle="+phi[0]+" grid=0 interpolation=Bicubic fill");
//•••• end of rotation alignment ••••

Please note that this code requires the ImageJ-plugin “golden_orientation”.

Regards

Herbie


#19

Dear all,

While the pattern matching approach per se does not correct for rotation, my colleague’s approach was to iterate over a range of angles and attribute a score to the pattern match in each angle, and then cropping on the best angle with the size from the pattern image (i.e. the “Microfluidics-2.png” in the first post). It has the limitation of not automatically detecting angles outside of the range, but as the actual range of angles is quite small and predictable it is not a problem.

Cheers!
PLM


#20

Hi Herbie,
ok, I had not got that it is more than tiny differences of orientations. In that case my approach would have been looking at the strongest maximum of the FFT in the range where the distance between the channels can be, and use its position to unrotate the image (and possibly correct the scale). If your ‘golden orientation’ algorithm or the (gradient-based) OrientationJ works, it’s fine anyhow!
–Michael