Search code examples
pythonpython-3.xpython-imaging-library

How to apply a color mask to an image in PIL, python?


I have an image created in the PIL library and there is a second image.

enter image description here

I need to take the second image, make this cross, for example red, and apply it to the overall outline. How to correctly replace the color, but not directly (FFF to F00), but leaving shadows and a three-dimensional effect?


Solution

  • Here are the steps:

    • load your image
    • call ImageOps.colorize(..., white='red') to make image vary from black through to red, rather than from black through to white
    • create background image with Image.new()
    • paste onto background with paste(..., mask=originalImage)- the mask ensures you only paste onto the background where the original image is opaque and not where it is transparent

    Like this:

    from PIL import Image, ImageOps
    
    # Load image
    im = Image.open('cross.png')
    
    # Colorize
    red = ImageOps.colorize(im.convert('L'), black='black', white='red')
    red.save('DEBUG-red.png')
    
    # Make solid white background, same size as cross
    bg = Image.new('RGB', size=im.size, color='white')
    
    # Paste
    bg.paste(red, mask=im)
    

    DEBUG-red.png

    enter image description here

    result.png

    enter image description here


    Here's a slightly different approach leveraging HSV colourspace:

    • make a single channel image to use as Hue, setting it to a Hue angle of zero, i.e. red
    • make a single channel image to use as Saturation, set it to 50% so it is not too vivid
    • get brightness of original cross image to use as Value channel
    • merge HSV channels and paste as before

    from PIL import Image, ImageOps
    im = Image.open('cross.png')
    brightness = im.convert('L')
    # Set Hue angle to 0 degrees, i.e. red
    Hue = Image.new('L', size=im.size, color=0)
    # Set Saturation to 50%, not too vivid
    Sat = Image.new('L', size=im.size, color=128)
    coloured = Image.merge('HSV', (Hue, Sat, brightness)).convert('RGB')
    coloured.save('DEBUG-coloured.png')
    bg = Image.new('RGB', size=im.size, color='white')
    bg.paste(coloured, mask =im)
    bg.save('result.png')
    

    enter image description here

    Or making a desaturated blue - change 128 to 255 to make vivid and saturated:

    from PIL import Image, ImageOps
    im = Image.open('cross.png')
    brightness = im.convert('L')
    # Set Hue angle to 240 degrees scaled to unsigned char range
    Hue = Image.new('L', size=im.size, color=int(240*255/360))
    # Set Saturation to 50%, not too vivid
    Sat = Image.new('L', size=im.size, color=128)
    coloured = Image.merge('HSV', (Hue, Sat, brightness)).convert('RGB')
    coloured.save('DEBUG-coloured.png')
    bg = Image.new('RGB', size=im.size, color='white')
    bg.paste(coloured, mask =im)
    bg.save('result.png')
    

    enter image description here