Search code examples
pythoncolorsrgbpython-3.5

Python- Is there a function or formula to find the complementary colour of a rgb code?


I have tried to find a good formula in Python 3 to calculate the complementary colour of a rgb code eg. complementary of a = b. Is there any way to do this?


Solution

  • Here's how to calculate the complement of an RGB colour directly. It gives the same results as the algorithm using colorsys as shown in Iva Klass's answer, but in my tests it's about 50% faster. Note that it works for any RGB scheme, it doesn't matter whether the RGB components are integers or floats (as long as each component uses the same range!).

    The function hilo implements a simple sorting network to sort the RGB components.

    # Sum of the min & max of (a, b, c)
    def hilo(a, b, c):
        if c < b: b, c = c, b
        if b < a: a, b = b, a
        if c < b: b, c = c, b
        return a + c
    
    def complement(r, g, b):
        k = hilo(r, g, b)
        return tuple(k - u for u in (r, g, b))
    

    Here's a short demo, using PIL / Pillow.

    #!/usr/bin/env python3
    
    ''' Complement the colours in a RGB image 
    
        Written by PM 2Ring 2016.10.08
    '''
    
    import sys
    from PIL import Image
    
    # Sum of the min & max of (a, b, c)
    def hilo(a, b, c):
        if c < b: b, c = c, b
        if b < a: a, b = b, a
        if c < b: b, c = c, b
        return a + c
    
    def complement(r, g, b):
        k = hilo(r, g, b)
        return tuple(k - u for u in (r, g, b))
    
    def complement_image(iname, oname):
        print('Loading', iname)
        img = Image.open(iname)
        #img.show()
    
        size = img.size
        mode = img.mode
        in_data = img.getdata()
    
        print('Complementing...')
        out_img = Image.new(mode, size)
        out_img.putdata([complement(*rgb) for rgb in in_data])
        out_img.show()
        out_img.save(oname)
        print('Saved to', oname)
    
    def main():
        if len(sys.argv) == 3:
            complement_image(*sys.argv[1:])
        else:
            fmt = 'Complement colours.\nUsage: {} input_image output_image'
            print(fmt.format(sys.argv[0]))
    
    if __name__ == '__main__':
        main()
    

    Input image

    source image

    Output image

    output image


    Here's a Numpy version of complement_image. On my machine it processes the "Glasses" image about 3.7 times faster than the previous version.

    import numpy as np
    
    def complement_image(iname, oname):
        print('Loading', iname)
        img = Image.open(iname)
        #img.show()
    
        in_data = np.asarray(img)
        #print(in_data.shape)
    
        print('Complementing...')
        lo = np.amin(in_data, axis=2, keepdims=True)
        hi = np.amax(in_data, axis=2, keepdims=True)
        out_data = (lo + hi) - in_data
    
        out_img = Image.fromarray(out_data)
        #out_img.show()
        out_img.save(oname)
        print('Saved to', oname)
    

    Here's a brief demo using scikit-image (and Numpy) to create complementary colours in the more perceptually uniform CIELuv colourspace.

    from skimage.color import rgb2luv, luv2rgb
    from skimage.util import img_as_ubyte
    
    luv_data = rgb2luv(in_data) * (1, -1, -1)
    out_data = img_as_ubyte(luv2rgb(luv_data))
    

    CIELuv complement