Reputation: 3385
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
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:
This technique is also used in perlin noise which allowed me to create this terrain generator.
Upvotes: 1