Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a discrete RGB colourmap with N colours using numpy

I am trying to create a really simple colourmap using RGB triples. That has a discrete number of colours.

Here is the shape that I am trying to make it follow:

enter image description here

I know it is probably not a true RGB spectrum, but i wanted it to be as easy to compute as possible.

I want to be able to generate an array of N 8-bit RGB tuples that are evenly spaced along the line.

For easy instances where all of the points in the image above fall on colours, it is easy to generate using numpy.linspace() using the following code:

import numpy as np

# number of discrete colours
N = 9

# generate R array
R = np.zeros(N).astype(np.uint8)
R[:int(N/4)] = 255
R[int(N/4):int(2*N/4)+1] = np.linspace(255,0,num=(N/4)+1,endpoint=True)

# generate G array
G = 255*np.ones(N).astype(np.uint8)
G[0:int(N/4)+1] = np.linspace(0,255,num=(N/4)+1,endpoint=True)
G[int(3*N/4):] = np.linspace(255,0,num=(N/4)+1,endpoint=True)

# generate B array
B = np.zeros(N).astype(np.uint8)
B[int(2*N/4):int(3*N/4)+1] = np.linspace(0,255,num=(N/4)+1,endpoint=True)
B[int(3*N/4)+1:] = 255

# stack arrays
RGB = np.dstack((R,G,B))[0]

This code works fine for 5 colours:

r   255 255 0   0   0
g   0   255 255 255 0
b   0   0   0   255 255

enter image description here

9 colours:

r   255 255 255 127 0   0   0   0   0
g   0   127 255 255 255 255 255 127 0
b   0   0   0   0   0   127 255 255 255

enter image description here

13 colours:

r   255 255 255 255 170 85  0   0   0   0   0   0   0
g   0   85  170 255 255 255 255 255 255 255 170 85  0
b   0   0   0   0   0   0   0   85  170 255 255 255 255

enter image description here

etc.. but I'm having trouble working out how to make it work for an arbitrary N number of colours because the linspace trick only works if it is going from one endpoint to another.

Can someone please help me with working out how to do it? Any ideas for how to make my code above more efficient would be great as well, I am just learning how to use numpy after putting it off for ages..

like image 347
guskenny83 Avatar asked Jun 22 '18 04:06

guskenny83


2 Answers

Here is one reasonably convenient method using np.clip:

def spec(N):                                             
    t = np.linspace(-510, 510, N)                                              
    return np.round(np.clip(np.stack([-t, 510-np.abs(t), t], axis=1), 0, 255)).astype(np.uint8)

It avoids the problem you describe by only relying on the only two points that are guaranteed to be on the grid for any N which are the first and last points.

Examples:

>>> spec(5)
array([[255,   0,   0],
       [255, 255,   0],
       [  0, 255,   0],
       [  0, 255, 255],
       [  0,   0, 255]], dtype=uint8)
>>> spec(10)
array([[255,   0,   0],
       [255, 113,   0],
       [255, 227,   0],
       [170, 255,   0],
       [ 57, 255,   0],
       [  0, 255,  57],
       [  0, 255, 170],
       [  0, 227, 255],
       [  0, 113, 255],
       [  0,   0, 255]], dtype=uint8)
like image 177
Paul Panzer Avatar answered Sep 29 '22 20:09

Paul Panzer


If you just want RGB at given levels, the diagram that you have posted itself serves as the answer -

R = [255] * 256
R.extend(list(reversed(range(256))))
R.extend([0] * 256)
R.extend([0] * 256)

G = list(range(256))
G.extend([255] * 256)
G.extend([255] * 256)
G.extend(list(reversed(range(256))))

B = [0] * 256
B.extend([0] * 256)
B.extend(list(range(256)))
B.extend([255] * 256)

level = 5
step = 1024 // (level -1)

print(R[::step])
print(G[::step])
print(B[::step])
like image 24
Bharath Avatar answered Sep 29 '22 20:09

Bharath