简体   繁体   English

使用 PIL 在保留透明度的同时对图像进行着色?

[英]Colorize image while preserving transparency with PIL?

Okay, here's the situation:好的,情况是这样的:

I want to use the Python Image Library to "theme" an image like this:我想使用 Python 图像库来“主题化”这样的图像:

Theme color:主题色:显示色调颜色的色板 "#33B5E5"

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

I got the result using this commands with ImageMagick:我通过 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.我想用 Python 图像库获得相同的结果。 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.希望大家理解。

Note : There's a Python 3/pillow fork of PIL version of this answer here .:有这个答案的PIL版本一个Python 3 /枕头叉这里

Update 4 : Guess the previous update to my answer wasn't the last one after all.更新 4 :猜猜对我的答案的上一次更新毕竟不是最后一次。 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.尽管将其转换为专门使用PIL是一项重大改进,但有一些事情似乎应该有更好的、不那么尴尬的方法,只要PIL有能力。

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.然而结果是它只需要调用一次相对较慢的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'

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.上面一行是带有 alpha 层的,下面一行是类似的,没有 alpha 层。

示例输入和输出图像显示有和没有 alpha 的图像结果

You need to convert to grayscale first.您需要先转换为灰度。 What I did:我做了什么:

  1. get original alpha layer using Image.split()使用Image.split()获取原始 alpha 层
  2. convert to grayscale转换为灰度
  3. colorize using ImageOps.colorize使用ImageOps.colorize着色
  4. put back original alpha layer放回原来的 alpha 层

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM