Image features measurement in batch mode not working

batch-processing
Tags: #<Tag:0x00007fa30b1f4270>

#1

Hello everybody,

I am new to this forum and very very rusty with the whole macro language writing.

I am currently trying to batch process a series of binary images containing a series of 2-D shapes of which I want to calculate different parameters using a macro function done for this purpose. Unfortunately, all my attempts so far led to errors despite all the guides and posts I’ve read.

These are my steps and goals:

  1. Open the binary image
  2. Using Threshold, I select the white portions of the image only
  3. Now I apply the ad-hoc macro, which measures several parameters of each single white shape, creating a “Results” table and a drawing of the shapes
  4. I then save the results table using the plugin “read and write excel”
  5. Finally I would like to save the drawing in a output folder

Please see a screenshot showing the thresholded image (top left), the shapes drawing (bottom left) and the results table (bottom right) after the ad-hoc macro is applied.

Here is my code:

setAutoThreshold(“Default”);
//run(“Threshold…”);
setThreshold(245, 255);
run(“ad-hoc macro.ijm”);
run(“Read and Write Excel”);

I tried to use this macro code in batch processing, as I have many images to process, but it doesn’t work. The batch processing runs twice, saving the first two sequential images looking exactly the same as the input images, before to display the message “A thresholded image or 8-bit binary image is required…”. For what I understand the batch macro get stuck in finding the right image to work with. In addition, the drawing is not saved.

I hope someone can help me to solve this.

Thank you so much for all your help!

If something is not clear please let me know, it is not easy to explain this.

Thanks

Pier


#2

Thanks for the detailed post. The batch processing is good for simple tasks but doesn’t give you a lot of control for creating new images and doing more complex work.

Scripting is ideal for this and there is a nice example of a batch processing template (for processing a folder) included with the script editor which will deal with all the input and output for you.

My recommendation would be to bring the ad-hoc macro code into a single script to batch process which will give you much more control over inputs and outputs. Something like this:

//@ File (label = "Input directory", style = "directory") input
//@ File (label = "Output directory", style = "directory") output
//@ String (label = "File suffix", value = ".tif") suffix

//-- Call the initial function to get a list of files
processFolder(input);
print("Done");

//-- FUNCTIONS ---
    function processFolder(input) {
    	list = getFileList(input);
    	list = Array.sort(list);
    	for (i = 0; i < list.length; i++) {
    		if(endsWith(list[i], suffix))
//-- Call the file processing function
    			processFile(input, output, list[i]);
    	}
    }

    function processFile(input, output, file) {
    print("Processing: " + input + File.separator + file);
    
    //-- Open the file
    open(input+File.separator+file);
    title=getTitle();

    //-- Your code here -------------------
    setAutoThreshold("Default");
    setThreshold(245, 255);

    //-- Copy the contents of the ad-hoc macro into here instead of calling it
    //run("ad-hoc macro.ijm");

    //-- Save out your drawing here
    //-- NOTE The name might will be different based on your processing (but based on your screenshot it might be ok)
    selectWindow("Drawing of "+title);
    saveAs("TIFF",output+File.separator+replace(file,suffix,"_drawing.tif"));
    //-- Close everything before the next image is opened
    close("*");

    //-- Save out the results table here:
    selectWindow("Results");
    saveAs("Results", output+File.separator+replace(file,suffix,"_results.csv"));
    }

I’ve pulled the recursive part of the code for clarity but hopefully you can work through and see how it all works. This also assumes that you are overwriting the results table each time. If this is not the case you will need to close (or clear) the table at the end of each loop.

Hope that helps!


#3

Thank you very much dnmason!!!

I will try straight away and let you know. Really thank you so much!!!


#4

Dear dnmason,

I do apologise to write you again, but I can’t manage to let the script to work properly, as it stops immediately at the first image of the list.

When I run it, it correctly asks me for the input and output folders, then it thresholds and execute the shape parameters macro, but the Results folder is empty, and an error appears which doesn’t make sense to me (see screenshots MacroErrors

).

Just to put you in context, after the threshold is applied to each binary image, the main macro operates the contouring of each particle excluding those on the edges, then calculate several parameters and return a table with the results.

Using the “Read and write excel” function in Fiji, an excel file is created containing the results. This function is built in such way that the results table for the next image in the file is added to the excel file without overwriting the preexisting data.

What am I doing wrong? Any suggestions?

Thanks a lot for any help!!

Below the code:

//@ File (label = "Input directory", style = "directory") input
//@ File (label = "Output directory", style = "directory") output
//@ String (label = "File suffix", value = ".tif") suffix

//-- Call the initial function to get a list of files

processFolder(input);
print("Done");

