Trouble applying recursive processes to specific folders

Hi,

I want to apply a set of processes on files that are in specific subfolders of a directory and I would like to do so recursively.

If my subdirectories in a directory (analysis) are as follows:

~analysis/condition1/DAPI
~analysis/condition1/GREEN
~analysis/condition2/DAPI
~analysis/condition2/GREEN

How can I recursively apply processes to ~/condition1/DAPI, and ~condition2/DAPI but not to ~/condition1/GREEN, and ~/condition2/GREEN?

This is what I figured would work but didn’t:

requires("1.33s"); 
   dir = getDirectory("Choose a Directory ");
   setBatchMode(true);
   count = 0;
   countFiles(dir);
   n = 0;
   processFiles(dir);
   //print(count+" files processed");
   
   function countFiles(dir) {
      list = getFileList(dir);
      for (i=0; i<list.length; i++) {
          if (endsWith(list[i], "/"))
              countFiles(""+dir+list[i]);
          else
              count++;
      }
  }
      
             
   function processFiles(dir) {
      list = getFileList(dir);
      for (i=0; i<list.length; i++) {
          if (endsWith(list[i], "/DAPI/"))
              processFiles(""+dir+list[i]);
          else {
             showProgress(n++, count);
             path = dir+list[i];
             processFile(path);
          }
      }
  }

  function processFile(path) {
       if (endsWith(path, ".tif")) {
           open(path);
           	setAutoThreshold("Otsu dark");
run("Threshold...");
setOption("BlackBackground", false);
run("Convert to Mask");
run("Fill Holes");
run("Despeckle");
           save(path);
           close();
      }
  }

Any help is greatly appreciated!

Hi,

What exactly is failing? Is if (endsWith(list[i], "/")) always false? Try using if(File.isDirectory(list[i])). This might be a Windows / Linux issue, because in the former the path separator is a forward slash (\).

Best Regards,
Richard Domander

I recommend adding a line

IJ.log("<enter function name here>" + dir)

at every first line of a function. In that way you can figure out, what exactly is not working. If you cannot proceed, the output of the program would be interesting for us to help you…

Presumably your input directory is ~/analysis, none of the subdirectories of which contain “DAPI”, so this code will try to processFile on each subdirectory (“condition1”, “condition2”), which don’t end with “.tif”, so the search ends with nothing happening.

This also is a valid point. In general string matching for the folder filtering will be more flexible than endsWith.

I would do something like this:

dir = getDirectory("Choose a Directory ");
filterDirectories(dir, ".*DAPI.*");

function filterDirectories(dir, pattern) {
    list = getFileList(dir);
    for (i=0; i<list.length; i++) {
        child = dir+list[i];
        if (File.isDirectory(child)) {
            if (matches(child, pattern)) {
                // We found a directory of interest, so process it
                processFolder(child);
            }
            else {
                // Recursively check subdirectories
                filterDirectories(child, pattern);
            }
        }
    }
}

// process all .tif files in the given directory
function processFolder(dir) {
    list = getFileList(dir);
    // find the .tifs in this directory
    for (i=0; i<list.length; i++) {
        path = dir+list[i];
        if (endsWith(path, ".tif")) {
            // Do stuff here
        }
    }
}

For optimal reuse/headless operation, I would turn this into a Parameterized script, replacing the first two lines with:

// @File(style="directory", label="Choose a Directory") dir
// @String(label="Folder pattern") dirPattern
// @String(label="File extension") extension

filterDirectories(dir + File.separator, dirPattern);
...
1 Like

Hi hinerm!

Your script worked perfectly! Thank you!

I tried to use the Parameterized script but I don’t understand how to implement it. Are there any good resources you know of to learn how to do these things well? I’m very new to this whole script writing business.

Justin

Definitely. First of all, you need to be working with an ImageJ2 or Fiji installation.

The Scripting pages on the wiki are really the intended reference material for using the ImageJ2 script editor. Also, in the script editor itself are a number of illustrative Templates intended to demonstrate various tasks.

You can still use the ImageJ macro language when scripting; the benefit of adding @Parameters is that you’re telling ImageJ “I want to use these things in my script” and let it handle creating the user interface, instead of manually asking for directories or creating a GenericDialog.

I’m sorry… it occurred to me that I was not presenting parameters clearly and I should have just re-posted the whole script. Anyway, here is a parameterized version:

// @File(style="directory", label="Choose a Directory") dir
// @String(label="Folder pattern") dirPattern
// @String(label="File extension") extension

filterDirectories(dir + File.separator, dirPattern);

function filterDirectories(dir, pattern) {
    list = getFileList(dir);
    for (i=0; i<list.length; i++) {
        child = dir+list[i];
        if (File.isDirectory(child)) {
            if (matches(child, pattern)) {
                // We found a directory of interest, so process it
                processFolder(child);
            }
            else {
                // Recursively check subdirectories
                filterDirectories(child, pattern);
            }
        }
    }
}

// process all .tif files in the given directory
function processFolder(dir) {
    list = getFileList(dir);
    // find the .tifs in this directory
    for (i=0; i<list.length; i++) {
        path = dir+list[i];
        if (endsWith(path, extension)) {
            // Do stuff here
        }
    }
}

If you paste that into the script editor and set the language to “IJ1 Macro” it should work, with a dialog automatically created for you to set those parameters.

Let me know if you have any problems or questions!

2 Likes

Hi hinerm,

This was incredibly helpful! Thank you so much.

Justin

2 Likes