RGB to Lab to HSV

../../_images/anim_rgb_lab_hsv.png

Animation control:

Visualization Frame Value
Surface geometry functional parameter per frame
Surface position constant
Surface color constant
Shading and highlighting fixed to the coordinate axis
Axis coordinate constant

Based on the static plot from the Color Space example.

This animation is three simple transforms between RGB, Lab and HSV color coordinates. Considering the RGB to Lab transform, define XRGB as the initial RGB coordinate and XLab as the final Lab coordinate. The intermediate coordinate is linearly interpolated as :

X(β) = XRGB + β( XLab - XRGB )

where β is in the domain from 0 to 1. The difference between initial and final coordinates is the same during the animation. Define this difference as :

Δ = XLab - XRGB

so that the intermediate coordinate is simply a linear function of β, ie:

X(β) = XRGB + βΔ

The other two transforms, Lab to HSV, and HSV to RGB, are taken to be the same form. These equations are highlighted in the following code. Notice that the intermediateCoor function does not use the input xyz argument, only the passed f value is used. The coordinates change with each frame, not the face colors. To ‘slow-down’ the transitions near the RGB, Lab and HSV surfaces, a cosine function was used for β with respect to the frame, as defined in the get_beta_type function.

import copy
import numpy as np
from matplotlib import pyplot as plt
import matplotlib.colors as colors
from matplotlib.animation import FuncAnimation
import matplotlib.animation as animation
from colorspacious import cspace_converter
import s3dlib.surface as s3d

# 1. Define function to examine ...............

xyzCoor,       labCoor,       hsvCoor       = None, None, None
delta_XYZ_Lab, delta_Lab_HSV, delta_HSV_XYZ = None, None, None

def intermediateCoor(xyz, f) :
    def get_beta_type(f) :
        t = int(3*f)
        rng = 3*f - t
        beta = 0.5*(1-np.cos(np.pi*rng))
        return beta, t
    beta, t = get_beta_type(f)
    if t == 1 : return labCoor + beta*delta_Lab_HSV  
    if t == 2 : return hsvCoor + beta*delta_HSV_XYZ
    return             xyzCoor + beta*delta_XYZ_Lab

def rgb_to_labCoor(rgb):
    L,a,b = cspace_converter("sRGB1", "CAM02-UCS"  )(rgb.T).T
    return a/80,b/80,L/100

def rgb_to_hsvCoor(rgb) :
    h,s,v = colors.rgb_to_hsv(rgb.T).T
    rtz = [ s/2, 2*np.pi*h, v  ]
    return s3d.CylindricalSurface.coor_convert(rtz,True)

# 2. Setup and map surface .........................................

base = s3d.CubicSurface().domain([0,1],[0,1],[0,1]).triangulate(5)
base.map_color_from_op(lambda xyz : xyz)
xyzCoor = base.vertices

labSurf = copy.copy(base).map_geom_from_op(rgb_to_labCoor)
labSurf.transform(translate=[0.5,0.5,0])
labCoor = labSurf.vertices

hsvSurf = copy.copy(base).map_geom_from_op(rgb_to_hsvCoor)
hsvSurf.transform(translate=[0.5,0.5,0])
hsvCoor = hsvSurf.vertices

delta_XYZ_Lab = np.subtract(labCoor,xyzCoor)
delta_Lab_HSV = np.subtract(hsvCoor,labCoor)
delta_HSV_XYZ = np.subtract(xyzCoor,hsvCoor)

frame = 0.0
surface = copy.copy(base)
surface.map_geom_from_op( lambda xyz : intermediateCoor(xyz, frame) ).shade(.6)

# 3. Construct figure, add surface plot ............................

fig = plt.figure(figsize=plt.figaspect(1))
ax = plt.axes(projection='3d',facecolor='k')
minmax, rgb_ticks, paneColor  = (-0.2,1.2) , [0,0.5,1], [0.2,0.2,0.2]
ax.set_proj_type('ortho')
ax.set(xlim=minmax, ylim=minmax, zlim=minmax )
ax.view_init(30,-70)
ax.set_xticks([0,0.5,1])
ax.set_yticks([0,0.5,1])
ax.set_zticks([0,0.5,1])
ax.w_xaxis.line.set_color(paneColor)
ax.w_yaxis.line.set_color(paneColor)
ax.w_zaxis.line.set_color(paneColor)
ax.w_xaxis.set_pane_color(paneColor)
ax.w_yaxis.set_pane_color(paneColor)
ax.w_zaxis.set_pane_color(paneColor)

ax.add_collection3d(surface)

fig.tight_layout(pad=0)

# 4. Animation ......................................................

def init_fig():
    return surface,

def update_fig(frame):
    global surface

    ax.collections.remove(surface)

    surface = copy.copy(base)
    surface.map_geom_from_op( lambda xyz : intermediateCoor(xyz, frame) ).shade(.6)
    ax.add_collection3d(surface)

    return surface,

ani = FuncAnimation(fig, update_fig, frames=np.linspace(0.0, 1.0, 121),
                    init_func=init_fig, blit=False, repeat=True, interval=50)

print(">>>>>>>>>>>>>>> Animation completed, file save proceeds")
#ani.save('ZZZ.mp4')                                   # use for movie file.
ani.save(None,writer=animation.FFMpegFileWriter())    # use for temp files.
print(">>>>>>>>>>>>>>> Save completed, screen display proceeds")

plt.show()