Create TIF with 4 channels per pixel?

Good morning,
imageJ can open TIF-images with 4 channels per pixel (output form scanner using Vuescan, R, G, B, IR). How can I create such a 4-channel image from 4 separate images in imageJ?

I used one of the VueScan images and separated the channels with “Color – split channels”, Then I tried “merge channels”. But this created a TIF file with 4 separate images, which is not what I want.

Hermann-Josef

Hey,

I hope this is what you want:

Open the images as stack or convert the images to a stack (open images --> IJ>Images>Stacks>Images to Stack)

Then convert the stack to a hyperstack (IJ>Images>Hyperstack>Stack to Hyperstack) with the settings 4 channels, 1 slice, 1 timepoint, Display Mode Color.

Lastly, go to IJ>Image>Color>Make Composite.

Now all four images are overlaid (RGB+Gray) and you can adjust the channels individually. If you want the overlay image only, save the file as PNG. If you want to keep the original channels, save it as TIF.

Let me know if there are further infos needed.

Hello,

thank you for your detailed instructions. Everything within imageJ works fine (now I have to read the manual to understand, what I do :slight_smile: ). The result in imageJ looks very much the same, as if I load an original VueScan image into imageJ.

However, if I save the result following your recipe above as TIF, I again get a TIF with 4 separate images, not one image with 4 channels/pixel. The latter is crucial, since I want to process this image again in VueScan.

Hermann-Josef

Hey,

Just to be sure: If you create the composite, do you see all four channels overlaid in one image?

If you flatten the image (CTRL+SHIFT+F) you can see by the color picker that each pixel has four intensity values (The same holds true for the png image). You can also save this flattened image as TIF.

If this is still not helpful, could you elaborate a little bit more?

Hello,

yes, I see all 4 channels in one image:
image
On the left is the new image, on the right the original VueScan image. The contrast is, however, different.

I tried the colour picker on the flattened image, but I must confess, I did not manage to get cursor values displayed. Here is what I see:
image

I cannot read individual pixel values in the image.

To elaborate more, of what I want:
If I look at the metadata of an original VueScan image, I see
image
i.e. there are 4 x 16 bits per sample.
If I look at the image saved as TIF following your procedure above, I get;
image
and there are 4 images embedded in the TIF file (IFD0 … IFD3), not only 1, each with 1 x 16 bits per sample.

Hermann-Josef

I am a bit confused about what you want to do. Reproducible examples make it easier for those of us on the Forum to help you.

You did not upload an exemplar image, so I found an exemplar 4 channel tif image with 4 16 bit channels here. It is 40MB, so I extracted a 512x512 region to test (see below).

small_rgbir.tif (2.0 MB)

Slnce this was output by Fiji, you could check how the VueScan software processes it…

I wrote a groovy script to generate an RGB image from the R, G, B channels and display the IR channel. You will need to change the image paths… Hope this helps…

/*	extract_rgb_from_rgbir.groovy
 *	
 *	A groovy script
 *	
 *	Date        Who  What
 *	==========  ===  ==============================
 *	2019-06-09  JRM  Initial prototype
 *	
 *	Create an RGB tif image from an image produced 
 *	by a scanner that saves a 4-channel TIF: R-G-B-IR
 *	
 *	I downloaded the image from 
 *	http://static.micasense.com/samples/sequoia/sample01/Sequoia_Sample_GEOTIFF_01.tif
 *	GeoTIFF file (4 layers): GeoTIFF File (40 MB)
 *	
 *	Spectral bands for GeoTIFF file:
 *	Band 1: 550 nm
 *	Band 2: 660 nm
 *	Band 3: 735 nm
 *	Band 4: 790 nm
 *	
 *	I named the image: Sequoia_Sample_RGBIR_01.tif
 *	It is 41.1 MB
 *	
 *	I created a small 512x512 version to upload (2.1 MB)
 *	
 */

import ij.IJ
import ij.ImagePlus

// start clean...
IJ.run("Close All")
imp = IJ.openImage("/Users/jrminter/dat/images/key-test/RGBIR/small_rgbir.tif");
imp.show()
IJ.run(imp, "Stack to Images", "")
// the last image opened is "IR"
imp_ir = IJ.getImage()
imp_ir.setTitle("IR")
IJ.run("Merge Channels...", "c1=Red c2=Green c3=Blue")
imp_rgb = IJ.getImage()
imp_rgb.show()

Good morning John,
@John_Minter thanks a lot for your effort and providing a script. Sorry for not providing an example. My main point was the 4 channels / pixel and I had thought that was all the information needed.

Here is the original output from VueScan:
VS_demo.tif (2.3 MB)

This is what I get, if I separate the channels in imageJ (I use version 1.52n):

Your input image has the correct format (bits per sample: 16 16 16 16), but the output image does not (bits per sample: 16). The output again is 4 separate images Ifd0 … ifd3, if I display the metadata:

image

My goal is to simulate a VueScan image. To see that I get the correct format, I wanted to separate the original scan into its 4 channels and then re-assemble them into a TIF of the same format as the original scan. If this is successful (test would be to load it into VueScan and see the correct image displayed), I could then go ahead and use scans from VueScan, process the IR-channel according to my needs, re-assemble the 4 images, store it as a TIF and finally process it by VueScan. This is the ultimate goal I have.

