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 |
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 |
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 |
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 |
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 |
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 |
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. |
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 > |
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 |
Free forum by Nabble | Edit this page |