Groovy RGBStackMerge.mergeChannels() error

fiji
imagej
macro
groovy
bioformats

#1

Hi All,

I’ve almost finished this basic macro to split, colour and combine ND2 files from an old version of NIS elements. I had this running fine with more basic IJ.run() commands but wanted to process in the background. After removing IJ.getImage() to hide images everything fell apart, so i’ve been rewriting using the following code. Unfortunately I’ve hit a wall and keep getting an error with ij.plugin.RGBStackMerge.mergeChannels() and this is doing my head in so I’ve resigned to ask or help.

The example is a 6 channel image where the first 3 are fluorescent and the final three are the bright-field images in RGB. I want a final image that merges the bright-field image into a single gray image. this is my process:- Split channels, apply LUT of correct colour, isolate DIC images and merge to greyscale image, isolate remaining fluorescent images all works ok. After combining fluorescent channels with single grey-scale channel the mergeChannels() command gives

groovy.lang.MissingMethodException: No signature of method: static ij.plugin.RGBStackMerge.mergeChannels() is applicable for argument types: (java.util.ArrayList, java.lang.Boolean) values: [[img["C2-Multichannel TEST.nd2" (-907), 16-bit, 1280x1024x1x1x1], ...], ...]
Possible solutions: mergeChannels([Lij.ImagePlus;, boolean)
	at groovy.lang.MetaClassImpl.invokeStaticMissingMethod(MetaClassImpl.java:1506)
	at groovy.lang.MetaClassImpl.invokeStaticMethod(MetaClassImpl.java:1492)
	at org.codehaus.groovy.runtime.callsite.StaticMetaClassSite.call(StaticMetaClassSite.java:53)
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:133)
	at Script60$_run_closure1.doCall(Script60.groovy:58)
	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:498)
	at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
	at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
	at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
	at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1027)
	at groovy.lang.Closure.call(Closure.java:414)
	at groovy.lang.Closure.call(Closure.java:430)
	at org.codehaus.groovy.runtime.ResourceGroovyMethods.eachFileRecurse(ResourceGroovyMethods.java:1131)
	at org.codehaus.groovy.runtime.ResourceGroovyMethods.eachFileRecurse(ResourceGroovyMethods.java:1363)
	at org.codehaus.groovy.runtime.dgm$940.invoke(Unknown Source)
	at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:274)
	at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:56)
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
	at Script60.run(Script60.groovy:22)
	at org.scijava.plugins.scripting.groovy.GroovyScriptEngine.eval(GroovyScriptEngine.java:303)
	at org.scijava.plugins.scripting.groovy.GroovyScriptEngine.eval(GroovyScriptEngine.java:122)
	at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264)
	at org.scijava.script.ScriptModule.run(ScriptModule.java:160)
	at org.scijava.module.ModuleRunner.run(ModuleRunner.java:168)
	at org.scijava.module.ModuleRunner.call(ModuleRunner.java:127)
	at org.scijava.module.ModuleRunner.call(ModuleRunner.java:66)
	at org.scijava.thread.DefaultThreadService$3.call(DefaultThreadService.java:238)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

this is my dirty code, any input is appreciated.

