Search code examples
pythonnumpyrgbhsl

Convert RGB array to HSL


A disclaimer first, I'm not very skilled in Python, you guys have my admiration.

My problem: I need to generate 10k+ images from templates (128px by 128px) with various hues and luminances.

I load the images and turn them into arrays

image = Image.open(dir + "/" + file).convert('RGBA')
arr=np.array(np.asarray(image).astype('float'))

From what I can understand, handling numpy arrays in this fashion is much faster than looping over every pixels and using colorsys.

Now, I've stumbled upon a couple functions to convert rgb to hsv. This helped me generate my images with different hues, but I also need to play with the brightness so that some can be black, and others white.

def rgb_to_hsv(rgb):
    # Translated from source of colorsys.rgb_to_hsv
    hsv=np.empty_like(rgb)
    hsv[...,3:]=rgb[...,3:]
    r,g,b=rgb[...,0],rgb[...,1],rgb[...,2]
    maxc = np.max(rgb[...,:2],axis=-1)
    minc = np.min(rgb[...,:2],axis=-1)    
    hsv[...,2] = maxc   
    hsv[...,1] = (maxc-minc) / maxc
    rc = (maxc-r) / (maxc-minc)
    gc = (maxc-g) / (maxc-minc)
    bc = (maxc-b) / (maxc-minc)
    hsv[...,0] = np.select([r==maxc,g==maxc],[bc-gc,2.0+rc-bc],default=4.0+gc-rc)
    hsv[...,0] = (hsv[...,0]/6.0) % 1.0
    idx=(minc == maxc)
    hsv[...,0][idx]=0.0
    hsv[...,1][idx]=0.0
    return hsv

def hsv_to_rgb(hsv):
    # Translated from source of colorsys.hsv_to_rgb
    rgb=np.empty_like(hsv)
    rgb[...,3:]=hsv[...,3:]    
    h,s,v=hsv[...,0],hsv[...,1],hsv[...,2]   
    i = (h*6.0).astype('uint8')
    f = (h*6.0) - i
    p = v*(1.0 - s)
    q = v*(1.0 - s*f)
    t = v*(1.0 - s*(1.0-f))
    i = i%6
    conditions=[s==0.0,i==1,i==2,i==3,i==4,i==5]
    rgb[...,0]=np.select(conditions,[v,q,p,p,t,v],default=v)
    rgb[...,1]=np.select(conditions,[v,v,v,q,p,p],default=t)
    rgb[...,2]=np.select(conditions,[v,p,t,v,v,q],default=p) 
    return rgb

How easy is it to modify these functions to convert to and from HSL? Any trick to convert HSV to HSL?

Any info you can give me is greatly appreciated, thanks!


