Use G'MIC to combine separate R,G,B images to create RGB image

Would it be possible you put some images somewhere so we can download them and try doing what you want ? I’m pretty sure this should be easy to find something that works.

Clearly, I still don’t understand how j works. Didn’t know it could replace the Y channel. Would it depend on how many channels that . has? E.g., if . had 2 channels, would YCb be replaced?

Hi David,
Happy to provide the test files I’m using. Will do it in my next reply. Your comment about rgb got me thinking. I’m not sure if my approach to identifying the problem is correct but I ran the following command:

gmic Ferniegair-GE3D-RGB.tif rgb2lab lab2rgb output Ferniegair-GE3D-RGBx.tif

My thinking was that the output should look identical to the input since all I’m doing is swapping color spaces back and forth: rgb → lab → rgb.
Following is the result of the above command:

The view on the right is from Irfanview. The view in Rawtherapee is pretty much all white with some some areas of blue and yellow. And the output is 32 bit.

Clearly I must be doing something wrong here.

When I first got into G’MIC, this was the first thing that stumped me. Conversion commands expect [0,255] range input and provide output of the same range. Anything outside will be clipped. So, if you are inputting 16 bit, you need to divide by 257, etc. After the operation, just remember to do the opposite. Saving using output is a bit confusing too because you would want it to be in the format that RT and Irfanview could interpret correctly. Someone will help you once you provide the sample images.

G’MIC uses floating point representation, 0.0 - 255.0. It’s not 8-bit. The 0.0 - 255.0 might be confusing, because it happens to line up with the 8-bit integer range, but no precision is being lost

So, the div 257 just scales the 16-bit integer range to the G’MIC range, represented in floating point.

Hi David,
Here are the source files:
Ferniegair-GE3D-B8.tif is the 16bit gray image to use for brightness.
Ferniegair-GE3D-B8.tif (4.0 MB)

Ferniegair-GE3D-RGB.tif is the 16bit RGB color image. This image was created using the G’MIC command that Afre provided in the original reply to my query.
Ferniegair-GE3D-RGB.tif (11.9 MB)

I think it noteworthy that the command:
gmic Ferniegair-GE3D-RED.tif Ferniegair-GE3D-GRN.tif Ferniegair-GE3D-BLU.tif to_gray append c output Ferniegair-GE3D-RGB.tif

correctly produced a 16bit and not a 32bit tiff whereas the command stream to replace the brightness channel results in 32bit output. ???

Hi Glenn,

I just want to make sure that I correctly understand your statement so I’ll restate it. When G’MIC converts from 16 bit to 8 bit, it’s not. What it is actually doing is converting numbers in the integer range of 0 to 65,535 into numbers in a floating point range of 0.0 to 255.0 by dividing the 16 bit value by 256.0. So a middle gray value of 32,767 becomes 127.998046875. In which case post conversion the data still has 65,536 possible distinct values. I agree that if this is true then no information is being lost. But is this how G’MIC is doing it?

Yes.

As @afre intimated, this can foist some confusion when using G’MIC. However, it lets folk load the great majority of images into G’MIC and work with them in the same range as was the file data, 0-255. It just gets confusing when you start with a 16-bit 0-65535 image, or, say, a floating point TIFF, which is usually ranged from 0.0-1.0.

By the time you guys are done with me I expect that I’ll be much better at G’MIC.

Running the following command:
gmic Ferniegair-GE3D-RGB.tif / 257 rgb2lab lab2rgb mul 257 output Ferniegair-GE3D-RGBxv2.tif

does give me a perfect looking image in Irfanvew but causes RawTherapee to go belly up and opens as an all white image in GIMP and Photoshop.

Ditto for the following command:
gmic Ferniegair-GE3D-RGB.tif / 257 rgb2lab lab2rgb output Ferniegair-GE3D-RGBxv3.tif

Here is my proposal:

$ gmic Ferniegair-GE3D-RGB.tif Ferniegair-GE3D-B8.tif div 257 srgb2lab8.. j.. . rm. lab82srgb mul 257 round o output16.tiff

