# Composite image of multiple channels

**URL:** <https://discourse.julialang.org/t/composite-image-of-multiple-channels/84066>\
**Category:** General Usage\
**Tags:** images\
**Created:** [July 11, 2022, 6:20pm UTC](https://discourse.julialang.org/t/composite-image-of-multiple-channels/84066 "2022-07-11T18:20:21Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![thompsonmj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thompsonmj/32/25172_2.png) [@thompsonmj](https://discourse.julialang.org/u/thompsonmj)\
**Post date:** [July 11, 2022, 6:20pm UTC](https://discourse.julialang.org/t/composite-image-of-multiple-channels/84066/1 "2022-07-11T18:20:21Z")

</div>

I have 3 images, each a separate channel of the same sample.

```julia
c1 = rand(100,100)
c2 = rand(100,100)
c3 = rand(100,100)

```

I’d like to make a custom color gradient for each channel:

```julia
using Plots
pal = palette(:batlow,3)
grad1 = cgrad([:black, pal[1]])
grad2 = cgrad([:black,pal[2]])
grad3 = cgrad([:black,pal[3]])

```

and plot them each separately and together in a composite image:

```julia
fig = plot(layout = grid(1,4),
    size=(2000,500),
    legend=false,
    colorbar=false,
    xticks=[],
    yticks=[],
    aspect_ratio=:equal,
    margin=5mm)

heatmap!(fig[1], c1, color=grad1)
heatmap!(fig[2], c2, color=grad2)
heatmap!(fig[3], c3, color=grad3)

```

 ![image](https://global.discourse-cdn.com/julialang/original/3X/0/d/0d9060579732ea81fd1cb2886b1c97f75271fd80.png)

But I’m not sure how to combine the channels to make the colored composite. Any tips?

---

<div class="post-metadata">

**Author:** ![empet](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/empet/32/221303_2.png) [@empet](https://discourse.julialang.org/u/empet)\
**Post date:** [July 11, 2022, 7:38pm UTC](https://discourse.julialang.org/t/composite-image-of-multiple-channels/84066/2 "2022-07-11T19:38:11Z")

</div>

An image of channels c1, c2, c3 can be defined as follows:

```julia
using Images
img= cat(c1, c2, c3; dims=3)
nimg= permutedims(img, (3, 1, 2))
img_rgb = colorview(RGB, nimg)

```

Is this the kind of image you intended to get?

---

<div class="post-metadata">

**Author:** ![thompsonmj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thompsonmj/32/25172_2.png) [@thompsonmj](https://discourse.julialang.org/u/thompsonmj)\
**Post date:** [July 11, 2022, 7:46pm UTC](https://discourse.julialang.org/t/composite-image-of-multiple-channels/84066/3 "2022-07-11T19:46:33Z")

</div>

Yes, I believe so, but how can I apply custom colorscales to each channel?

---

<div class="post-metadata">

**Author:** ![empet](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/empet/32/221303_2.png) [@empet](https://discourse.julialang.org/u/empet)\
**Post date:** [July 11, 2022, 8:03pm UTC](https://discourse.julialang.org/t/composite-image-of-multiple-channels/84066/4 "2022-07-11T20:03:57Z")

</div>

You have already defined the three heatmaps by mapping the values in c1, c2, respectively c3, to gradients.

---

<div class="post-metadata">

**Author:** ![stillyslalom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stillyslalom/32/45687_2.png) [@stillyslalom](https://discourse.julialang.org/u/stillyslalom)\
**Post date:** [July 11, 2022, 9:15pm UTC](https://discourse.julialang.org/t/composite-image-of-multiple-channels/84066/5 "2022-07-11T21:15:07Z")

</div>

Is there any reason to not use red, green, and blue for each of the individual channels? Otherwise there’s not really a sensible way to add three channels together without resorting to a luminance-based colorspace like YCbCr.

---

<div class="post-metadata">

**Author:** ![thompsonmj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thompsonmj/32/25172_2.png) [@thompsonmj](https://discourse.julialang.org/u/thompsonmj)\
**Post date:** [July 11, 2022, 10:29pm UTC](https://discourse.julialang.org/t/composite-image-of-multiple-channels/84066/6 "2022-07-11T22:29:24Z")

</div>

Accessibility, there are great palettes available for optimized perception for those who have and don’t have perfect color vision, e.g. [this resource](https://www.fabiocrameri.ch/visualisation/)

---

<div class="post-metadata">

**Author:** ![stillyslalom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stillyslalom/32/45687_2.png) [@stillyslalom](https://discourse.julialang.org/u/stillyslalom)\
**Post date:** [July 11, 2022, 11:55pm UTC](https://discourse.julialang.org/t/composite-image-of-multiple-channels/84066/7 "2022-07-11T23:55:07Z")

</div>

Accessibility is a worthy consideration, but at the end of the day, colorblindness typically involves the absence of one of three types of color-perceiving cells, and you can’t project a space onto a lower-dimensional subspace without losing information. Below are the averages of pairwise combinations of your chosen color gradients ((grad1, grad2), (grad2, grad3), (grad1, grad3)) with the remaining gradient value held constant, and with the protanopic version right below:

 ![image](https://global.discourse-cdn.com/julialang/original/3X/f/f/fff14ecc7824ce48cf3514996c6c7e0e0bc4a4ed.jpeg)  
I don’t think that adds much perceptual value to a colorblind user, and the trichromatic majority are better-served by red/green/blue:  
 ![image](https://global.discourse-cdn.com/julialang/original/3X/e/2/e2358671f45928751b7e8a81dd4d1e85bfdbda22.jpeg)

---

<div class="post-metadata">

**Author:** ![thompsonmj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thompsonmj/32/25172_2.png) [@thompsonmj](https://discourse.julialang.org/u/thompsonmj)\
**Post date:** [July 12, 2022, 1:37am UTC](https://discourse.julialang.org/t/composite-image-of-multiple-channels/84066/8 "2022-07-12T01:37:24Z")

</div>

They’re in the gradients, but how do I get those gradients to display with `colorview`? When I use the snippet you shared, it simply makes it rainbow-colored.

---

<div class="post-metadata">

**Author:** ![empet](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/empet/32/221303_2.png) [@empet](https://discourse.julialang.org/u/empet)\
**Post date:** [July 12, 2022, 5:43am UTC](https://discourse.julialang.org/t/composite-image-of-multiple-channels/84066/9 "2022-07-12T05:43:31Z")

</div>

I suppose that you intend to get an image having in each pixel the corresponding color in the heatmap. But the ColorGradient grad1, for example, is just a 2-element Array{RGBA{Float64},1}. `heatmap` associates to each c1-value the corresponding color in the ColorGradient, resulted by interpolating the two end colors. However we cannot access the color codes (resulted by interpolation), to associate an image to the heatmap.

_When I use the snippet you shared, it simply makes it rainbow-colored_:  
I defined an image having as Red, Green, Blue components the values in c1, c2, respectively c3, as an answer to the vaguely formulated question: _But I’m not sure how to combine the channels to make the colored composite. Any tips?_

---

<div class="post-metadata">

**Author:** ![thompsonmj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thompsonmj/32/25172_2.png) [@thompsonmj](https://discourse.julialang.org/u/thompsonmj)\
**Post date:** [July 12, 2022, 4:21pm UTC](https://discourse.julialang.org/t/composite-image-of-multiple-channels/84066/10 "2022-07-12T16:21:16Z")

</div>

Yes, thanks for pointing out the ambiguity in the original question and for your time helping with this! … I’ll use an example with what I have in ImageJ to make it as concrete as possible. I think you summarized it well with “I suppose that you intend to get an image having in each pixel the corresponding color in the heatmap.”

I have two images with custom color mapping applied by making an interpolated LUT from black to the max RGB color value using ImageJ’s LUT Editor, similar to the gradient created with e.g. `cgrad(:black, pal[1])`:

 ![c1](https://global.discourse-cdn.com/julialang/original/3X/4/b/4b78ab495acce69660d0de3a082417c486015bfd.png)  
 ![c2](https://global.discourse-cdn.com/julialang/original/3X/0/4/04e2a0cf8ca6ae0d43c12009ba3a8913649c0afc.png)

And a composite made using the Merge Channels… tool:

 ![c1-c2-comp](https://global.discourse-cdn.com/julialang/original/3X/7/e/7ec7cd8b4038ca39aedcaed0360ccc12f9f4a979.jpeg)

(Have not settled on these colors as final choices)

How can I achieve a similar effect in Julia? That is, apply a custom LUT/gradient from black to a custom color choice (if not using `cgrad`, then is there some other way?) to each image, then blending the images (with ImageJ, it looks like simple RGB addition).

---

<div class="post-metadata">

**Author:** ![empet](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/empet/32/221303_2.png) [@empet](https://discourse.julialang.org/u/empet)\
**Post date:** [July 12, 2022, 6:15pm UTC](https://discourse.julialang.org/t/composite-image-of-multiple-channels/84066/11 "2022-07-12T18:15:28Z")

</div>

You can associate images to heatmaps defined by a matrix and a ColorGradient,  
as in your example, as follows:

If you have an arbitrary matrix A= -3 .+ 5\*rand(100, 100) for your heatmap  
and

```julia
Am, AM =extrema(A)

```

are its extrema, then the associated normalized matrix is:

```julia
nA =(A .- Am)/(AM-Am)

```

Hence I explain how to associate an image to a heatmap, working with normalized matrices:

```julia
nm1 = rand(100,100)
nm2 = rand(100,100)

```

Let us define img1, img2, the images representing the heatmaps associated to nm1, nm2 and respectively grad2, grad3 in your initial example.  
Then:

```julia
img1 = (1.0 .- nm1) .*grad2[1] .+ nm1 .*grad2[2]
img2 = (1.0 .- nm2) .*grad3[1] .+nm2 .* grad3[2]

```

A convex combination of the two images is a blended image, i.e.

```julia
blended_img = a *img1 .+ b* img2

```

where a, b \>=0, and a+b=1.  
The simplest example is

```julia
blended_image = 0.5 *img1 .+ 0.5*img2

```

For α-blending (compositing) see [https://en.wikipedia.org/wiki/Alpha\_compositing](https://en.wikipedia.org/wiki/Alpha_compositing), where  
the coeefficients, a, b in the above convex combination of images are:  
a= α₁(1- α₂)/ ( α₁(1- α₂) +α₂)  
b = α₂/( α₁(1- α₂) +α₂)  
and α₁ , α₂ are the α- parameters for img1, respectively img2.

---

<div class="post-metadata">

**Author:** ![thompsonmj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thompsonmj/32/25172_2.png) [@thompsonmj](https://discourse.julialang.org/u/thompsonmj)\
**Post date:** [July 12, 2022, 7:42pm UTC](https://discourse.julialang.org/t/composite-image-of-multiple-channels/84066/12 "2022-07-12T19:42:52Z")

</div>

Thank you! This is exactly what I was looking for.

Though on the img coefficients, it does not seem to be necessary that a+b=1:  
`blend = 1 * img1 .+ 1 * img2`

 ![image](https://global.discourse-cdn.com/julialang/original/3X/7/5/7547dc0ecfc7996521fe039751d38d089305432f.jpeg)

`blend = 2 * img1 .+ 2 * img2`

 ![image](https://global.discourse-cdn.com/julialang/original/3X/3/3/33e8faeff9b9c03d15440d9cee9e959ba70ae24c.jpeg)

if I have

```julia
α₁ = 1
α₂ = 1

```

that formula gives

```julia
a = 0.0
b = 1.0

```

For the purposes of this post though, scaling `a` and `b` to give a subjectively bright enough signal in the image for each channel does the trick.

---

<div class="post-metadata">

**Author:** ![empet](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/empet/32/221303_2.png) [@empet](https://discourse.julialang.org/u/empet)\
**Post date:** [July 13, 2022, 7:39am UTC](https://discourse.julialang.org/t/composite-image-of-multiple-channels/84066/13 "2022-07-13T07:39:02Z")

</div>

I haven’t tried yet `ColorBlendModes.jl`, and my suggestion to define a new image by a convex combination of the two images was influenced by Python, where a RGB color is an array, [r, g, b], with r, g, b ∈ [0,1]. A convex combination of two colors is also a color.  
Since your images, img1, img2 are of type RGBA{Float64}, you can add them simply, but I don’t know how can you interpret this operation from the point of view of images (not arrays).  
The convex combination for α-compositing is the right one, but it is non-trivial, for α₁ in (0,1] and α₂ in (0,1).  
Example:

```julia
using Images
img1 = load("foregr500.png")
img2 = load("backgr500.png")                    
@assert size(img1)==size(img2)
α₂= 0.3 # the parameter for the background image
α₁ = 1 # parameter for the foreground image 
#output_alpha= α₂+α₁*(1-α₂)=0.3+0.7=1
cimg = (α₂ * img2 .+ α₁*(1-α₂)* img1)/(α₂+α₁*(1-α₂))
#i.e. cimg = 0.3*img2 .+ 0.7*img1

```

![foregr500](https://global.discourse-cdn.com/julialang/original/3X/9/d/9d7e7f08642517e3177041d1a1c282c9e932c151.png)

![backgr500](https://global.discourse-cdn.com/julialang/original/3X/f/e/fe1949b346331c7139354ee8dbe509e8ffcb8ad5.png)

Their α-composition:

![alpha_blended](https://global.discourse-cdn.com/julialang/original/3X/b/6/b6e12c56a7197203b0a1b5fe7947904ca6855166.png)

The two images img1, img2 are derived from the netCDF files, provided here: [https://zenodo.org/record/4759091/files/GF\_FESOM2\_testdata.tar.gz](https://zenodo.org/record/4759091/files/GF_FESOM2_testdata.tar.gz), and read with xarray.  
LE: Note that α₁, α₂ do not represent the α-parameter of the involved RGBA images. Here both img1, img2 have the type:

```julia
typeof(img1), typeof(img2)
        
(Matrix{RGB{N0f8}}, Matrix{RGB{N0f8}})

```

Rather, these parameters define the proportion of contribution of each image to the output image.  
In the above example, α₁\*(1-α₂)/(α₂+α₁\*(1-α₂)), α₂/(α₂+α₁\*(1-α₂)) give the contribution of img1, respectively img2 to the output image, cimg.
