How to convert a matrix to an RGB image using Images.jl?

Let’s say that I have a matrix x

x = [0 0.5 1; 0.6 0.7 08.; 1 0.1 0.2]

I can display it as a grayscale image using

Gray.(x)

How can I convert it to an RGB image using a specified color gradient?

Plots.jl allows me to do something similar with

Plots.heatmap(x, yflip=true, color=:inferno)

But I’d like the actual image rather than the plot of the image please.

2 Likes

What do you mean by " actual image rather than the plot of the image"? An image file on disk or the image array at the REPL?

You can get both with GMT. For example this

imshow(x, cmap="rainbow", fmt="png")

creates a GMTjl_tmp.png file in you temporary dir and displays it (you need ghostscript installed too).

I’d like an RGB image array (as per Images.jl) to be returned and displayed in IJulia.

The following works, but gives me a color image where ll the colours are gray

RGB.(x, x, x)

Ideally, I want to say something like

RGB(x, cmap=:inferno)

so that the colors are interpolated from a color gradient.

Thanks for your input, but I don’t really want to use ImageView (I’m in IJulia) and anyway, your example doesn’t work for me.

There is no ImageView involved in what I said. But you would need GMT and Ghostscript installed and that imshow is from GMT. To have it displayed on IJulia you would need something like

GMT.imshow(x, cmap="rainbow", fmt="png", show=false)

and than load the the /tmp/GMTjl_tmp.png (or the equivalent on Windows) from within IJulia.

2 Likes

You could use the applycolormap from the PerceptualColourMaps package, e.g.:

using TestImages
img = testimage("moonsurface") # a grayscale image
using PerceptualColourMaps
imgc = applycolormap(img, cmap("R3")) # outputs a 3-dimensional array
using Images
imgc2 = colorview(RGB, permuteddimsview(imgc, (3,1,2)))

which plots in my terminal using the nice TerminalExtensions package :slight_smile:
image

Cheers!

7 Likes

Wow! -that looks exactly what I want - thanks.

Unfortunately on my system (Linux) with julia version 0.6.1 I get

using PerceptualColourMaps

ERROR: LoadError: LoadError: UndefVarError: AbstractImage not defined
Stacktrace:

and

cmap("R3")

ERROR: UndefVarError: bbspline not defined

Perhaps I need to clone the bleeding edge stuff from Github? Or do I need Julia 0.7.x ?

Yes

Pkg.clone("https://github.com/peterkovesi/PerceptualColourMaps.jl")

and it works! many thanks. RB

@robblackwell consider using Plots.jl if your goal is only plotting a 2D matrix with a custom colormap.

using Plots

heatmap(img, cmap=:inferno)

These are all great suggestions. Just for the record, one other alternative is to use IndirectArrays, which can be used to lazily construct a colormap array. In this case you’d need to convert your Float64 array to an Int array, which you could do in a lazy way with MappedArrays. For example:

using MappedArrays, IndirectArrays

function color_me(A, cmap)
    n = length(cmap)
    f = s->clamp(round(Int, (n-1)*s)+1, 1, n)  # safely convert 0-1 to 1:n
    Ai = mappedarray(f, A)       # like f.(A) but does not allocate significant memory
    IndirectArray(Ai, cmap)      # colormap array
end

(For this to work you have to be running the very latest IndirectArrays.)

In most cases you may find PerceptualColourmap’s applycolormap easier, since it’s already written. Cases there might be reasons to consider IndirectArrays:

  • big data (e.g., >1TB image): the version here allocates essentially no memory and takes essentially no time (you pay only when you access the values, and then only for those values you need)
  • in applications where you’d like to pass the colorized array to a function but also have access to the original values (which you can get with parent(imgc.index)). An example might be a GUI “tooltip hover” application.

Again, these probably don’t matter in most cases. Not only is applycolormap already available, but @peterkovesi’s color maps are so well designed that you should just be using those anyway :slight_smile:.

7 Likes

Interesting. Thanks for taking the time to respond and for all the great work!

Sorry to resurrect this old thread but Prof. Edelman’s recent Julia video about Structure (Structure | Week3 | 18.S191 MIT Fall 2020) led me here.

A function show_image() is used in a Pluto notebook to display matrices as images in color, instead of using heatmap(). Could not find this function anywhere, until finding @tim.holy’s color_me() in post above.

In case it might be useful to others, please find herein a working example (at least on Win10 Julia 1.5.3 VS_Code):

using Plots, MappedArrays, IndirectArrays, PerceptualColourMaps

outer(v,w) = [x * y for x ∈ v, y ∈ w] # Prof. Edelman's: https://youtu.be/ConoBmjlivs
show_image(m) = plot(color_me(m,cmap("R3")))  # color_me() by @tim.holy

m = outer(rand(8),rand(12))
show_image(m)

Multiplication_table_of_randoms

6 Likes

After 6 months I also resurect this thread, because I’d like to use a colorscheme defined in ColorSchemes.jl, instead of a cmap from PerceptualColourMaps. I didn’t succeed with a colorscheme. How to access a colorscheme and pass it as an argument for color_me? Thanks!!

Meanwhile I succeeded to use a colorscheme as folows:

color_me(m,  ColorSchemes.balance.colors)
2 Likes

Here is another version of the function that scales the colormap linearly between the extrema of A:

    function color_me_scaleminmax(A, cmap)
        n = length(cmap)
        scale = takemap(scaleminmax, A)
        f = s->clamp(round(Int, (n-1)*scale(s))+1, 1, n)  # safely convert 0-1 to 1:n
        Ai = mappedarray(f, A)       # like f.(A) but does not allocate significant memory
        IndirectArray(Ai, cmap)      # colormap array
    end

    precompile(color_me_scaleminmax, (Matrix{Float64}, Vector{RGB{Float64}}))
2 Likes

@mkitti, could you confirm that takemap comes from using ImageCore.jl?

NB: and also comment on the use of precompile?

takemap is from ImageCore.jl. Folow the link.

I added the precompile statement to indicate what types I used with this function. It is also extracted from this code here:

(edit: I forgot to push and update the code)

If this piece of code is that popular, perhaps we should package it.

1 Like

Why use both IndirectArray and mappedarray here? Wouldn’t the code below work as well?

function color_me(A, clr_map)
    n = length(clr_map)
    f(s) = clr_map[clamp(round(Int, (n-1)*s)+1, 1, n)]
    Am = mappedarray(f, A)
    return of_eltype(eltype(clr_map), Am)
end

I guess it’s connected with this discussion, that is, MappedArrays are powerful.