Export histogram values of all images in a stack to one excel file

Hello community,

I have a stack with about 1000 greyscale images, want to count the pixels per grey level on each image and export the values to excel. Currently, I try to do this with Analyze --> Histogram for each image separately.
Is there a way to build a makro which

  1. Opens the first image of the stack
  2. Runs the Histogram command and displays the values in a ‘Results’ window
  3. Exports the values in the ‘Results’ window to excel
  4. Closes the image
  5. Opens the next image in the stack
  6. … (Loop)

Instead of using a stack of images, I’d be fine with analyzing all images in one directory as well.
The values of each image should be exported to the same excel file (ideally one new column per image).

That’s my try on this:

PATH = "/Users/XYZ/Stack/";
list = getFileList(PATH);
for (i=0; i<list.length; i++) {
	open(PATH+list[i]);
	run("8-bit");

	nBins = 256;
	run("Clear Results");
	row = 0;
	getHistogram(values, counts, nBins);
	run("Histogram");
	setOption("ShowRowNumbers", false);

	for (i=0; i<nBins; i++) {
		setResult("Value", i, values[i]);
		setResult("Count", i, counts[i]);
		row++;
	}
	updateResults();
	saveAs("Results", PATH+list[i]+".xls");
	run("Close");
}

Unfortunately, when running my makro, it opens all images and performs the Histogram command for each one, but neither closes an image after completing the command nor exports all values to one excel file (apperently, it exports the values of just one image).

Do you have any suggestions?

Thank you!

Hi derjazzer,

I may have misunderstood you a little, especially in relation to whether or not you are working with a stack of images (i.e. many images occupying different slices of a single file) or if you are instead processing many single-slice images within one workflow. Based on your macro, I chose to interpret the latter scenario and have come up with the following potential solution:

run("Input/Output...", "jpeg=85 gif=-1 file=.csv use_file copy_row save_column");

PATH = "/Users/XYZ/Stack/";
//PATH = "C:/Users/XYZ/Stack/"; //consider this version with the drive letter (e.g. 'C:') as a prefix
list = getFileList(PATH);
nBins = 256;

//check for an existing excel file and open it as a results table, ready to append data to it
if (File.exists(PATH+"excel file.csv")==true){
	open(PATH+"excel file.csv");
	Table.showRowNumbers(false);
	updateResults();
} else{
	for (x=0; x<nBins; x++) {
		setResult("Value", x, x);
	}
}

for (i=0; i<list.length; i++) {
	open(PATH+list[i]);
	run("8-bit");
/**
	for (s = 1; s<=nSlices; s++){
		setSlice(s);
		// PUT CODE HERE if you really are extracting data from each slice of an image stack
	}
*/
	getHistogram(values, counts, nBins);
	imageNum = i+1;
	Table.setColumn("Count for image "+imageNum, counts);
	updateResults(); //probably faster to do this outside of the loop but the table will not save outside of the loop for some reason
	Table.save(PATH+"excel file.csv"); //again, I should be saving outside of the loop, but the table is temporary (as if a nested variable!?)
	selectWindow(list[i]);
	run("Close");
}

/** Would have preferred to update and save the table outside of the loop, as below, but the table reverts to the pre-loop state
updateResults();
Table.save(PATH+"excel file.csv");
*/

selectWindow("Results");
run("Close");

Based on your macro, it made little sense to repeatedly open and close an existing .csv file, as you had already hard-coded your file path as a constant variable. Therefore, all data is written to a single appended results table, with columns added for each image export. Ideally, this table would have been updated and written to file only once outside of the loop. This implementation works when an existing .csv file is not opened at the beginning. However, when imagej does import data from an existing file into a new data table, it for some reason does not like to update and write this data table outside of the work loop (!?).
Regardless, the posted code seems to work when both 1) processing images from one folder without there being an existing .csv file to update and 2) opening an existing .csv file and updating it with data from a set of images.

NOTE: in the second case, I have left it up to you to assign new column names (so that old ones are not overwritten). At the moment, column naming proceeds as ‘Count for image’ 1, 2, 3, 4 etc, but you can easily change this to the name of the ‘unique’ processed image or some other changing variable.

2nd NOTE: Again, the macro will work best when applied to a single folder of images, without opening an existing .csv file in that folder, but if you do update an existing .csv file also be aware that imagej, for some other unkonwn reason to me (perhaps to do with the results table not being a true results table at this point), will let you know that there are no open images to close and despite the macro not requesting the closure of any images. Regardless, the .csv file will be updated correctly.

3rd NOTE: if you do actually want to process individual slices from image stacks, I have included some code within a comment block for you to use, that should get you started in that direction.

Hope this helps,
Kind regards.

2 Likes

EDIT: I opened the CSV-File the wrong way. Your macro works perfectly fine.

THANK YOU SO MUCH!

Hi,
Your code works for me as well! Thank you for sharing. I am a total newb to macro coding (coding in general, to be honest) and I was wondering how to change the code so that each column is labeled with the image name. You mention it under your first NOTE, but I don’t know how to change that. I tried getTitle(), but maybe I didn’t place it in the correct location?

NEVERMIND, I FIGURED IT OUT!! :slight_smile:

2 Likes

Hi @roseywhiting,

Glad you figured it out! Maybe, to help future readers of this thread, you could consider sharing how you solved it? :slight_smile:

BTW: To format code in a post, enclose it in “code fences”: Put ```javascript on the line before your code chunk (that’s 3 backticks) and another 3 backticks on the line after the code. More details here: How to put code in a post?

Oh yes! I will do that. I changed the code here, where it has +imageNum:

	getHistogram(values, counts, nBins);
	imageNum = i+1;
	Table.setColumn("Count for image "+imageNum, counts);

to

	getHistogram(values, counts, nBins);
	imageNum = i+1;
	Table.setColumn("Count for image "+getTitle(), counts);

This renamed each column as the image file name, for example: “Count for image 1145b”
Originally I made the mistake of deleting “count for image” and replacing it with getTitle() which was incorrect. (Thanks @tswayne for teaching me how to post code!)

2 Likes