#@File (label="Files to analyse",style="directory") dirIN
#@File (label="Save results here",style="directory") dirOUT
#@String(label="Channel order", description="R=Red;B=Blue;G=Green;W=Brightfield") chanOrder
println dirIN
println dirOUT
chanOrderUP = chanOrder.toUpperCase()
println chanOrderUP
channels = []
import ij.IJ
import ij.ImagePlus
import loci.plugins.BF
import ij.plugin.ChannelSplitter
import ij.plugin.RGBStackMerge
import ij.process.LUT
import java.awt.Color
import ij.CompositeImage
//import ij.plugin.OverlayCommands
import ij.plugin.ZProjector
dirIN.eachFileRecurse { file ->    
	filename =  dirIN.path+File.separator+file.name 
	savename =  dirOUT.path+File.separator+file.name
    //open all files of certain type
    if (filename.endsWith(".nd2")){
    	imp = BF.openImagePlus(filename)
    	println imp
		//split channels
		channels = ChannelSplitter.split(imp[0])
		//loop through channles order and change LUT to right colour
		for (i=0; i<chanOrder.length(); i++){
			if (chanOrderUP[i] =="R"){
						luts = LUT.createLutFromColor(Color.RED)
						channels[i].setLut(luts)}
			if (chanOrderUP[i] =="G"){
						luts = LUT.createLutFromColor(Color.GREEN)
						channels[i].setLut(luts)}
			if (chanOrderUP[i] =="B"){
						luts = LUT.createLutFromColor(Color.BLUE)
						channels[i].setLut(luts)}
			if (chanOrderUP[i] =="C"){
						luts = LUT.createLutFromColor(Color.CYAN)
						channels[i].setLut(luts)}
			if (chanOrderUP[i] =="M"){
						luts = LUT.createLutFromColor(Color.MAGENTA)
						channels[i].setLut(luts)}
			if (chanOrderUP[i] =="Y"){
						luts = LUT.createLutFromColor(Color.YELLOW)
						channels[i].setLut(luts)}
			if (chanOrderUP[i] =="W"){
						//isolate DIC RGB images and merge into single grayscale
						DIC = channels.drop(i)
						println DIC
						DICmerge = RGBStackMerge.mergeChannels(DIC, false)
						DICflat = ZProjector.run(DICmerge,"avg");
						//Save DIC
						IJ.saveAsTiff(DICflat, savename+"_BF")
						//isolate Fluorescent channels
						fluor = channels.take(i)						
						//Merge fluorescent with DIC greyscale
						channels = fluor+DICflat
						println fluor
						println channels2
						}
			}
	newImage = RGBStackMerge.mergeChannels(channels, false)
	newImage.show()
	IJ.saveAsTiff(newImage, savename)
    }
}

Cheers
B


#2

The issue is the type matching here, because Groovy treats channels as a ArrayList<ImagePlus> instead of an ImagePlus[] array.

You can tell Groovy to treat it as ImagePlus[] like this:

RGBStackMerge.mergeChannels(channels as ImagePlus[], false)

#3

Ahh thanks very much @imagejan , that works a treat :grin:. Is the reason that the list turns into an ArrayList<ImagePlus> due to the use of the ‘+’ operator when combining the channels in channels = fluor + DICflat ? If so is there a better way of combining ImagePlus[] arrays.


#4

Is there a reason why the originally imported camera+microscope metadata would be removed after running RGBStackMerge.mergeChannels()?

To troubleshoot I have saved the single images directly before this step and the metadata is still present.

any advice appreciated :thinking:

 //example user generated input colour array
    strings = ["red", "green", "blue", "none"]
    colours = strings.findAll {it != "None"}

//Import libraries
import ij.IJ
import ij.ImagePlus
import loci.plugins.BF
import loci.plugins.in.ImporterOptions
import ij.plugin.ChannelSplitter
import ij.plugin.RGBStackMerge
import ij.process.LUT
import java.awt.Color
import ij.CompositeImage
import ij.plugin.ZProjector

//Recurse images in specified folders
dirIN.eachFileRecurse { file ->    
	filename =  dirIN.path+File.separator+file.name 
	savename =  dirOUT.path+File.separator+file.name
    //open all files of certain type
    if (filename.endsWith(".nd2")){
    	options = new ImporterOptions();
		options.setId(filename);
		options.setAutoscale(false);
    	imp = BF.openImagePlus(options)
    	//split channels
    	channels = ChannelSplitter.split(imp[0])
    	//loop through channles order and change LUT to right colour
		for (i=0; i<colours.size(); i++){
			if (colours[i] !="BrightField"){
						channels[i].setLut(LUT.createLutFromColor(Color.(colours[i].toUpperCase())))}
			 }
                      
         //check where metadata is lost
          IJ.saveAsTiff(channels[0], savename+"Metadatacheck")
    			
          //----metadata lost here-----
          channels = RGBStackMerge.mergeChannels(channels as ImagePlus[], false)
          IJ.saveAsTiff(channels, savename)
    	  IJ.run("Close All", "")
			
		}
}