使用PIL保持透明度的同时着色图像?

时间:2012-09-03 17:19:10

标签: python image transparency python-imaging-library colorize

好的,情况如下:

我想使用Python Image Library来“主题化”这样的图像:

主题颜色:swatch showing tint color "#33B5E5"

IN:http://mupload.nl/img/olpiyj9is.png OUT:http://mupload.nl/img/fiaoq6gk5.png

我使用ImageMagick的这个命令得到了结果:

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


图像首先转换为黑白,然后以主题为主。

我想用Python Image Library获得相同的结果。 但是从那以后我似乎遇到了一些问题:

  1. 无法处理透明度
  2. 背景(主图像的透明度)也是主题..
  3. 我正在尝试使用此脚本:

    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)
    

    你可以看到我没有先将其转换为灰度,因为这也不能用于透明度。

    我很遗憾在一个问题上发布了这么多问题,但我无法做任何其他事情:$

    希望大家都明白。

2 个答案:

答案 0 :(得分:8)

注意:这个答案here的PIL版本有一个Python 3 /枕头叉。

更新4 :猜测我的回答的上一次更新毕竟不是最后一次更新。虽然将其转换为仅使用PIL是一项重大改进,但如果只有PIL具备这种能力,那么有一些事情似乎应该更好,更不尴尬,可行。“ / p>

嗯,在仔细阅读了文档以及一些源代码之后,我意识到我想要做的事情实际上是。权衡的是现在它必须构建手动使用的查找表,因此整体代码稍长。但结果是它只需要调用一个相对较慢的Image.point()方法,而不是三个方法。

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'

这是一个屏幕截图,显示左侧的输入图像,右侧有相应的输出。上面的行是一个带有alpha层的,下面是一个没有alpha层的类型。

sample input and output images showing results of image with and without alpha

答案 1 :(得分:3)

您需要先转换为灰度。我做了什么:

  1. 使用Image.split()
  2. 获取原始alpha图层
  3. 转换为灰度
  4. 使用ImageOps.colorize
  5. 着色
  6. 放回原始alpha图层
  7. 结果代码:

    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")