Several things to note here :

  • I use commands srgb2lab8 and lab82srgb rather than srgb2lab and lab2srgb. This is basically the same Lab colorspace, but renormalized so that each L,a,b channel has a [0,255] range.
  • I use round before saving. This ensures you get only integer values in your image (here in range [0,65535]), so the output .tif file is saved as 16bits-integers, and not 32bits float-valued (some viewers do not read float-valued tiff).
  • Otherwise, it uses the same technique I’ve shown before: the command j (aka image) is used to draw the scalar image of the lightness directly in the first channel of the Lab image.
    Be sure your lightness image is always a single channel one, otherwise you’ll need to use channel 0 or something similar to constrain it having a single channel.

Because the usual Lab colorspace has a range for the lightness which is [0,100] instead.

Hi David,
Thanks to your invaluable help I am able to replace a part of my workflow for which I formerly relied on Photoshop. Following is my minor mod to your command along with my commentary as to my understanding what each component is doing.

gmic Ferniegair-GE3D-RGB.tif Ferniegair-GE3D-B8.tif div 257 rgb2lab8[0] j[0] [1] rm[1] lab82srgb mul 257 round o output16d.tiff

Ferniegair-GE3D-RGB.tif # read in 1st image (color)
Ferniegair-GE3D-B8.tif  # read in 2nd image (gray for L channel)
div 257                 # divide all pixel values in both images by 257 to remap from range 0-65535 to range 0.0 - 255.0
rgb2lab8[0]             # convert the 1st image from RGB to LAB8 mode 
j[0] [1]                # Image command (not certain of) to draw the 2nd image onto the 1st image
rm[1]                   # remove the 2nd image from the list 
lab82srgb               # convert the remaining image from LAB8 to SRGB 
mul 257                 # multiply each pixel value by 257 to get back to the 0-65535 value range 
round                   # Assuming that round insures all values in the 0-65535 range are integer values 
o output16rgbl.tiff     # output final image

What I don’t understand is the image command (j) as I am unclear what it means to draw the b/w image onto the rgb image. Someone used to working with layers would conclude
that the result would be a b/w image. I am assuming that G’MIC sees the b/w image as having only one channel and simple “draws” that on top of the 1st channel of the LAB image, thus replacing that image’s L channel only since that is the 1st channel. I infer this to mean that if the second image had 3 channels, then drawing it onto the 1st image would have resulted in the 1st image becoming just a copy of the 2nd image.

Once again, thank you.

2 Likes

The image command replaces part of the original image with the image given as an argument. Since the image given as an argument has only a single channel, the part replaced in the original image is only replaced on one channel as well (in this case the luminance channel).
Here, you copy a smaller image into a larger image. But the smaller dimension is not in width or height (which are identical), but in number of channels. But the idea is the same: you copy a table of values into a larger table of values.

The call to j[0] [1] is actually equivalent to j[0] [1],0,0,0,0 (as the default values for the other arguments are 0). If you want to copy your single-channel image in the a channel for instance, rather than on the L channel, then you would have to write j[0] [1],0,0,0,1.

2 Likes

What is _max_opacity_mask?

That’s the value defined in [sprite_mask] for which the opacity of the drawing will be 100%. Usually, its value is chosen to be 1 (default) or 255 (when dealing with alpha channels from RGBA images for instance).

Hi David,

Ah, so the ‘c’ in the ref doc stands for the channel to replace in the copy-to or target image.
So I can assume that the x,y,z,c parms all apply to the target image.

Therefore j[0] [1],100,150,0,0 means to copy from image ‘1’ to image ‘0’ but use as the upper left hand corner for image ‘0’ the x,y position 100,150. Is this a correct read on my part?

In this situation I understand the channel aspect: the source has only a single channel so will target a specific channel in the destination. From this I take it that the ‘c’ operation only works with a grayscale image as input, yes?

Thanks again.

1 Like

Makes sense. Is this parameter only available with image and not other blend commands?

AFAIK, only for image because that’s the only command which takes an opacity mask as an argument.

That is correct yes.

No, it works with any number of channels, just like for the other dimensions.
For instance, you can draw a 2-channels image into a 3-channels image :

gmic sp lena,tiger channels. 0,1 j.. .,0,0,0,1

I am actually referring to opacity in general as it is typically [0,1] in G’MIC lingo but [0,255] in GUI-oriented apps like GIMP and Krita, which causes some confusion. A max parameter might help (or make things even more confusing :sweat_smile:).