Martin
Martin

Reputation: 3385

Python - Creating colormap with RGB values defined by X,Y position

I am solving a problem, that I solved only half-way. I am working on 2D drawing space with X,Y coordinates. I want to set 4 points on the drawing space, set color to every point and generate colormap.

I was able to generate colormap with 2 points - create vector and get from one point to another. However this method creates in geometrical sense 2D plane/1D line over the points. But If set 4 points, it requires generating surface. So in other words I need to fit surface in the points.

This is how i imagine it:

This is my code for generating direct change from one X,Y,[RGB] to another X,Y,[RGB].

import numpy as np 
import colorsys
import cv2

a = np.array([100,0,0]) # Point one with x=0,y=0-max and rgb value
b =  np.array([0,255,0]) # point two with x=max,y=0-max and rgb value
#in this case i loop from y=0 to y=max for corresponding point on the other side of drawing space - that is x=max
a = a[::-1]
b= b[::-1]
leds = 31 # just constant
h_color=100 # height of drawing space
t_lengt = (600/leds)*leds #recalculation of width (because I need integer)
vector = (b-a)*1.0/t_lengt

arr_texture = np.zeros(shape=[h_color, t_lengt, 3], dtype=np.uint8) #drawing space defined by x,y and 3d value

for i in range(t_lengt): # loop for all points in x=0 to x=max (that is y=0 to max)
    for j in range(h_color):

        arr_texture[j][i]=[a[0]+vector[0]*i,a[1]+vector[1]*i,a[2]+vector[2]*i]


cv2.imwrite('color_img.jpg', arr_texture)
cv2.imshow("image", arr_texture);
cv2.waitKey(0)
cv2.destroyAllWindows()

result:

Also I am quite confused about the method, because the points on drawing space are defined by X,Y coordinates but they carry [R,G,B] values.

So to sum it up, I need to fit 3 to more points into creating colormap surface, where the points have X,Y coordinates but carry [R,G,B] values.

Thanks in advance

Upvotes: 3

Views: 3129

Answers (1)

Joe Iddon
Joe Iddon

Reputation: 20424

You know the RGB values at each of the four corners, so, to be able to create a colormap, all you need is a function that will take a coordinate (x,y) (where x and y are in the range 0 to 1) and return the RGB value at that coordinate.

To do this, we need to implement bilinear interpolation which is an extension of interpolating linearly to interpolating in 2d.


You perform bilinear interpolation by interpolating between the top two corners and then interpolating the result of this with the result of the interpolation between the bottom two corners.

a ----|---- b
      |
      |
c ----|-----d

So before we write our main function, we first need a helper to perform the interpolation as we will be using it nine times.

This could be linear:

def lerp(x, a, b):
    return a + x * (b-a)

or something more smooth, like the smoothstep function:

def serp(x, a, b):
    return a + (3*x**2 - 2*x**3) * (b-a)

(it turns out that for this case where we are just going to the corners (and not hills like with a perlin noise generator), linear produces a more gradual gradient!)


The function will take the four RGB values as four lists/numpy arrays of length 3 (a, b, c, d) and the coordinate (x, y) to return the RGB value at that coordinate.

def get_color(x, y, a, b, c, d):
    r = lerp(y, lerp(x, a[0], b[0]), lerp(x, c[0], d[0]))
    g = lerp(y, lerp(x, a[1], b[1]), lerp(x, c[1], d[1]))
    b = lerp(y, lerp(x, a[2], b[2]), lerp(x, c[2], d[2]))
    return np.array([r, g, b])

or, we could make it more Pythonic with a list-comp:

def get_color(x, y, a, b, c, d):
    return np.array([lerp(y, lerp(x, a[i], b[i]),
                             lerp(x, c[i], d[i])) for i in range(3)])

Now we just need to evaluate this over a numpy array which we can use np.meshgrid for (see this question for alternative methods).

Oh, and I will plot with matplotlib as I don't have OpenCV installed!

import matplotlib.pyplot as plt
import numpy as np
w = h = 200
verts = [[255,0,0],[0,255,0],[0,0,255],[255,0,0]]
img = np.empty((h,w,3), np.uint8)
for y in range(h):
    for x in range(w):
        img[y,x] = get_color(x/w, y/h, *verts)
plt.imshow(img)
plt.show()

which gives the following image:

colormap.jpg


This technique is also used in perlin noise which allowed me to create this terrain generator.

Upvotes: 1

Related Questions