Need a macro to eliminate black slices in a stack

I need help writing a macro on Fiji to eliminate black slices in a stack.

I have in a folder a series of stacks 1,2,3, etc (about 60 depending on the manipulation) each of which is composed of 3 colors (Blue, Green, Red) and a number X of slices (about 90).

I would like to make a macro that, for each stack, removes slices whose average green channel intensity is less than 1 and gives me a new corrected stack.

The macro must therefore:
-Call stack 1 in the selected folder
-Browse the slices
-Calculate the intensity of the green channel (channel 2) in the foreground
-If “mean” is less than 1 (the image is almost black): delete the plane (so the Blue+Green+Red images from this plane)
-If “mean” is greater than 1: move on to the next slice
-Save the corrected stack in the “Correction” folder under a new name “Stack-1-correct”.
-Go to the next stack until the last one.

Thank you in advance for your help! I would be very grateful if you could :slight_smile: !


Hello Marie-Julie -

The most useful documentation for writing macros in the IJ Macro
(IJM) language is this list of IJM functions:

Built-in Macro Functions

There is also the ImageJ Macro Language reference, as well as a
number of tutorials, such as Introduction into Macro Programming.

(Note, if you are already familiar with python or one of the other
scripting language supported by Fiji / ImageJ, such as groovy,
javascript, etc., then you might prefer to use that, instead of IJM.)

The macro Recorder, Plugins > Macros > Record... is a
very useful tool for (help in) writing IJM macros.

To get you started, here is a complete, runnable IJM macro that
shows some ways to access slices in a stack. If you uncomment
the last three lines it will delete the first slice (that happens to be
all gray) from the stack.

// make an RGB stack ...
newImage ("sample", "8-bit ramp", 256, 256, 5);
run ("Stack to Images");
selectWindow ("sample-0001");
run ("RGB Color");
selectWindow ("sample-0002");
run ("Fire");
run ("RGB Color");
selectWindow ("sample-0003");
run ("Red");
run ("RGB Color");
selectWindow ("sample-0004");
run ("Green");
run ("RGB Color");
selectWindow ("sample-0005");
run ("Blue");
run ("RGB Color");
run ("Images to Stack", "name=sample-RGB-stack");

// now we have an RGB stack ...

// we can manipulate it
// for example, select a particular slice ...
print ("nSlices = ", nSlices);
setSlice (2);
Stack.getPosition (channel, slice, frame);
print ("slice = ", slice);

// uncomment to delete the gray (first) slice
// setSlice (1);
// run ("Delete Slice");
// print ("nSlices = ", nSlices);

For this part, you will want to look at the setRGBWeights() IJM
function, and at the getStatistics() function, or use IJM’s run()
function to call the "Measure" command (that you can call from
the gui with Analyze > Measure).

Why don’t you try writing your own macro that does part of the
problem – maybe looping over the stacks in your folder, or just
analyzing one stack – and see how far you get.

I’m sure if you post what you’ve tried with any output and / or
errors, together with some sample images, people here will
be able to give you further assistance.

Thanks, mm

Thank you for your answer.
Of course, I’m tried something. Here is the macro I have done and with which I am working at the moment.

// “BatchProcessFolders”
// This macro batch processes all the files in a folder and any
// subfolders in that folder. In this example, it runs the Subtract
// Background command of TIFF files. For other kinds of processing,
// edit the processFile() function at the end of this macro.

dir = getDirectory(“Choose a Directory “);
count = 0;
n = 0;
//print(count+” files processed”);

function countFiles(dir) {
list = getFileList(dir);
for (i=0; i<list.length; i++) {
if (endsWith(list[i], “/”))

function processFiles(dir) {
list = getFileList(dir);
for (i=0; i<list.length; i++) {
if (endsWith(list[i], “/”))
else {
showProgress(n++, count);
path = dir+list[i];

function processFile(path) {
if (endsWith(path, “.env”)) {

// Importer le stack directement
run(“Bio-Formats Windowless Importer”, “open=path autoscale color_mode=Default view=Hyperstack”);

//transformer l’image en 8-bit

//Remettre l’image à l’echelle avec la fonction ci dessous :
// run(“Scale…”, “x=1.0 y=0.42236328 z=1.0 width=865 height=865 depth=71 interpolation=None average create”);

//Modifier l’ordre des images pour pouvoir appliquer le plugin 3D drift
run(“Re-order Hyperstack …”, “channels=[Channels ©] slices=[Frames (t)] frames=[Slices (z)]”);

//Remettre les canaux dans les bonnes couleurs
run(“Enhance Contrast”,“saturated=0.35”);
run(“Enhance Contrast”,“saturated=0.35”);
run(“Enhance Contrast”,“saturated=0.35”);

//Supprimer les frames qui n’ont pas de datas correctes
//car le piezo fait une image au milieu du stack à la fin de l’acquisition
//car les premières et dernières images sont moins intenses que celles au centre du stack
run(“Delete Slice”,“delete=frame”);
run(“Delete Slice”,“delete=frame”);
run(“Delete Slice”,“delete=frame”);

//Corriger le drift dû aux battements du coeur avec le channel 2 (vert) pour référence
run(“Correct 3D drift”, “channel=2 only=0 lowest=1 highest=1”);

//Sauver l’image tif dans le fichier parent
run(“Close All”);

My problem is that on my 90 stacks: some have the first black slices, others the last, sometimes only one, sometimes six or seven. So with my current macro it happens that I delete data or on the contrary that I leave black slices which gives me a bad shift later.
So I tend to eliminate more than enough and I would like to succeed in writing a macro that allows me to obtain a clean and repeatable result on each manipulation.

Hello Marie-Julie -

I’m not entirely sure what you are asking here. (If you have
specific questions about parts of the macro you posted, please
ask. Let us know what some specific piece of code did, and
what you would have liked it to do.)

I do have one comment:

I do see your macro deletes some slices (frames, to be precise),
and you seem to asking about how to delete the right slices and
the right number of slices.

One possible issue – I don’t know whether it’s relevant – is when
you delete slices from a stack, the remaining slices have their
slice numbers (indices) changed. A common way to deal with
this sort of thing is to process your slices in reverse order, that
is, from largest slice number to smallest. Then, as you delete
slices, the slice numbers of the yet unprocessed slices won’t
be changing out from under you.

Here is a complete, runnable IJM macro that illustrates this:

// create a (hyper) stack with labelled slices
newImage("HyperStack", "8-bit grayscale-mode label", 256, 256, 1, 10, 1);

print (nSlices);

// loop over the slices in reverse order
// deleting some based on desired criterion
for (i = nSlices; i > 0; i--) {
	setSlice (i);
	// evaluate deletion criterion
	doDelete = (i == 3 || i == 6 || i == 7);
	if (doDelete)  run ("Delete Slice");	

print (nSlices);

In this sample macro I just say by fiat that slices 3, 6, and 7
should be deleted. But in your real case (in place of the comment
// evaluate deletion criterion) you would presumably
evaluate your green threshold test, and delete the slice if it were
“almost black.”

Again, I don’t know whether this particular issue has caused
problems for you, but it might be something to look out for.

Thanks, mm