Colorize image while preserving transparency with PIL?

点点圈 提交于 2019-12-17 19:09:28

问题


Okay, here's the situation:

I want to use the Python Image Library to "theme" an image like this:

Theme color:

"#33B5E5"

IN:

OUT:

I got the result using this commands with ImageMagick:

convert image.png -colorspace gray image.png
mogrify -fill "#33b5e5" -tint 100 image.png
Explanation:


The image is first converted to black-and-white, and then it is themed.

I want to get the same result with the Python Image Library. But it seems I'm having some problems using it since:

  1. Can not handle transparency
  2. Background (transparency in main image) gets themed too..

I'm trying to use this script:

import Image
import ImageEnhance

def image_overlay(src, color="#FFFFFF", alpha=0.5):
    overlay = Image.new(src.mode, src.size, color)
    bw_src = ImageEnhance.Color(src).enhance(0.0)
    return Image.blend(bw_src, overlay, alpha)

img = Image.open("image.png")
image_overlay(img, "#33b5e5", 0.5)

You can see I did not convert it to a grayscale first, because that didn't work with transparency either.

I'm sorry to post so many issues in one question, but I couldn't do anything else :$

Hope you all understand.


回答1:


Note: There's a Python 3/pillow fork of PIL version of this answer here.

Update 4: Guess the previous update to my answer wasn't the last one after all. Although converting it to use PIL exclusively was a major improvement, there were a couple of things that seemed like there ought to be better, less awkward, ways to do, if only PIL had the ability.

Well, after reading the documentation closely as well as some of the source code, I realized what I wanted to do was in fact possible. The trade-off was that now it has to build the look-up table used manually, so the overall code is slightly longer. However the result is that it only needs to make one call to the relatively slow Image.point() method, instead of three of them.

from PIL import Image
from PIL.ImageColor import getcolor, getrgb
from PIL.ImageOps import grayscale

def image_tint(src, tint='#ffffff'):
    if Image.isStringType(src):  # file path?
        src = Image.open(src)
    if src.mode not in ['RGB', 'RGBA']:
        raise TypeError('Unsupported source image mode: {}'.format(src.mode))
    src.load()

    tr, tg, tb = getrgb(tint)
    tl = getcolor(tint, "L")  # tint color's overall luminosity
    if not tl: tl = 1  # avoid division by zero
    tl = float(tl)  # compute luminosity preserving tint factors
    sr, sg, sb = map(lambda tv: tv/tl, (tr, tg, tb))  # per component adjustments

    # create look-up tables to map luminosity to adjusted tint
    # (using floating-point math only to compute table)
    luts = (map(lambda lr: int(lr*sr + 0.5), range(256)) +
            map(lambda lg: int(lg*sg + 0.5), range(256)) +
            map(lambda lb: int(lb*sb + 0.5), range(256)))
    l = grayscale(src)  # 8-bit luminosity version of whole image
    if Image.getmodebands(src.mode) < 4:
        merge_args = (src.mode, (l, l, l))  # for RGB verion of grayscale
    else:  # include copy of src image's alpha layer
        a = Image.new("L", src.size)
        a.putdata(src.getdata(3))
        merge_args = (src.mode, (l, l, l, a))  # for RGBA verion of grayscale
        luts += range(256)  # for 1:1 mapping of copied alpha values

    return Image.merge(*merge_args).point(luts)

if __name__ == '__main__':
    import os

    input_image_path = 'image1.png'
    print 'tinting "{}"'.format(input_image_path)

    root, ext = os.path.splitext(input_image_path)
    result_image_path = root+'_result'+ext

    print 'creating "{}"'.format(result_image_path)
    result = image_tint(input_image_path, '#33b5e5')
    if os.path.exists(result_image_path):  # delete any previous result file
        os.remove(result_image_path)
    result.save(result_image_path)  # file name's extension determines format

    print 'done'

Here's a screenshot showing input images on the left with corresponding outputs on the right. The upper row is for one with an alpha layer and the lower is a similar one that doesn't have one.




回答2:


You need to convert to grayscale first. What I did:

  1. get original alpha layer using Image.split()
  2. convert to grayscale
  3. colorize using ImageOps.colorize
  4. put back original alpha layer

Resulting code:

import Image
import ImageOps

def tint_image(src, color="#FFFFFF"):
    src.load()
    r, g, b, alpha = src.split()
    gray = ImageOps.grayscale(src)
    result = ImageOps.colorize(gray, (0, 0, 0, 0), color) 
    result.putalpha(alpha)
    return result

img = Image.open("image.png")
tinted = tint_image(img, "#33b5e5")


来源:https://stackoverflow.com/questions/12251896/colorize-image-while-preserving-transparency-with-pil

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!