Combining several "Summary of [image title]" into a single results table

Hi ImageJ users!

I am running the following mini-macro:

macro "Cell_quant [q]"{
	run("8-bit");
    roiManager("measure")
	setAutoThreshold("Default dark");    
	setThreshold(50, 255);
	run("Analyze Particles...", "size=10-1000 show=Nothing summarize");
}

I am used to batch processing Analyze Particles, so that all of the summarized results are loaded into a window called “Summary”. Once my batch processing is completed, I can simply save the “Summary” window into a single Excel file.

I am now running Analyze Particles on images that cannot be batch processed. Every “Analyze Particles” action produces a unique window called “Summary of [image title]” that contains only a single row of data. This opens several useless windows… I do not want to save a single result to hundreds of separate new files!

Can anyone help me combine these results into a single table? (Or, if anyone is aware of a way to stop the “Summary” window from being renamed "Summary of [image title], that would be phenomenal!)
I’ve also played around with a few programs that try to add the “Summary” data to the “Results” window, but all of those either clear the results window first or rewrite the first row of the results window instead of appending to the end…

I am thinking something like the following, but can’t find any examples to make this work:

[create Results Window "Analysis"]
run("Analyze Particles...", "size=10-1000 show=Nothing summarize");
IJ.renameResults("single_result");      //This renames the window "Summary of [image title]"
selectWindow("Analysis");
[Add "single_result" to "Analysis"]
selectWindow("single_result");
run("Close");

Thank you very much!

Hi Mariemaia,

My ‘Analyze Particles’ summary table is always just called ‘Summary’ and is appended without issue. Regardless, I think this macro should do what you specifically requested:

macro "Cell_quant [q]"{
	run("8-bit");
	roiManager("measure");
	setAutoThreshold("Default dark");
	setThreshold(50, 255);
	run("Analyze Particles...", "size=10-1000 show=Nothing summarize");
	tableTitle = Table.title;
	Table.rename(tableTitle, "Results"); //can only get text from this table if it is a Results table
	headings = Table.headings;
	headingsArray = split(headings, "\t");
	if (isOpen("Analysis")==false) {
		Table.create("Analysis");
	}
	selectWindow("Analysis");
	size = Table.size;
	for (i=0; i<headingsArray.length; i++){
		//selectWindow(""+tableTitle);
		//data = Table.get(headingsArray[i], 0);
		data = getResultString(headingsArray[i], 0);
		selectWindow("Analysis");
		Table.set(headingsArray[i], size, data);
		Table.update;
	}
	
	//close(""+tableTitle);
	close("Results");
}

Minor point: The only way I could guarantee that any text/string data is faithfully copied when needed was to do some funny table renaming in conjuntion with the ‘getResultString’ command. I don’t think this will affect the final export, but it does cause an occasional minor hang-up (noticible macro pause) in my imageJ. Hopefully this is no big problem for you.

Again, I didn’t fully understand your issue, as my Summary table is populated and named appropriately in every instance I normally use it, but hopefully the above macro catches whatever fringe case you have encountered.

Kind regards.

Thank you very much! This solution ended up working great!

Hello Antinos,

I tried using your script to append several results(text file) into a single table and save each group of results in their respective subfolders. The problem I get is it saves all results files of all folders in one table, instead of a table for each folder.
input = getDirectory(“Input directory”);
//suffix = “ch2_measurements.txt”;
Area_path = input + “/6_Summarize/”;
File.isDirectory(Area_path);
processFolder(Area_path);
processFolder(input);
function processFolder(Area_path) {
list = getFileList(Area_path);
suffix = “summary.txt”; //suffix = “.lsm”; //you only want to apply
for (i = 0; i < list.length; i++) {
if(File.isDirectory(Area_path + File.separator + list[i]))
processFolder(Area_path + File.separator + list[i]);
if(endsWith(list[i], suffix))
processFile(Area_path, list[i]);
}
}

function processFile(Area_path, file) {

title1 = File.getName(Area_path+list[i]);
		//print(i);
		//print(title1);
		new_path = replace(Area_path, "\\" , "/");
		//print(new_path);
		total_path = new_path+list[i];
		run("Results... ", "open=[total_path]");
		//print(total_path);

Table.deleteColumn("AR");
Table.deleteColumn("Round");
Table.deleteColumn("Solidity");

		
tableTitle = Table.title;
Table.rename(tableTitle, "Results"); //can only get text from this table if it is a Results table
headings = Table.headings;
headingsArray = split(headings, "\t");
if (isOpen("Analysis")==false) {
	Table.create("Analysis");
}
selectWindow("Analysis");
size = Table.size;
for (i=0; i<headingsArray.length; i++){
	//selectWindow(""+tableTitle);
	//data = Table.get(headingsArray[i], 0);
	data = getResultString(headingsArray[i], 0);
	selectWindow("Analysis");
	Table.set(headingsArray[i], size, data);
	Table.update;

}	

//title=“stop”;
//waitForUser(title);

//close(""+tableTitle);
close("Results");

}
selectWindow(“Analysis”);
Table.deleteColumn(“X”);
Table.deleteColumn(“Y”);
//Table.deleteColumn(1);
Table.update;

selectWindow(“Analysis”);
Ana_title= “Analysis.csv”;
//Area_path = input + “/6_Summarize/”;
//File.makeDirectory(Area_path);
saveAs(“Measurements”, input + Ana_title);
//save(input);

run(“Close”);

Thank you

Hi @DavidBNorm,

Sorry for the delayed reply. I’ve attempted to solve your issue, but these problems are always tricky to solve without testing with your files and folder structure. Also, please note that the solution I offered for OP was specific to their situation. None-the-less, give this a go and see how it works for you:

input = getDirectory("Input directory");
//suffix = “ch2_measurements.txt”;
Area_path_x = input + "/6_Summarize/"; //this will get replaced so could be set to any string. I have left your line in. Note: your slahes are Mac specific here.
//File.isDirectory(Area_path); //not sure why this is here, as you don't save or use the output
//processFolder(Area_path); //we are already passing through all sub-folders with the below, so I am not sure why this is called separately.
processFolder(input); 

function processFolder(Area_path) {
		subFolderCount = 0;
		list = getFileList(Area_path);
		suffix = "summary.txt"; //suffix = “.lsm”; //you only want to apply
		for (i = 0; i < list.length; i++) {
			if(File.isDirectory(Area_path + File.separator + list[i])) {
				subFolderCount++;
				//processFolder(Area_path + File.separator + list[i]);
			}
			if(endsWith(list[i], suffix))
				processFile(Area_path, list[i]);
		}
		if (isOpen("Analysis")==true) {
			selectWindow("Analysis");
			Table.deleteColumn("X");
			Table.deleteColumn("Y");
			//Table.deleteColumn(1);
			Table.update;
			
			selectWindow("Analysis");
			Ana_title= "Analysis.csv";
			//Area_path = input + "/6_Summarize/";
			//File.makeDirectory(Area_path);
			//saveAs("Measurements", input + Ana_title);
			saveAs("Results", Area_path + Ana_title); //in addition to changing the save location, I am not familiar with the "Measurements" variable, so changed to "Results".

			Table.create("Analysis"); //reset table
		}
		if (subFolderCount>0){
			subFolderArray = newArray(subFolderCount);
			subFolderCount = 0;
			for (j = 0; j < list.length; j++) {
				if(File.isDirectory(Area_path + File.separator + list[j])) {
					subFolderArray[subFolderCount] = Area_path + File.separator + list[j];
					subFolderCount++;
				}
			}
			for (k = 0; k < subFolderArray.length; k++) {
				Area_path_x = subFolderArray[k];
				processFolder(Area_path_x);
			}
			//subFolderArray = null;
		}
}

function processFile(Area_path, file) {
		title1 = File.getName(Area_path+list[i]);
		//print(i);
		//print(title1);
		new_path = replace(Area_path, "\\" , "/");
		//print(new_path);
		total_path = new_path+list[i];
		//run("Results... ", "open=[total_path]"); //doesn't look right
			run("Results... ", "open=["+total_path+"]");
		//print(total_path);

		Table.deleteColumn("AR");
		Table.deleteColumn("Round");
		Table.deleteColumn("Solidity");

				
		tableTitle = Table.title;
		Table.rename(tableTitle, "Results"); //can only get text from this table if it is a Results table
		headings = Table.headings;
		headingsArray = split(headings, "\t");
		if (isOpen("Analysis")==false) {
			Table.create("Analysis");
		}
		selectWindow("Analysis");
		size = Table.size;
		for (i=0; i<headingsArray.length; i++){
			//selectWindow(""+tableTitle);
			//data = Table.get(headingsArray[i], 0);
			data = getResultString(headingsArray[i], 0);	//only catches the top row of data, which is okay for saved summary files that contain only 1 row.
			selectWindow("Analysis");
			Table.set(headingsArray[i], size, data);
			Table.update;
		
		}

		close("Results");
}

run("Close");

It’s probably way more convoluted than it needs to be but I tried to keep as much of your code as I could. I also don’t know what you are closing at the very end (but I left the command in anyway). Regardless of my exact macro, you’ll hopefully grasp the principal, which was to move the ‘Analysis’ table save from the processFile code-block to the processFolder function. I now expect the ‘Analysis’ table to be updated with the summary information of all files within a folder before it is saved to that folder. I prefer to test the outcome myself before posting here, but I didn’t spend the time to try to mimic your folder and file structure myself.

It also wasn’t clear to me if you wanted every sub-folder analysed separately. If it works, this macro should generate a separate results table for each sub-folder, in which it will also be saved each time.

Fingers crossed.

Hi Antinos,

I have had the same issue working with 3-channel MaxIP .nd2 files. To clarify, it seems that the ‘Analyze Particles’ summary table is called ‘Summary’ when produced from a single channel image, but when it is produced from a multichannel image or stack, it is called ‘Summary of [image title].’

I found your solution to work well analyzing the particles of C1, except for that the final ‘Analysis’ table renamed each image as ‘1.’ If there is a way to keep the original file name that would help. Thanks!

image

Hi @smith6jt ,
Thanks for the education! I totally missed that the original poster may have encountered this issue in relation to stack or multichannel images. I would have designed the macro differently to capture all slices. I designed the ‘Jul 10’ macro to extract the top-most line of the generated summary window, as I envisaged it only containing 1 line, as per the case for single-slice analyze particles summary tables. The whole line is copied verbatim, including the contents of the ‘slice’ column, which for the top-row will be ‘1’.
I can see how you may have adapted my macro to capture channel-1 summary info, as this is likely also at the top of the generated summary window. I’m only surprised the method worked for OP!
In your case, and if you are happy to proceed with the macro as it currently generally works, then you will want to inject the more meaningful description into the bottom-most ‘slice’ cell on each iteration. If you are using the ‘Jul 10’ macro, you can try adding this line after ‘Table.set(headingsArray[i], size, data)’:

Table.set("Slice", (size-1), tableTitle); //EDIT, this is not right, see below.

It’s a bit of a hack, but may work for you. If the ‘tableTitle’ variable isn’t appropriate, you can put any string you want there.

Kind regards.

@smith6jt
Apologies, but I noticed an error when I just came back to check on this post. I’m pretty certain the added line should read:

Table.set("Slice", size, tableTitle);

Regards.

Hi Antinos,

I realized that my original post included the file name on line 1 due to an error on my part. I changed the image to show that all entries under ‘Slice’ are ‘1’ under those conditions and edited the text of the post accordingly. Perhaps this is why you originally suggested ‘(size-1)’? I apologize for the confusion.

Adding ‘Table.set(“Slice”, size, tableTitle);’ worked brilliantly. Thank you for your help!

I’m glad it worked. No, the ‘size-1’ suggestion was purely my error (it’s similar to normal practice for updating the bottom most-row and I misread how I was passing the table size variable… which are all not very important details). Your original post was very clear!
Best wishes.