Jython script to combine images into a single hyperstack

I’m trying to write a Jython script to combine individual images into a single TIFF hyperstack.

They are named as following : prefix_Wchannel_Zslice.tif

I’m able to achieve this by opening images in the right order, then using first images to stack and then stack to hyperstack.

I’ve come up with this script (should work for one defined prefix):

import os
from ij.io import DirectoryChooser, FileSaver
from ij import IJ, ImagePlus, VirtualStack
from ij.plugin import HyperStackConverter

def run():
    listimg = [0 for x in range(34)]
    srcDir = '/path/to/images/'
    for root, directories, filenames in os.walk(srcDir):
    for filename in filenames:
        if filename.startswith(prefix):
            listimg[order[filename[len(prefix):-12]]] = filename
    vs = None
    for filename in listimg:
        imp = IJ.openImage(srcDir+filename)
    if not vs:
        vs = VirtualStack(imp.width, imp.height, None, srcDir)
    vs.addSlice(filename)
    hs = ImagePlus(prefix[:-1], vs)
    vhs = HyperStackConverter.toHyperStack(hs,2,17,1)
    vhs.show()
    fs = FileSaver(vhs)
    fs.saveAsTiff("/where/i/want/the/output/test.tif")

prefix='prefix'
order={}
for i,name in enumerate(['W'+str(W)+'_Z'+str(Z) for Z in range(1,18)
                                                for W in range(1,3)]):
    order[name]=i

run()

I have 2 problems with this really basic script:

  • Unlike when I use the menu items, I can see both channels at the same time on the output image (switching between the 2 c doesn’t change anything)

  • Trying to save the image as I did (with or without displaying it first) results in:

      Traceback (most recent call last):
      File "/home/ncedilni/Dropbox/rnpdetect/scripts/fiji/hyperstackimages.py", line 31, in <module>
      order[name]=i
      File "/home/ncedilni/Dropbox/rnpdetect/scripts/fiji/hyperstackimages.py", line 23, in run
      #  fs.save()
      at ij.io.FileSaver.saveAsTiffStack(FileSaver.java:178)
      at ij.io.FileSaver.saveAsTiff(FileSaver.java:99)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:497)
    
       java.lang.NullPointerException: java.lang.NullPointerException
    
      at org.python.core.Py.JavaError(Py.java:495)
      at org.python.core.Py.JavaError(Py.java:488)
      at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:188)
      at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:204)
      at org.python.core.PyObject.__call__(PyObject.java:404)
      at org.python.core.PyObject.__call__(PyObject.java:408)
      at org.python.core.PyMethod.__call__(PyMethod.java:124)
      at org.python.pycode._pyx4.run$1(/home/ncedilni/Dropbox/rnpdetect/scripts/fiji/hyperstackimages.py:23)
      at org.python.pycode._pyx4.call_function(/home/ncedilni/Dropbox/rnpdetect/scripts/fiji/hyperstackimages.py)
      at org.python.core.PyTableCode.call(PyTableCode.java:165)
      at org.python.core.PyBaseCode.call(PyBaseCode.java:120)
      at org.python.core.PyFunction.__call__(PyFunction.java:307)
      at org.python.pycode._pyx4.f$0(/home/ncedilni/Dropbox/rnpdetect/scripts/fiji/hyperstackimages.py:31)
      at org.python.pycode._pyx4.call_function(/home/ncedilni/Dropbox/rnpdetect/scripts/fiji/hyperstackimages.py)
      at org.python.core.PyTableCode.call(PyTableCode.java:165)
      at org.python.core.PyCode.call(PyCode.java:18)
      at org.python.core.Py.runCode(Py.java:1275)
      at org.scijava.plugins.scripting.jython.JythonScriptEngine.eval(JythonScriptEngine.java:76)
      at org.scijava.script.ScriptModule.run(ScriptModule.java:174)
      at org.scijava.module.ModuleRunner.run(ModuleRunner.java:167)
      at org.scijava.module.ModuleRunner.call(ModuleRunner.java:126)
      at org.scijava.module.ModuleRunner.call(ModuleRunner.java:65)
      at org.scijava.thread.DefaultThreadService$2.call(DefaultThreadService.java:191)
      at java.util.concurrent.FutureTask.run(FutureTask.java:266)
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
      at java.lang.Thread.run(Thread.java:745)
    
      Caused by: java.lang.NullPointerException
    
      at ij.io.FileSaver.saveAsTiffStack(FileSaver.java:178)
      at ij.io.FileSaver.saveAsTiff(FileSaver.java:99)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:497)
      at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:186)
      ... 24 more
    

Should I use other classes ?
What am I doing wrong ?

What you’re doing is almost correct, I think. The exception is coming from your HyperStack ImagePlus probably not having a FileInfo because you manually constructed it here:

hs = ImagePlus(prefix[:-1], vs)

I think you just need to set the FileInfo to that of the original image, e.g.

...
vhs.setFileInfo(imp.getFileInfo())
fs = FileSaver(vhs)
fs.saveAsTiff("/where/i/want/the/output/test.tif")
...

Try that and see if you have more problems. :slight_smile:

@Wayne - is it intended that a VirtualStack needs a FileInfo to be saved? At a glance, it looks like a null FileInfo would be OK.

1 Like

The null pointer exception is caused by a bug that is fixed in the latest ImageJ daily build (1.50i2). Here is some JavaScript code that reproduces the bug:

  dir = IJ.getDir("Choose Directory ");
  f = new File(dir);
  list = f.list();
  stack = new VirtualStack(256, 256, null, dir);
  for (i=0; i<list.length; i++) {
     if (list[i].endsWith(".tif"))
        stack.addSlice(list[i]);
  }
  img = new ImagePlus("Virtual Stack", stack);
  IJ.saveAsTiff(img, dir+"stack.tiff");
2 Likes

Thanks @hinerm, your help solved the crashing issue !
However, I still don’t understand why but I see both channels superimposed when I use the jython script, unlike when I combine my images using the menu entries. I can switch from c:1/2 and c:2/2 using the slider but it doesn’t change the visual.
However, I tried to open the image as a numpy array using tifffile’s imread (outside FIJI), and it looks like both channels are indeed separated, which is what I want. It may just be a display setting I missed I guess.

Hi @nicoco

Most likely your multi-C image is displayed as a Composite, where all channels are superimposed using their LUT. This is just a display settings.

Check the Channel Tools in Image > Color to change this.

2 Likes

This was it, thanks !

2 Likes