Automate stack splitter frame numbers

I have written a macro in ImageJ to automate stack splitter from an array of .tiff files which runs successfully, but there’s a catch. I have to press enter everytime the stack splitter runs to specify the number of frames to split into. I do not want this and rather want to split all frames irrespective of how many are there. Is there a way to do so?

Thanks!

Hello @Ajax567,

The command that you issue, is probably not specific enough, so a dialog kicks in to get the remaining information. Please post your code to let us help you in resolving the issue.

Hi eljonco,

Here is the code:

//BELOW IS A LIST OF HARDCODED STUFF
//FILENAMES
//ITER NUMBERS
//FRAME NUMBERS

dir1=getDirectory("Choose source directory");
createdir=dir1+"Split_folder\\";
File.makeDirectory(createdir);
//dir2=getDirectory("Choose destination directory");

filenames=newArray(20);
tt=1;
for (i=0; i<filenames.length; i++) {
	str1="F";
	str2="_pmtUG.tiff";
	filenames[i]=str1+tt+str2;
	tt=tt+1;
	//print(filenames[i]);
}

setBatchMode(true);
for (j=0; j<filenames.length; j++) {
	//showProgress(j+1, filenames.length);
	open(dir1+filenames[j]);
	run("Stack Splitter");

	subdir=createdir+filenames[j]+"\\";
	File.makeDirectory(subdir);
    
	for (k=1; k<19; k++) {
		saveAs("Tiff",subdir+getTitle);
		close();
	}
	close();
}

There are 20 files and each file has 18 frames that I want to split.

Hi @Ajax567,
You can read the file names from the directory with getFileList(directory), so you don’t need to generate the filenames yourself. Using the backslash as directory separator makes it harder to run your code on another platform, the usual method is File.separator, which adapts itself to the platform it is running on.

Stack Splitter is a plugin, which can probably do more that you seem to use it for. I think (hope?) Image > Stacks > Stack to Images is an alternative (it is a native ImageJ command) suited for your purpose. After opening a stack, the stack is split into its individual frames as separate images. You then can continue to run the rest of your macro.
This macro below generates a stack, changes it to separate windows, selects each window, prints its name (which you want to change into saving the image). Finally, it then closes the images.

newImage("Untitled", "8-bit white", 500, 500, 6);
myNSlices=nSlices();
run("Stack to Images");

for(imageNo = 1;imageNo <=myNSlices; imageNo++){
	selectImage(imageNo);
	print(getTitle);
}
close("*");

1 Like

Hi eljonco,

Stack to images is the exact thing I need, thanks for that!

I did not get how to use File.separator, can you please show me?

Also, getFileList(directory) works too but there’s a catch, I have 20 files with the names F1_pmtUG.tiff, F2_pmtUG.tiff, … , F20_pmtUG.tiff, and when I write this code

filelist=getFileList(dir1); print(filelist[1]);

I get F10_pmtUG.tiff as the first result, hence I need to sort these files by their number. Any help with that?

File.separator is a function that returns the separator for the platform you use. On windows it returns , on mac it returns /

If you have the filelist variable to hold the entries, you can try Array.sort(filelist) to sort them. Maybe you are ‘picky’ about sort order; does 2 come before or after 10?
Just check.

When you have exactly 20 files every time, your initial design might be easier, when the numbers of files vary, you might want to load all files up front and do a sort of some kind.
Splitting up the name using a regular expression can relatively simply split out the number. Array sorting is very handy, there is a function that returns an array which contains pointers as to where you will find the sorted item.

when the numbers of files vary, you might want to load all files up front and do a sort of some kind

This is absolutely true, I indeed want to make the code more flexible that way. I’ll try and let you know.

Edit: After using the Array.sort(filename) function, it seems that for 1,2,…,10, 10 comes before 2.

Also regarding getFileList, I have files with extensions .txt and .tiff, how to specifically make a list of the .tiff files?

Lest another device/application is dictating enumeration, you can use IJ.pad() to create numbers 01,02, …09, 10,… 99 so alphabetically and numerically both have the same sort order.

When forced to use 1…9,10…19,20 etc. yet wanting to use the array sorting, you can generate an array of the enumeration part of the file name, then use Array.rankPositions(array) to find the proper order in the filenames array.

