ImageJ Merge Channels in Macro with parameters

Hello Image.SC forums,

I have a portion of ImageJ 1.x script (using parameters) that is supposed to merge two channels. So far, this code will open the two stacks from separate folders, merge them, and save the merged stack into a new folder.

This is the weird part.

When I manually merge them, I get exactly what you would expect, two fully merged stacks with the assigned colors.

When I use this code:

  1. For some reason this code only semi works with the “&” symbol in front of “image1” and “image2”.

  2. These are 8-bit images and one of these stacks has numbers next to the structure in the stack (on one image of the stack) and is assigned the 255 (white) value for 8-bit resolution (0-255). The code loses the numbers and the quality of the merge is not equal to that of manually merging.

  3. The combination of channel assignment (c=) determines if it merges any color at all. The way I have it now (c1= c7=) semi-works but not all combinations (such as: c1= c2=) do (resulting in one color for both channels).

This is a post that helped me get this far

Any help or direction would be great.

-Michael

setBatchMode(true);
processFolder_6(Mask_Overlay, Numbered_Masks);
function processFolder_6 (Mask_Overlay, Numbered_Masks) {
list1= getFileList(Mask_Overlay); 
n1=lengthOf(list1);
print("n1 = ",n1);
list2= getFileList(Numbered_Masks); 
n2=lengthOf(list2);
small = n1;
if(small<n2)
	small = n2;
for(i=0;i<small;i++)
    {
	stack1=list1[i];
	stack2=list2[i];
    function processFile_6(Mask_Overlay, Numbered_Masks, IENF_Scoring) {
    }
    open(Mask_Overlay+File.separator+list1[i]);
    open(Numbered_Masks+File.separator+list2[i]);
	print("processing image",i);
	run("Merge Channels...", c1=&stack2 c7=&stack1"); 
   	saveAs("Tiff", IENF_Scoring + File.separator + list1[i]);
	print("Processing: " + Mask_Overlay + File.separator + Mask_Overlay);
	print("Processing: " + Numbered_Masks + File.separator + list2[i]);
	print("Saving to: " + IENF_Scoring); 
}
close("*");

run("Merge Channels...", c1=&stack2 c7=&stack1"); 

That this works at all is strange.
The proper way would be this:

run("Merge Channels...", "c1=" + stack2 + " c7=" + stack1); 

Under the condition the stack1 and stack2 variables will correspond to the image titles.

1 Like

Thank you for your help.

I tried this but received an error message:

You are still missing a plus sign after the stack2 and you need a space after the " before c7. Copy paste the stuff I wrote above.

1 Like

Opps, thanks. I then received this error message:

Sorry just saw and edited. There is a space missing before c7.

1 Like

Thanks Chris!

That worked but the results are the same as before, with the “&” symbol.

There is an image that is produced but the numbers for the structure are missing (255 value, 8-bit image) and not all of the c= combination work.

I really just want to merge the two images and have the same results as a manual merge but have had such a problem with this.

Could you provide an example image. I guess you are talking about an overlay?

What do you mean by the quality of the merge? Maybe you want to create a composite rather than a RGB then you need to add the create flag.

run("Merge Channels...", "c1=" + stack2 + " c7=" + stack1 + " create");

I don’t understand the third problem. If you want to reproduce the interaction via the GUI maybe you can try to record the action you perform: Plugins > Macros > Record.

1 Like

Thanks Chris,

Below are examples of the output differences between manually merging the channels and using the code to merge the channels.

For some reason I am losing the numbers and the contrast.

I also tried image calculator but there is no way (that I could find) to have specific colors for each channel.

I did use the macro recorder to confirm the code. It may be a bug in the system somewhere but I cannot reproduce the same results from manual merging, with a macro.

Manual merge (step 10 of stack) (immediately below)

Code merge (step 10 of stack) (below)

From what I can see is that your first image is a single channel that contains an overlay. Whereas the second result is a two channel image without an overlay.

Which means the merge channel cannot produce the same result as above. Since the first manual merge must somehow add an overlay not another channel. Could you provide the actual input images. We are happy to find a solution.

1 Like

Thank you, Chris!

Here are the stacks, compressed with winrar.

One is the output from plugin “3d object counter” and the other is the raw original combined with, via image-calculated (ADD), a skeletonized version of the raw original image.

The files are too large for uploading here so I compressed them with winrar and uploaded them to my dropbox: https://www.dropbox.com/sh/b0fqsacoquscw21/AAAbgeg1tzBUDHbJaOiNedwRa?dl=0

I just noticed this… the file sizes are the same (uncompressed) but the “(numberedfrom-3dObjectCounter)SBB_FOV1.rar” file is half of the size of the other compressed file “mask-overlay_SBB_FOV1”. This looks like a clue.

Thanks again,

-Michael

Ok, thanks for the test images.
The size difference comes from the fact that one image is almost only black.
Thus when you compress it, it will be really small.

I have a slight suspicion what is happening.
The merge channel is fine. It does what it should do. I tested that.
But you have named your images the same.
Thus if you open them in Fiji the second image with the same name gets a suffix “-1” added to the end of the title. Because two images with the same title in Fiji is a no no.

But your macro is using the original file name for the merge. Thus the same image is merged together.

EDIT: Sorry had to remove the code. Tested it and there is another issue together with batch mode.

2 Likes

So ok this is a bit more complicated.
The problem is.

In the normal visible mode two images with the same name are not possible so it opens with the “-1” suffix. Fine we can use the image title.

But once batchmode is activated. The suffix is not added. Which again leads to the fact that the same two images are merged together.
Would also be fine, we can use the imageids for that. No!! The merge channel does not like imageids…it only takes titles… eye twitching intensivies :rofl:

2 Likes

New fix. Now we just rename the incoming file and add an identification to the original filename. So we can be 110% sure that this is the image we need.

Maybe there is a alternative to the merge channels that does not insist on just taking titles. But thats what this is now…

I anyways recommend in macros to always rename files and give them unique IDs just to code defensively against mixing up images with their titles.

setBatchMode(true);
processFolder_6(Mask_Overlay, Numbered_Masks);
function processFolder_6 (Mask_Overlay, Numbered_Masks) {
list1= getFileList(Mask_Overlay); 
n1=lengthOf(list1);
print("n1 = ",n1);
list2= getFileList(Numbered_Masks); 
n2=lengthOf(list2);
small = n1;
if(small<n2)
	small = n2;
for(i=0;i<small;i++)
    {
	stack1=list1[i];
	stack2=list2[i];
    function processFile_6(Mask_Overlay, Numbered_Masks, IENF_Scoring) {
    }
    open(Mask_Overlay+File.separator+list1[i]);
    newName1 = "image-" + list1[i];
    rename(newName1);
    open(Numbered_Masks+File.separator+list2[i]);
    newName2 = "overlay-" + list1[i];
    rename(newName2);
	print("processing image",i);
	run("Merge Channels...", "c1=" + newName1 + " c7=" + newName2); 
   	saveAs("Tiff", IENF_Scoring + File.separator + list1[i]);
	print("Processing: " + Mask_Overlay + File.separator + Mask_Overlay);
	print("Processing: " + Numbered_Masks + File.separator + list2[i]);
	print("Saving to: " + IENF_Scoring); 
}
close("*");

2 Likes

I know I have thanked you a lot already but I really do appreciate your time. I learn a lot through troubleshooting and with help resolving problems. I also tried using image IDs (because they work with image calculator) and I could not figure out why it wouldn’t work with the Merge function. It is neat to know that the Merge function requires an actual title, I would not have guessed that.

For some reason, it takes awhile “Initializing base reader”.

image

Then it gives this error:

(Fiji Is Just) ImageJ 2.1.0/1.53f; Java 1.8.0_172 [64-bit]; Windows 10 10.0; 748MB of 6024MB (12%)

java.lang.StackOverflowError
at java.util.regex.Pattern$GroupTail.match(Pattern.java:4719)
at java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3800)
at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
at java.util.regex.Pattern$GroupHead.match(Pattern.java:4660)
at java.util.regex.Pattern$LazyLoop.match(Pattern.java:4849)
at java.util.regex.Pattern$GroupTail.match(Pattern.java:4719)
at java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3800)
at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
at java.util.regex.Pattern$GroupHead.match(Pattern.java:4660)
at java.util.regex.Pattern$LazyLoop.match(Pattern.java:4849)
at java.util.regex.Pattern$GroupTail.match(Pattern.java:4719)
at java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3800)
at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
at java.util.regex.Pattern$GroupHead.match(Pattern.java:4660)
at java.util.regex.Pattern$LazyLoop.match(Pattern.java:4849)
at java.util.regex.Pattern$GroupTail.match(Pattern.java:4719)
at java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3800)
at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
at java.util.regex.Pattern$GroupHead.match(Pattern.java:4660)
at java.util.regex.Pattern$LazyLoop.match(Pattern.java:4849)
at java.util.regex.Pattern$GroupTail.match(Pattern.java:4719)

