Batch Processing Help - Cropping> Histogram Matching>CLAHE>Save in new folder?

Hi All,

I have cameratrap images from 10 farms where I recorded birds on heaps of cattle feed. I have 10,000 JPEG images total, approx 1000 per farm. Images were taken every 5 minutes for 6 consecutive days, so the sunlight/weather changes dramatically. In order to count birds accurately, I am trying to roughly balance the lighting for each image. In order to do this I need to:

  1. Select all images in a folder (for a specific farm).
  2. Crop the area of interest from each image.
  3. Histogram-match all the cropped images to the clearest image from the specific farm.
  4. Use the CLAHE plugin to enhance local contrast.
  5. Save the resulting images in a new folder.

Currently:
I am performing these parts separately in Fiji and I have only done this with small sample folders containing 5 JPEGS:

1 and 2. Process > Batch > Macro… - This crops my JPEGS and (slowly) makes a new folder of cropped Tiff images.
3. I use File > Import > Image Sequence… Which creates a stack of the 5 images. In order to match histograms of all images to a chosen image, I have been using the script found on the third reply Here . This produces a new stack of histogram-matched images.
4. I then run the CLAHE script found Here . This alters the local contrast for each image in the stack.
5. I then manually save the sequence of images.

As you can see, this is not elegant or time-efficient. When I try to perform these actions on large folders (1000 images), it is far too slow. Also I cannot open a stack big enough to use this method for large folders of hundreds of images.

Can anyone show me how I can do this more efficiently, with large numbers of images, and minimal human input? I have searched the forum but I cannot easily solve this myself.

Many thanks,
Richard

I’ve been looking at this problem further, and a key issue I have is that I have learnt to crop in Imagej Macro language, HistogramMatcher runs in beanshell, and CLAHE then runs in ImageJ Macro language. This makes it difficult to run them on one script, particularly as each script asks for user input, for example in dialog boxes.

Any help would be very much appreciated.
Thanks,
Richard

I have now made a single .ijm script to run crop and CLAHE, but I still need to find a way to include Histogram Matcher in the same macro.

Any ideas? I want to combine the histogram matcher so that the whole thing runs as one macro and I only have to enter input directory, output directory, and a reference image for the histogram match.

Thanks,
Richard

  • Save your Beanshell script into ./Fiji.app/scripts/MyMenu/Match_Histogram.bsh
  • Restart Fiji
  • Start the macro recorder (Plugins > Macros > Record…)
  • Run your new menu command MyMenu > Match Histogram
  • Use the recorded call in your macro

as explained here in the thread you linked already:

1 Like

Hi Jan @imagejan ,

Thanks for the reply! Basically I think that my beanshell code for the histogram matcher is not correct.

Here’s details of what I have done so far:
I tried the method described in the other thread, but found several problems which stop it being generic. First I had trouble finding a way to make new scripts appear in any menus, eventually I had to create a 2nd plugins folder within Fiji/scripts, and place my macros there before restarting Fiji. Only then would they appear on menus and respond to calls from scripts. So far so good!

Below is my current script Crop_HistMatch_CLAHE.ijm . It successfuly performs crop and CLAHE and saves the resulting images. The Match Histogram call works but the macro fails to function.

blocksize = 127;
histogram_bins = 256;
maximum_slope = 3;
mask = "*None*";
fast = true;
process_as_composite = true;


dir1 = getDirectory( "Choose a Directory" );
dir2 = getDirectory( "Choose a Directory" );
setBatchMode( true );
fList = getFileList( dir1 );
for ( i=0; i < fList.length; i++ ) { 
	open( dir1 + fList[i] );
	getDimensions( width, height, channels, slices, frames );
isComposite = channels > 1;
parameters =
  "blocksize=" + blocksize +
  " histogram=" + histogram_bins +
  " maximum=" + maximum_slope +
  " mask=" + mask;
if ( fast )
  parameters += " fast_(less_accurate)";
if ( isComposite && process_as_composite ) {
  parameters += " process_as_composite";
  channels = 1;
}
	makeRectangle(786, 738, 2376, 1236);
	run( "Crop" );
	**run( "Match Histograms" );**
	run( "Enhance Local Contrast (CLAHE)", parameters );
	saveAs( "jpeg", dir2 + File.nameWithoutExtension + "B" + ".jpg" );
	close();
}
setBatchMode( false );
exit();

So now my problem is with the script for Match_Histogram.bsh. I want to select my histogram reference image in the Crop_HistMatch_CLAHE.ijm in the same place I select the input and output directories. I then want Match_Histogram.bsh to match the histogram of the open image to the reference image.

What would you suggest as a script for Match_Histogram.bsh? Here is my non-functioning version:

import ij.IJ;
import histogram2.HistogramMatcher;
import ij.ImagePlus;
imp1 = IJ.getImage();
imp2 = IJ.openImage(/Volumes/RICH BACKUP/Starling Backup/Experiment 1/Photos/A_Raftra/A Raftra Cropped/A Raftra-182.tif);


ip1 = imp1.getProcessor();
ip2 = imp2.getProcessor();

hist1 = ip1.getHistogram();
hist2 = ip2.getHistogram();

matcher = new HistogramMatcher();
newHist = matcher.matchHistograms(hist1, hist2);

ip1.applyTable(newHist);
imp1.setProcessor(ip1);

// show the histograms of both images
IJ.run(imp1, "Histogram", "");
IJ.run(imp2, "Histogram", "");

Any help very much appreciated!
Thank you,
Richard

Instead of these two lines (that btw contain syntax errors, the path would need to be in quotes), I suggest to use script parameters. While you could use @ImagePlus for both, and then in the macro you could define the input by name:

run("Match Histogram", "imp1=someImage.tif imp2=[A Raftra-182.tif]");

the solution requiring the least changes to your original code would be a combination of an #@ ImagePlus and a #@ File parameter, such as:

#@ ImagePlus imp1
#@ File reference
import ij.IJ;
import histogram2.HistogramMatcher;
import ij.ImagePlus;

imp2 = IJ.openImage(reference.getPath());


ip1 = imp1.getProcessor();
ip2 = imp2.getProcessor();

hist1 = ip1.getHistogram();
hist2 = ip2.getHistogram();

matcher = new HistogramMatcher();
newHist = matcher.matchHistograms(hist1, hist2);

ip1.applyTable(newHist);
imp1.setProcessor(ip1);

// show the histograms of both images
IJ.run(imp1, "Histogram", "");
IJ.run(imp2, "Histogram", "");

… which would be recorded like this (disclaimer: I didn’t test it):

run("Match Histogram", "reference=[/path/to/your/A Raftra-182.tif]");

In the calling macro you could then replace the recorded path by whatever variable you’d like to contain the path to your current reference image:

referencePath = "/path/to/your/reference.tif"
run("Match Histogram", "reference=[" + referencePath + "]");

Hope that helps.

Jan thanks so much for your help! Unfortunately I am still trying to find a functioning solution to this…

I’ve been trying to make this work but keep getting error messages or finding that the beanshell script seems to get “stuck” on the first image handed to it. It runs the histogram match on the first image multiple times.

e.g. 5 images in my test directory dir1, means the first image in dir1 seems to go though the histogram matcher (and CLAHE) 5 times, producing a results directory dir2 that contains one image which has very very strange colouring.

I think the problem could be that the bsh script is remembering (persistence issue?) the very first image handed to it, and recalling the same image every time it is asked to run.

I’ve seen the example where eval() is used but I can’t get that to work for me either. Could anyone point me to any example of the histogram matcher being called from a loop within an .ijm macro?

Many thanks,
Richard