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
from colorspacious import cspace_converter
import s3dlib.surface as s3d

# 0. Define animation control parameters ............................

totalTime, f_domain, numFrames = 10, (0.0,1.0), 120   # time in seconds
frames=np.linspace(*f_domain, numFrames, endpoint=False)
interval = int(1000.0*totalTime/numFrames)          # milliseconds

# 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', aspect='equal', proj_type='ortho')
minmax, ticks, paneColor  = (-0.2,1.2) , [0,0.5,1], '.05'
ax.set(xlim=minmax, ylim=minmax, zlim=minmax,
        xticks=ticks, yticks=ticks, zticks=ticks )
ax.xaxis.set_pane_color(paneColor)
ax.yaxis.set_pane_color(paneColor)
ax.zaxis.set_pane_color(paneColor)
ax.view_init(30,-70)
ax.add_collection3d(surface)

fig.tight_layout(pad=0)
#plt.show()

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

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

    return

anim = FuncAnimation(fig, update_fig, frames, interval=interval, repeat=True)
anim.save('rgb_lab_hsv.html',writer='html')

msg = "saved {} frames, values: [{:.3f} to {:.3f}] @ {} milliseconds/frane"
print(msg.format(numFrames,np.min(frames),np.max(frames),interval))