there are a lot more lines to this.

After searching this error, it looks like the line numbers may be recursively called. I am looking at the code but it is hard for me to identify where this could be.

Is this an error with the script I posted or with a different one? I am confused sorry.

1 Like

Yes, I copied your code and replaced mine. Here is the section of code that I am using to troubleshoot this. If you create empty folders titled “3_Mask_Overlay”, “5_Numbered_Masks”, and “6_IENF_Scoring” and place the stacks that I uploaded into the “3_Mask_Overlay” and “5_Numbered_Masks” folders, then you should be able to replicate my issue on your end, if neccesary.

#@ File (label = "select folder 1: PGP 9.5", style = "directory") PGP
#@ File (label = "select folder 2: Skeletons", style = "directory") Skeletons
#@ File (label = "select folder 3: Mask Overlay", style = "directory") Mask_Overlay
#@ File (label = "select folder 4: Binary folder", style = "directory") Binary
#@ File (label = "select folder 5: Numbered Masks", style = "directory") Numbered_Masks
#@ File (label = "select folder 8: IENF Scoring", style = "directory") IENF_Scoring
#@ File (label = "select folder 9: Final Processing", style = "directory") Final_Processing

getDateAndTime(year, month, week, day, hour, min, sec, msec);
 print("Date: "+year+"/"+month+"/"+day);
 print("Time:");
  start = getTime;
  while (getTime-start<=10000) {
      getDateAndTime(year, month, week, day, hour, min, sec, msec);
      print("\\Update:"+"Time: "+hour+":"+min);
      wait(100);

setBatchMode(true);
processFolder_6(Mask_Overlay, Numbered_Masks);
function processFolder_6 (Mask_Overlay, Numbered_Masks) {
list1= getFileList(Mask_Overlay); 
n1=lengthOf(list1);
print("n1 = ",n1);
list2= getFileList(Numbered_Masks); 
n2=lengthOf(list2);
small = n1;
if(small<n2)
	small = n2;
for(i=0;i<small;i++)
    {
	stack1=list1[i];
	stack2=list2[i];
    function processFile_6(Mask_Overlay, Numbered_Masks, IENF_Scoring) {
    }
    open(Mask_Overlay+File.separator+list1[i]);
    newName1 = "image-" + list1[i];
    rename(newName1);
    open(Numbered_Masks+File.separator+list2[i]);
    newName2 = "overlay-" + list1[i];
    rename(newName2);
	print("processing image",i);
	run("Merge Channels...", "c1=" + newName1 + " c7=" + newName2); 
   	saveAs("Tiff", IENF_Scoring + File.separator + list1[i]);
	print("Processing: " + Mask_Overlay + File.separator + Mask_Overlay);
	print("Processing: " + Numbered_Masks + File.separator + list2[i]);
	print("Saving to: " + IENF_Scoring); 
}
close("*");

//--------------------------------------------------------------------------------------------------------- Closing

getDateAndTime(year, month, week, day, hour, min, sec, msec);
 print("Date: "+year+"/"+month+"/"+day);
 print("Time:");
  start = getTime;
  while (getTime-start<=10000) {
      getDateAndTime(year, month, week, day, hour, min, sec, msec);
      print("\\Update:"+"Time: "+hour+":"+min);
      wait(100);

showMessage ("IENF Processing Complete");

It is a strange error. As far as I see you do not use regular expressions.
It is also super hard to reproduce the error without completely reproducing the folder and file setup you have there. So I have not much to go from. All one can say is that stuff goes wrong from this line onwards:

list2= getFileList(Numbered_Masks); 

until:

print("processing image",i);

If I where you. I drop all the unessary stuff for the merging. The gui, the time measurement.
Just define an input and an output and the operation that needs to be performed to go from the input files to the output files. Thats it.

Then make sure that works.
Then build stuff around it.

If stuff breaks use print statements to isolate where stuff breaks.
See which variables contain what when stuff breaks.

There is also some weird stuff that I can spot.
The function defintion is entirely unessary it seems. Does not contain anything and is not called:

function processFile_6(Mask_Overlay, Numbered_Masks, IENF_Scoring) {
    }

I made a mistake also before this line:
newName2 = "overlay-" + list1[i];should actually read: newName2 = "overlay-" + list2[i]; but that is not the problem.

1 Like

Thanks for all of your time, Chris! I will take your advice and get more familiar with what is going on in the code. You have provided me a lot of insight so I will take some time this weekend to try and figure this out.

Have a great weekend,

-Michael

1 Like

The problem was with the stacks. The Mask_Overlay stack was 96 dpi and all of the other stacks were 6 dpi. The Mask_Overlay stack was the original PGP stack (6 dpi) that was processed to generate skeletons and then Image Calculate “ADD” with the original PGP stack, resulting in 96 dpi resolution and a recursive stack error. I may look into downsampling the 96 dpi to 6 dpi (as long as there is no pixel loss) but for now I am simply merging the “Numbered_Masks” (6 dpi) with the original PGP stack ( 6 dpi) and it works perfectly!

Thanks so much Chris. I was able to learn more about the code and what is going on but I would have not been able to do it if it were not for a working code. I am doing my best to learn this so I use it as a tool but it does take a bit.

Here is a small 7-second OBS screen recording mp4 video showing the results and the contrast is amazing, I compressed it because we cannot upload video. I am using this to score the quality of my algorithm and this makes is very efficient, almost luxurious. I really appreciate your help and hope to help others in the future.

Solved_stack_merging.rar (363.8 KB)

Have a great week.

Thanks again,
-Michael

2 Likes