programmatically opening a stack from a list of filenames

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

programmatically opening a stack from a list of filenames

Ben.BigHair
Hello,

I keep fooling myself into thinking that there exists a simple interface
in the ImagePlus or ImageStack classes to open a series of images in a
stack by providing the filenames. I can't decide if I suffer from (a) a
combination of wishful thinking and laziness or (b) a muddled concept of
the relationships among ImageStack, ImagePlus and ImageProcessor.*  In
any event, when I roll my own I always feel like I am doing things
awkwardly, and seek to get some pointers on how to  program this more
elegantly.

The code snippet (shown at very bottom of message) works, but it seems
like a lot of steps.  Here are the pseudo-code steps shown in the code.

- open the first image in the list as an ImagePlus
- define an ImageStack based upon the first images dimensions
- add the first image's ImageProcessor to the ImageStack
- add each of the remaining files to the ImageStack
- create a new ImagePlus from the ImageStack


Have I made this unnecessarily complex?


Thanks,
Ben

* or I suffer from both (a) and (b)!



///BEGIN

private ImagePlus openImagesAsStack(String name, String[] fnames){

     //use Opener to read images into ImagePlus objects
     //read the first image in the list and use it's dimensions to define
     // the ImageStack object dimensions
     Opener opener = new Opener();
     ImagePlus imp = opener.openImage(fnames[0]);
     ImageStack stack = new ImageStack(imp.getWidth(), imp.getHeight());

     //add the first slice
     File file = new File(fnames[0]);
     stack.addSlice(file.name(), imp.getProcessor();

     //step through the remaining files and
     //add addtional slices by adding ImageProcessor objects
     if (fnames.length > 1) {
         for (int i = 1; i< fnames.length; i++){
             stack.addSlice(fnames[i], (opener.openImage(
fnames[i])).getProcessor());
         }
     }
     //create a new ImagePlus to contain the stack
     ImagePlus imp2 = new ImagePlus(name, stack);
     return imp2;
}

///END
Reply | Threaded
Open this post in threaded view
|

Re: programmatically opening a stack from a list of filenames

Albert Cardona
Ben,

What you have listed is generally correct (beware though your code has at least
one parenthesis missing at addSlice).

The easier way: extend ij.plugin.FolderOpener and override the sortFileList to
open the image file sequence like you need.

Even easier: assuming the sortFileList will work as is, just pass macro values
and execute the plugin.

You can see what options to pass if you open the Macro recorder and call the
File / Import / Image Sequence command.

    IJ.run("Image Sequence...", "open=/my/folder/anyfile.tif number=752
starting=1 increment=1 scale=100 file=experiment1");

If you set the folder only, through
Macro.setOptions("open=/my/folder/anyfile.tif");
then only the GenericDialog will open asking for input.
If any of the fields in the GenericDialog is not completed from an option from
the macro engine, then the dialog will show as well.

Albert

--------------------------------------------------------------------
This message was sent using Webmail@INI: https://webmail.ini.ethz.ch
Reply | Threaded
Open this post in threaded view
|

Re: programmatically opening a stack from a list of filenames

Ben.BigHair
Albert Cardona wrote:

> Ben,
>
> What you have listed is generally correct (beware though your code has at least
> one parenthesis missing at addSlice).
>
> The easier way: extend ij.plugin.FolderOpener and override the sortFileList to
> open the image file sequence like you need.
>
> Even easier: assuming the sortFileList will work as is, just pass macro values
> and execute the plugin.
>
> You can see what options to pass if you open the Macro recorder and call the
> File / Import / Image Sequence command.
>
>     IJ.run("Image Sequence...", "open=/my/folder/anyfile.tif number=752
> starting=1 increment=1 scale=100 file=experiment1");
>
> If you set the folder only, through
> Macro.setOptions("open=/my/folder/anyfile.tif");
> then only the GenericDialog will open asking for input.
> If any of the fields in the GenericDialog is not completed from an option from
> the macro engine, then the dialog will show as well.
>
> Albert
>

Hi Albert,

Thanks for the information. I'm glad I have it basically correct.  I'm
also happy to see the FolderOpener class which I hadn't noticed before.
Unfortunately, I need to open some but not all of the images within a
folder.  For example, I would like to open the first 11 in one stack and
the second 3 in the second stack. I have a metadata file from which I
can derive each listing of filenames so I need not search the folder.

016-095708_000001.tif
016-095708_000002.tif
016-095708_000003.tif
016-095708_000004.tif
016-095708_000005.tif
016-095708_000006.tif
016-095708_000007.tif
016-095708_000008.tif
016-095708_000009.tif
016-095708_000010.tif
016-095708_000011.tif
016-095708_cal_000001.tif
016-095708_cal_000002.tif
016-095708_cal_000003.tif


I guess I could play tricks with the "Image Sequence..." command using
some string matching.  That might be a whole lot easier.

My wishful thinking is to have an ImageStack constructor that looks
something like...

ImageStack(java.lang.String[] files)

from which I could get an instance of ImagePlus to show,

or

ImagePlus(java.lang.String[] files)

which would be very handy for sloths like me.  I see that, as you point
out, I could extend  ij.plugin.FolderOpener and override the run method
where the list is created (and sorted). My Java programming skills might
be too wobbly for me to tackle that just yet.

Thanks again,
Ben
Reply | Threaded
Open this post in threaded view
|

Re: programmatically opening a stack from a list of filenames

Albert Cardona
Ben

In the FolderOpener run method:

    list = sortFileList(list);

note how above the String[] list pointer is overwritten. Which means that in
overriding the sortFileList method you are free to resize it. Opening different
images from different folders would not be allowed. Opening different chunks of
different sequences existing within the same will, which is what you need as
far as I understand.

About the constructors:

>
> ImageStack(java.lang.String[] files)
>
> ImagePlus(java.lang.String[] files)


Well, don't stop yourself :) Just extend class ImagePlus and give it the
constructor you need, which is basically the pseudocode from your first email
with some width/height safety checks.

Albert



--------------------------------------------------------------------
This message was sent using Webmail@INI: https://webmail.ini.ethz.ch
Reply | Threaded
Open this post in threaded view
|

Re: programmatically opening a stack from a list of filenames

Ben.BigHair
Albert Cardona wrote:

> Ben
>
> In the FolderOpener run method:
>
>     list = sortFileList(list);
>
> note how above the String[] list pointer is overwritten. Which means that in
> overriding the sortFileList method you are free to resize it. Opening different
> images from different folders would not be allowed. Opening different chunks of
> different sequences existing within the same will, which is what you need as
> far as I understand.
>
> About the constructors:
>
>> ImageStack(java.lang.String[] files)
>>
>> ImagePlus(java.lang.String[] files)
>
>
> Well, don't stop yourself :) Just extend class ImagePlus and give it the
> constructor you need, which is basically the pseudocode from your first email
> with some width/height safety checks.
>
> Albert
>

Hi Albert,

This sounds like a good challenge to me. It would help me to make I sure
how I would access such a plugin. I want to pass the plugin (e.g.
"OpenMyListAsStack") a string, or a concatenation of strings, that lists
the files to open.  I don't want  to present the caller with any
dialogs. Is this where I would call...

IJ.runPlugin("OpenMyListAsStack", stringList)

from my other plugin?


Thanks again,
Ben
Reply | Threaded
Open this post in threaded view
|

Re: programmatically opening a stack from a list of filenames

Albert Cardona
Ben,

Perhaps best is:

public class StackPlus extends ImagePlus {
    public StackPlus(String concat_filenames) {
        super();
        String[] files = concat_filenames.split("\n");
        // open all files and place their processors in an ImageStack
        // ...
    }
}

Then just call, from your plugin:

public class My_Plugin implements PlugIn {
    public void run(String arg) {
        // obtain filenames from wherever
        // ...
        ImagePlus imp = new StackPlus(filenames);
        imp.show();
    }
}


The above assumed absolute paths where concatenated in a single String and
separated by newline characters.

Albert


--------------------------------------------------------------------
This message was sent using Webmail@INI: https://webmail.ini.ethz.ch
Reply | Threaded
Open this post in threaded view
|

Re: programmatically opening a stack from a list of filenames

Ben.BigHair
Albert Cardona wrote:

> Ben,
>
> Perhaps best is:
>
> public class StackPlus extends ImagePlus {
>     public StackPlus(String concat_filenames) {
>         super();
>         String[] files = concat_filenames.split("\n");
>         // open all files and place their processors in an ImageStack
>         // ...
>     }
> }
>
> Then just call, from your plugin:
>
> public class My_Plugin implements PlugIn {
>     public void run(String arg) {
>         // obtain filenames from wherever
>         // ...
>         ImagePlus imp = new StackPlus(filenames);
>         imp.show();
>     }
> }
>
>
> The above assumed absolute paths where concatenated in a single String and
> separated by newline characters.
>
> Albert

Oh! I didn't understand that it could be this easy. I will try this and,
as you cautioned earlier, make sure that the input image sizes are all
the same.  Pity I can't get to this until next week.  Nuts.

Thanks!
Ben

P.S.  I still have a muddled view of the interrelationships among
ImageStack, ImagePlus and ImageProcessor. I don't have enough experience
  reading UMLs so I can't decipher the UML for ImageJ. But that's
another day.
Reply | Threaded
Open this post in threaded view
|

Re: programmatically opening a stack from a list of filenames

ctrueden
In reply to this post by Ben.BigHair
Hi Ben,

> Thanks for the information. I'm glad I have it basically correct.  I'm
> also happy to see the FolderOpener class which I hadn't noticed before.
> Unfortunately, I need to open some but not all of the images within a
> folder.  For example, I would like to open the first 11 in one stack and
> the second 3 in the second stack. I have a metadata file from which I
> can derive each listing of filenames so I need not search the folder.
>
> 016-095708_000001.tif
> 016-095708_000002.tif
> 016-095708_000003.tif

Another option is to do this with the Bio-Formats Importer plugin.
Check the "Stitch files with similar names" box, then choose
016-095708_000001.tif. Hit OK, and the next dialog box will be a "file
pattern" indicating which files you want. In your case, you'll see
something like: "016-095708_0000<01-55>.tif" where the "55" is the
maximum numerical value in that block. You can change it to "11" and
hit OK, and you'll get 01-11. A similar procedure will work for your
other TIFF series.

The Bio-Formats filename numbering autodetection is really quite
smart. If you find a case where it fails, let me know and I can look
into it. But even if it does fail, you can type your own pattern in
manually to the file pattern dialog, to override any faulty guesses it
makes. If you put leading zeroes in the pattern, it assumes fixed with
numbering (so, <01-55> is fixed width), while no leading zeroes (e.g.,
<1-55>) indicates variable width.

-Curtis

On 4/2/07, Ben Tupper <[hidden email]> wrote:

> Albert Cardona wrote:
> > Ben,
> >
> > What you have listed is generally correct (beware though your code has at least
> > one parenthesis missing at addSlice).
> >
> > The easier way: extend ij.plugin.FolderOpener and override the sortFileList to
> > open the image file sequence like you need.
> >
> > Even easier: assuming the sortFileList will work as is, just pass macro values
> > and execute the plugin.
> >
> > You can see what options to pass if you open the Macro recorder and call the
> > File / Import / Image Sequence command.
> >
> >     IJ.run("Image Sequence...", "open=/my/folder/anyfile.tif number=752
> > starting=1 increment=1 scale=100 file=experiment1");
> >
> > If you set the folder only, through
> > Macro.setOptions("open=/my/folder/anyfile.tif");
> > then only the GenericDialog will open asking for input.
> > If any of the fields in the GenericDialog is not completed from an option from
> > the macro engine, then the dialog will show as well.
> >
> > Albert
> >
>
> Hi Albert,
>
> Thanks for the information. I'm glad I have it basically correct.  I'm
> also happy to see the FolderOpener class which I hadn't noticed before.
> Unfortunately, I need to open some but not all of the images within a
> folder.  For example, I would like to open the first 11 in one stack and
> the second 3 in the second stack. I have a metadata file from which I
> can derive each listing of filenames so I need not search the folder.
>
> 016-095708_000001.tif
> 016-095708_000002.tif
> 016-095708_000003.tif
> 016-095708_000004.tif
> 016-095708_000005.tif
> 016-095708_000006.tif
> 016-095708_000007.tif
> 016-095708_000008.tif
> 016-095708_000009.tif
> 016-095708_000010.tif
> 016-095708_000011.tif
> 016-095708_cal_000001.tif
> 016-095708_cal_000002.tif
> 016-095708_cal_000003.tif
>
>
> I guess I could play tricks with the "Image Sequence..." command using
> some string matching.  That might be a whole lot easier.
>
> My wishful thinking is to have an ImageStack constructor that looks
> something like...
>
> ImageStack(java.lang.String[] files)
>
> from which I could get an instance of ImagePlus to show,
>
> or
>
> ImagePlus(java.lang.String[] files)
>
> which would be very handy for sloths like me.  I see that, as you point
> out, I could extend  ij.plugin.FolderOpener and override the run method
> where the list is created (and sorted). My Java programming skills might
> be too wobbly for me to tackle that just yet.
>
> Thanks again,
> Ben
>
Reply | Threaded
Open this post in threaded view
|

Re: programmatically opening a stack from a list of filenames

Ben.BigHair
In reply to this post by Albert Cardona
Albert Cardona wrote:

> Ben,
>
> Perhaps best is:
>
> public class StackPlus extends ImagePlus {
>     public StackPlus(String concat_filenames) {
>         super();
>         String[] files = concat_filenames.split("\n");
>         // open all files and place their processors in an ImageStack
>         // ...
>     }
> }
>
> Then just call, from your plugin:
>
> public class My_Plugin implements PlugIn {
>     public void run(String arg) {
>         // obtain filenames from wherever
>         // ...
>         ImagePlus imp = new StackPlus(filenames);
>         imp.show();
>     }
> }
>
>
> The above assumed absolute paths where concatenated in a single String and
> separated by newline characters.
>
> Albert
>


Hi and Wow!

That is easy-easy-easy!  To think I have been bumbling around all this
time.   The class (below) accepts either a string of concatenated paths
or a string array of the same.  Thanks so much for the guidance and
encouragement.

Cheers,
Ben

***BEGIN
/**

     I had asked on the ImageJ mailing list how to programmatically
create a stack from a
     list of one or more file names.  This is slightly different than
the making a stack by
     specifying a directory which contains the images.  My need comes
from working with an
     instrument that writes "data" images and related-but-different
"calibration" images
     to the same directory.

     Albert Cardona answered via the ImageJ mailing list suggesting a
couple of approaches.
     This was his last suggestion.

         Perhaps best is:

         public class StackPlus extends ImagePlus {
             public StackPlus(String concat_filenames) {
                 super();
                 String[] files = concat_filenames.split("\n");
                 // open all files and place their processors in an
ImageStack
                 // ...
             }
         }
         Then just call, from your plugin:

         public class My_Plugin implements PlugIn {
             public void run(String arg) {
                 // obtain filenames from wherever
                 // ...
                 ImagePlus imp = new StackPlus(filenames);
                 imp.show();
             }
         }

         The above assumed absolute paths where concatenated in a single
String and
         separated by newline characters.

         Albert

     2007-04-05 [hidden email] - Thanks Albert!
*/

import ij.*;
import ij.io.*;
import java.io.*;

/**
This class extends ImagePlus by adding two constructors. The first
accepts a String
of absolute file paths concatenated with the new line character ("\n").
  The second
accepts a String array of absolute file paths.

@see ij.ImagePlus
@see ij.ImageStack
*/
public class StackPlus extends ImagePlus {

     /** Constructs a stack from a String of absolute file paths
concentanated with the
      "\n" newline character */
     public StackPlus(String concat_filenames) {
         super();
         String[] files = concat_filenames.split("\n");
         // open all files and place their processors in an ImageStack
         // ...
         Opener opener = new Opener();
         File file = new File(files[0]);
         String name = file.getName();
         if  (file.exists() == false) {
             IJ.log("File not found: " + files[0]);
             return;
         }

         IJ.showProgress(0, files.length);
         ImagePlus imp = opener.openImage(files[0]);
         int w = imp.getWidth();
         int h = imp.getHeight();
         ImageStack stack = new ImageStack(imp.getWidth(), imp.getHeight());
         stack.addSlice(name, imp.getProcessor());
         //step through the remaining images - check for existence and size
         if (files.length > 1) {
             for (int i = 1; i< files.length; i++){
                 IJ.showProgress(i, files.length);
                     //check each file
                 file = new File(files[i]);
                 if  (file.exists() == false) {
                     IJ.log("File not found: " + files[i]);
                     return ;
                 }
                 imp = opener.openImage(files[i]);
                 //check the dimensions of each image
                 if ((imp.getWidth() != w) || (imp.getHeight() != h)){
                     IJ.log("Images must all the same size");
                     return ;
                 }
                 stack.addSlice(file.getName(), imp.getProcessor());
             }
         }
        setStack(name,stack);
     }//end of run method

     /** Constructs a stack from a String array of absolute file paths */
     public StackPlus(String[] files) {
         super();
         // open all files and place their processors in an ImageStack
         // ...
         Opener opener = new Opener();
         File file = new File(files[0]);
         String name = file.getName();
         if  (file.exists() == false) {
             IJ.log("File not found: " + files[0]);
             return;
         }

         IJ.showProgress(0, files.length);
         ImagePlus imp = opener.openImage(files[0]);
         int w = imp.getWidth();
         int h = imp.getHeight();
         ImageStack stack = new ImageStack(imp.getWidth(), imp.getHeight());
         stack.addSlice(name, imp.getProcessor());

         if (files.length > 1) {
             for (int i = 1; i< files.length; i++){
                 IJ.showProgress(i, files.length);
                     //check each file
                 file = new File(files[i]);
                 if  (file.exists() == false) {
                     IJ.log("File not found: " + files[i]);
                     return ;
                 }

                 imp = opener.openImage(files[i]);

                 //check the dimensions of each image
                 if ((imp.getWidth() != w) || (imp.getHeight() != h)){
                     IJ.log("Images must all the same size");
                     return ;
                 }

                 stack.addSlice(file.getName(), imp.getProcessor());
             }
         }
        setStack(name,stack);
     }//end of run method

}//end of class definition


***END