Python Pillow(PIL) Not completely recoloring gradients?

Question:

I am having an issue where I’m using Pillow to recolor an image that has a lot of soft gradients but it seems to not completely color the most translucent part of these gradients, with the recolored image having a gradient that is not as smooth. Is there a way to fix this issue? Example Images and current code below.

enter image description here

Original Gradient: 1: https://i.stack.imgur.com/VFi75.png

enter image description here

Recolored Gradient: 1: https://i.stack.imgur.com/e5iNa.png

Here is the Original transparent PNG of the image

import random
import Owl_Attributes
from PIL import Image, ImageColor

# I create the image here and convert the color code to RGBA
RGB_im = image_base_accent3.convert("RGBA")

datas = RGB_im.getdata()


newData = []

for item in datas:
    if item[0] == 208 and item[1] == 231 and item[2] == 161:
        newData.append((255, 0, 0, item[3]))
    else:
        newData.append(item)
RGB_im.putdata(newData)

RGB_im.save('Owl_project_pictures_final_RGB.png')
Asked By: Jimmy Seifert

||

Answers:

First, a couple of things to consider:

  • Inspect your images before you start work. Yours has an alpha channel that is pretty much pointless and irrelevant so I would discard that to save space and processing time.

  • Using for loops over Python lists of pixels is slow, inefficient, and error-prone in Python. Try to use built-in functions based on C code, or to use vectorised functions like Numpy.

On to your image. There are a whole load of shades and gradations of tone in your image and dealing with one separately through if statements is going to be difficult. I would suggest you want to use HSV colourspace instead.

I think you want the basic result to be a very saturated red with the lightness dictated by the lightness of the original image.

So, I would make an image with:

  • Hue=0 (see lower part of this diagram), and
  • Saturation=255 (i.e. fully saturated), and
  • Value (i.e. brightness) of the original image.

In code that might look like this:

#!/usr/bin/env python3

# ImageMagick command-line "equivalent"
# magick -size 599x452 xc:black xc:white ( VFi75.png -colorspace gray +level 0,60% ) +combine HSL result.png
    
from PIL import Image
    
# Load image and create HSV version
im  = Image.open('VFi75.png')
HSV = im.convert('HSV')

# Split into separate channels for processing, discarding Hue and Saturation
_, _, V = HSV.split()

# Synthesize Hue channel, same size as input image, filled with 0, to make Red
H = Image.new('L', (im.width, im.height), 0)

# Synthesize Saturation channel, same size as input image, filled with 255, to make fully saturated
S = Image.new('L', (im.width, im.height), 255)

# Recombine synthesized H, S and V (based on original image brightness) back into a recombined image
RGB = Image.merge('HSV', (H,S,V)).convert('RGB')

# Save processed result
RGB.save('result.png')

enter image description here


If you wanted to make it lime green, you would change the Hue angle like this:

# Synthesize Hue channel, same size as input image, filled with 120, to make Lime Green
H = Image.new('L', (im.width, im.height), 120)

enter image description here


If you wanted to make it less saturated, you would change the saturation like this:

# Synthesize Saturation channel, same size as input image, filled with 64, to make less saturated
S = Image.new('L', (im.width, im.height), 64)

enter image description here

Answered By: Mark Setchell