Regarding txt and tiff, you can use endsWith(filenames[j], "tiff) and ignore if false.

1 Like

Everything worked out just perfectly! Thanks a ton! :grin:

Also is there any way for the program to detect how many frames there are in a stack?

nSlices()
Returns the number of images in the current stack. Returns 1 if the current image is not a stack. The parentheses “()” are optional. See also: getSliceNumber, getDimensions.

getDimensions(width, height, channels, slices, frames)
Returns the dimensions of the current image.

The manual; a wealth of information :sunglasses:

1 Like

You rock! Cannot thank you enough for your help! My code is now shorter and nothing is hard coded.

Final code here:


dir1=getDirectory("Choose source directory");
list=getFileList(dir1);
filenames=newArray(list.length/2);
createdir=dir1+"Split_folder"+File.separator;
File.makeDirectory(createdir);
tt=0;

for (i=0; i<list.length; i++) {
	h=endsWith(list[i],"tiff");
	if (h==1) {
		filenames[tt]=list[i];
		setBatchMode(true);
		open(dir1+filenames[tt]);
		frames=nSlices;
		run("Stack to Images");

		subdir=createdir+filenames[tt]+File.separator;
		File.makeDirectory(subdir);
    
		for (k=0; k<frames; k++) {
			saveAs("Tiff",subdir+getTitle);
			close();
			}
		tt=tt+1;
	}		
}

As you asked for the working of Array.rankPositions(array), I made a macro that does the sorting for you.

First, the macro creates a test folder in your temp directory, in there, it generates 24 files with the pattern you use and the number 1…24 hidden in the file name. Then it forgets what it did, opens the same directory and reads all file names.

The file names are then inspected for the pattern F<digits>_pmtUG.tiff, the macro extracts the digits, stores the digits in an array in a way that sorting is working properly (1,…9,10,…19,20,…) is stored as (001,…009,010,…019,020,…), then creates an array that has the file names in order of the sorted one. So fileNames[0] points to the file where <digits> is 1, in fileNames[1] <digits> equals 2, etc. up until 24.
Actually, the digits not even need to be 1,…9, 10,…19 consecutively, you can skip numbers.

The macro can be made much more compact, but the current version shows you how your variables actually behave if you use ImageJ1’s Debug feature.

create ranked file list from files in a folder
close("*");//close any open image to prevent confusion
tmpDir=getDirectory("temp");//find the system's local tmp folder
imgFolder=tmpDir + "inFolder"+File.separator;//create a path name for a folder for our test
File.makeDirectory(imgFolder);//create the actual folder
newImage("_pmtUG", "8-bit white", 500, 500, 1);//sample image
//File.delete(path);
for(i=1;i<25;i++){//create 24 files
	outFn=imgFolder+"F"+i+"_pmtUG.tif";//with the number hidden within the name
	saveAs("Tiff", outFn);//create the actual tiff file
}

fileNames=getFileList(imgFolder);//get a list of files in our test folder
numbersArray = newArray(fileNames.length);

for(i = 0; i< fileNames.length; i++){
	testString=fileNames[i];
	delimiters = "([0-9]+)";
	chunks=split(testString,delimiters);
	if(chunks.length == 2){
		lead=chunks[0];
		trailer=chunks[1];
		numStart=lead.length;
		numEnd=indexOf(testString,trailer);
		number = substring(testString,numStart,numEnd);
		paddedNumber=IJ.pad(number,3);
		numbersArray[i]=paddedNumber;
		//test1String=lead + paddedNumber + trailer;
		print (testString + " -> " + number);
	}else{
		print("file name "+testString+" does not seem to have a numer in it");
	}
}
Array.show(numbersArray);
dupArray = Array.slice(numbersArray,0,numbersArray.length);
Array.show(dupArray);
Array.sort(dupArray);
Array.show(dupArray);
rankArray= Array.rankPositions(numbersArray);
Array.show(rankArray);
sortedFileNames=newArray(fileNames.length);
for(i=0;i<fileNames.length;i++){
	sortedFileNames[i]=fileNames[rankArray[i]];
}
Array.show(sortedFileNames);
1 Like