Best wishes

Hermann-Josef

Jossie, thanks for uploading the demo image. I have the VuseScan software and have been having success on one scanner and failure on the second. I can split the image as I had hoped. I tried to recombine (the last step in the script below) and I get an error I do not understand. Maybe Wayne Rasband @Wayne will see what I missed… It nice to have the key pair of eyes take a look :slight_smile:

/*
 * groovy function test 
 * the test image is VS_demo.tif in Jossie's post above...
 *
 */
import ij.*
import ij.plugin.*
import ij.process.*
import java.awt.image.*

def proc_r_g_b_ir_to_stack(imp_r, imp_g,
                           imp_b, imp_ir){
	width = imp_r.getWidth()
	height = imp_r.getHeight()
	stack = new VirtualStack(width, height)
	stack.addSlice(imp_r)
	stack.addSlice(imp_g)
	stack.addSlice(imp_b)
	stack.addSlice(imp_ir)
	img = new ImagePlus("Virtual Stack", stack)
	// this from
	// https://forum.image.sc/t/jython-script-to-combine-images-into-a-single-hyperstack/1130
	// did not fix...
	img.setFileInfo(imp_r.getFileInfo())
	
	return img
}

// start clean
IJ.run("Close All")

// open the demo image as a stack & display
// change the path to your directory
stack = IJ.openImage("/Users/jrminter/Downloads/VS_demo.tif")
stack.show()

// successively extract and display each component...
ip_red = stack.getProcessor(1)
imp_red = new ImagePlus('red', ip_red)
imp_red.show()

ip_green = stack.getProcessor(2)
imp_green = new ImagePlus('green', ip_green)
imp_green.show()

ip_blue = stack.getProcessor(3)
imp_blue = new ImagePlus('blue', ip_blue)
imp_blue.show()

ip_ir = stack.getProcessor(4)
imp_ir = new ImagePlus('ir', ip_ir)
imp_ir.show()

// there really isn't much in the IR... so...
// create an RGB image from the three RGB channels

IJ.run("Merge Channels...", "c1=red c2=green c3=blue")
imp_rgb = IJ.getImage()
imp_rgb.show()

// trying to reconstruct the stack fro r, g,b,
// and ir fails. Haven't figgured out the error

def ImagePlus new_stack = proc_r_g_b_ir_to_stack(imp_red, imp_green,
                                       imp_blue, imp_ir)

//new_stack.show()

Just in case you are not restricted to ImageJ, consider using Python:

import numpy
import tifffile

# read interleaved RGB+extrasample channels from TIFF into separate arrays
im = tifffile.imread('vs_demo.tif')
r, g, b, ir = numpy.moveaxis(im, -1, 0)

# process arrays; make sure not to change the data type
...

# save arrays as interleaved RGB+extrasample TIFF
im = numpy.moveaxis([r, g, b, ir], 0, -1)
tifffile.imwrite('vs_demo.out.tif', im, photometric='rgb',
                 planarconfig='contig', extrasamples='unspecified',
                 rowsperstrip=6, metadata=None)

Or directly change values in the file, e.g.:

import tifffile

# memory-map the ir values in the TIFF file
ir = tifffile.memmap('vs_demo.tif')[..., 3]

# process the ir array in-place or write processing results to ir array
...

# write any changes in the array back to the TIFF file
ir.flush()
1 Like

@John_Minter Good evening and thanks again for looking into this issue.

Since imageJ can open a VueScan image I had thought I try to just save such an image in imageJ without making any changes. Well, it saves it as 4 single images in a TIF. Thus I would conclude, since there are no options available when saving as TIF, that imageJ probably is not able to save the VueScan image in the same format as it was loaded from.

@cgohlke Thanks for pointing out a solution with Python.

I was looking for a solution and found out that with imagemagick one can save the image in the desired format:

magick convert R.tif G.tif B.tif IR.tif -channel RGBA -combine test.tif

does the trick. I could open the test image with VueScan. :slight_smile: Since I have learned how to launch a DOS script from within an imageJ macro, I think I will use this option – unless a solution within imageJ will become available.

Many thanks again and best wishes

Hermann-Josef

Not sure what your Groovy script is doing, but the following JavaScript code opens the 4 channel demo image, displays each of the four channels and saves it as a tiff stack. It is unlikely that VueScan can open tiff stacks created by ImageJ and ImageJ is not able to create 4 channels per pixel tiff files.

  dir = "/Users/wayne/Downloads/"
  img = IJ.openImage(dir+"VS_demo.tif")
  img.setDisplayMode(IJ.COLOR)
  img.show()
  for (i=1;i<=img.getStackSize(); i++) {
     img.setSlice(i)
     IJ.wait(1000)
  }
  IJ.saveAs(img, dir+"VS_demo2.tif")

Thank you, Wayne.

My Groovy scripts are a work in progress. I’m trying to migrate from Jython because it will be unsupported after Jan 1, 2020…

@Wayne Thanks for the clarification. So I will process the VueScan images with an imageJ macro (as I have done for scans with SilverFast) and then save the result with the DOS-batch using imagemagick.

Best wishes

Hermann-Josef