//-- FUNCTIONS ---
    function processFolder(input) {
    	list = getFileList(input);
    	list = Array.sort(list);
    	for (i = 0; i < list.length; i++) {
    		if(endsWith(list[i], suffix))
//-- Call the file processing function
    			processFile(input, output, list[i]);
    	}
    }

    function processFile(input, output, file) {
    print("Processing: " + input + File.separator + file);
    
    //-- Open the file
    open(input+File.separator+file);
    title=getTitle();

    //-- Your code here -------------------
    setAutoThreshold("Default");
    setThreshold(245, 255);
	
		// Shape Analysis Macro for ImageJ
		// Copyright of Liu, E.J., Cashman, K.V., & Rust, A.C., (2015). Optimising Shape Analysis to quantify volcanic ash morphology. GeoResJ

		// INPUTS: this macro requires a binary image.

		// OUTPUTS: Particle area, Particle perimeter, Convex hull (CH) area, Convex hull (CH) perimeter, Solidity, Convexity, 
		// Concavity Index, Form Factor, Major axis (of best fit ellipse), Minor axis (of best fit ellipse), Axial ratio (minor/major axis), 
		// Bounding box width (along x axis), Bounding box height (along y axis), Feret diameter, Minimum Feret diameter. All mathematical 
		// definitions given in Table 2 of Liu et al., (2015).

		// Default variables: particle size range = 750-Infinity pixels per particle; 'exclude' edge-touching particles; 
		// 'include' holes in the particle area.

		{
		run("Set Measurements...", "area centroid perimeter bounding fit shape feret's redirect=None decimal=3");
		run("Analyze Particles...", "size=750-Infinity pixel circularity=0.00-1.00 show=Outlines exclude clear include record stack");


			n = nResults;
			area1 = newArray(n);
			length1 = newArray(n);
			area2 = newArray(n);
			length2 = newArray(n);
			xstart = newArray(n);
			ystart = newArray(n);
			ff1 = newArray(n);
			AR1 = newArray(n);
			round1 = newArray(n);
			BBX1 = newArray(n);
			BBY1 = newArray(n);
			major1 = newArray(n);
			minor1 = newArray(n);
			feret1 = newArray(n);
			minferet1 = newArray(n);
			solidity1 = newArray(n);
			for (i=0; i<n; i++) {
			  area1[i] = getResult('Area', i);
			  length1[i] = getResult('Perim.', i);
			  xstart[i] = getResult('XStart', i);
			  ystart[i] = getResult('YStart', i);
			ff1[i] = getResult('Circ.', i);
			AR1[i] = getResult('Aspect', i);
			round1[i] = getResult('Round', i);
			BBX1[i] = getResult('Width', i);
			BBY1[i] = getResult('Height', i);
			minor1[i] = getResult('Minor', i);
			major1[i] = getResult('Major', i);
			feret1[i] = getResult('Feret', i);
			minferet1[i] = getResult('MinFeret', i);
			}
			run("Clear Results");
			for (i=0; i<n; i++) {
			  doWand(xstart[i], ystart[i]);
			  run("Convex Hull");
			  run("Measure");
			  area2[i] = getResult('Area', i);
			  length2[i] = getResult('Perim.', i);
			}
		run("Clear Results")
			run("Select None");
			for (i=0; i<n; i++) {
			  setResult("Area", i, area1[i]);
			  setResult("Perim.", i, length1[i]);
			  setResult("CH Area", i, area2[i]);
			  setResult("CH Perim.", i, length2[i]);
			  setResult("Solidity", i, area1[i]/area2[i]);
			  setResult("Convexity", i, length2[i]/length1[i]);
			  setResult("Concavity Index", i, sqrt((pow(1-(area1[i]/area2[i]),2)+(pow(1-(length2[i]/length1[i]),2)))));
			  setResult("FormFactor", i, ff1[i]);
			  setResult("Major axis", i, major1[i]);
			  setResult("Minor axis", i, minor1[i]);
			  setResult("Axial ratio", i, minor1[i]/major1[i]);
			  setResult("BX width", i, BBX1[i]);
			  setResult("BY height", i, BBY1[i]);
			  setResult("Feret d", i, feret1[i]);
			  setResult("MinFeret d", i, minferet1[i]);
			}
			 updateResults();
		  }
		  

    //-- Save out the results table here:
    selectWindow("Results");
    //saveAs("Results", output+File.separator+replace(file,suffix,"_results.csv"));
	run("Read and Write Excel");

    //-- Save out your drawing here
    selectWindow("Drawing of "+title);
    saveAs("TIFF",output+File.separator+replace(file,suffix,"_drawing.tif"));
	
	//-- Close everything before the next image is opened
    close("*");
    }

#5

I’m just trying to run through your code and before I get onto the error, a quick note:
In the following lines of code you don’t need both:

setAutoThreshold(“Default”);
setThreshold(245, 255);

Just the second line will do what you think you want (in fact if your inputs images are white/255 objects on black/0 background, you need setAutoThreshold("Default dark");). That said, you don’t even need this at all, as Analyze Particles will operate just fine on a binary image without a threshold (especially as you don’t have the “Limit To Threshold” option set on “Set measurements”)

Right, down to the actual error. I made a test binary image (from Blobs) and ran it (with the two lines above commented out). Your (fairly common) error is caused by missing a semicolon off a command. In this case, it’s the line:

run(“Clear Results”)

Add a semicolon to the end and the script runs. Ta Da!


I don’t have Read and Write excel installed to I went back to my line commented out above and you get a CSV like this:
blobs_001_results.zip (2.2 KB)

I dumped the version of the code I ran here just in case:
https://bitbucket.org/snippets/davemason/eaGrgX


#6

Thank you so much!!!

I Actually solved it just a second ago before receiving your message.

Thanks a lot anyway, especially for the un-needed part.

PP