Solution

  • Yes, numpy, namely the vectorised code, can speed-up color conversions.

    The more, for massive production of 10k+ bitmaps, you may want to re-use a ready made professional conversion, or sub-class it, if it is not exactly matching your preferred Luminance model.

    a Computer Vision library OpenCV, currently available for python as a cv2 module, can take care of the colorsystem conversion without any additional coding just with:

    a ready-made conversion one-liner

    out = cv2.cvtColor(   anInputFRAME, cv2.COLOR_YUV2BGR ) # a bitmap conversion
    

    A list of some color-systems available in cv2 ( you may notice RGB to be referred to as BRG due to OpenCV convention of a different ordering of an image's Blue-Red-Green color-planes ),

    ( symmetry applies COLOR_YCR_CB2BGR <-|-> COLOR_BGR2YCR_CB not all pairs shown )

    >>> import cv2
    >>> for key in dir( cv2 ):                              # show all ready conversions
    ...     if key[:7] == 'COLOR_Y':
    ...         print key
    
    COLOR_YCR_CB2BGR
    COLOR_YCR_CB2RGB
    COLOR_YUV2BGR
    COLOR_YUV2BGRA_I420
    COLOR_YUV2BGRA_IYUV
    COLOR_YUV2BGRA_NV12
    COLOR_YUV2BGRA_NV21
    COLOR_YUV2BGRA_UYNV
    COLOR_YUV2BGRA_UYVY
    COLOR_YUV2BGRA_Y422
    COLOR_YUV2BGRA_YUNV
    COLOR_YUV2BGRA_YUY2
    COLOR_YUV2BGRA_YUYV
    COLOR_YUV2BGRA_YV12
    COLOR_YUV2BGRA_YVYU
    COLOR_YUV2BGR_I420
    COLOR_YUV2BGR_IYUV
    COLOR_YUV2BGR_NV12
    COLOR_YUV2BGR_NV21
    COLOR_YUV2BGR_UYNV
    COLOR_YUV2BGR_UYVY
    COLOR_YUV2BGR_Y422
    COLOR_YUV2BGR_YUNV
    COLOR_YUV2BGR_YUY2
    COLOR_YUV2BGR_YUYV
    COLOR_YUV2BGR_YV12
    COLOR_YUV2BGR_YVYU
    COLOR_YUV2GRAY_420
    COLOR_YUV2GRAY_I420
    COLOR_YUV2GRAY_IYUV
    COLOR_YUV2GRAY_NV12
    COLOR_YUV2GRAY_NV21
    COLOR_YUV2GRAY_UYNV
    COLOR_YUV2GRAY_UYVY
    COLOR_YUV2GRAY_Y422
    COLOR_YUV2GRAY_YUNV
    COLOR_YUV2GRAY_YUY2
    COLOR_YUV2GRAY_YUYV
    COLOR_YUV2GRAY_YV12
    COLOR_YUV2GRAY_YVYU
    COLOR_YUV2RGB
    COLOR_YUV2RGBA_I420
    COLOR_YUV2RGBA_IYUV
    COLOR_YUV2RGBA_NV12
    COLOR_YUV2RGBA_NV21
    COLOR_YUV2RGBA_UYNV
    COLOR_YUV2RGBA_UYVY
    COLOR_YUV2RGBA_Y422
    COLOR_YUV2RGBA_YUNV
    COLOR_YUV2RGBA_YUY2
    COLOR_YUV2RGBA_YUYV
    COLOR_YUV2RGBA_YV12
    COLOR_YUV2RGBA_YVYU
    COLOR_YUV2RGB_I420
    COLOR_YUV2RGB_IYUV
    COLOR_YUV2RGB_NV12
    COLOR_YUV2RGB_NV21
    COLOR_YUV2RGB_UYNV
    COLOR_YUV2RGB_UYVY
    COLOR_YUV2RGB_Y422
    COLOR_YUV2RGB_YUNV
    COLOR_YUV2RGB_YUY2
    COLOR_YUV2RGB_YUYV
    COLOR_YUV2RGB_YV12
    COLOR_YUV2RGB_YVYU
    COLOR_YUV420P2BGR
    COLOR_YUV420P2BGRA
    COLOR_YUV420P2GRAY
    COLOR_YUV420P2RGB
    COLOR_YUV420P2RGBA
    COLOR_YUV420SP2BGR
    COLOR_YUV420SP2BGRA
    COLOR_YUV420SP2GRAY
    COLOR_YUV420SP2RGB
    COLOR_YUV420SP2RGBA
    

    I did some prototyping for Luminance conversions ( based on >>> http://en.wikipedia.org/wiki/HSL_and_HSV )

    But not tested for release.

    def        get_YUV_V_Cr_Rec601_BRG_frame( brgFRAME ):                   # For the Rec. 601 primaries used in gamma-corrected sRGB, fast, VECTORISED MUL/ADD CODE
        out =  numpy.zeros( brgFRAME.shape[0:2] )
        out += 0.615 / 255 * brgFRAME[:,:,1]    # // Red                    # normalise to <0.0 - 1.0> before vectorised MUL/ADD, saves [usec] ... on 480x640 [px] faster goes about 2.2 [msec] instead of 5.4 [msec]
        out -= 0.515 / 255 * brgFRAME[:,:,2]    # // Green
        out -= 0.100 / 255 * brgFRAME[:,:,0]    # // Blue                   # normalise to <0.0 - 1.0> before vectorised MUL/ADD
        return out