【问题标题】:Colorize image while preserving transparency with PIL?在使用 PIL 保持透明度的同时为图像着色?
【发布时间】:2012-08-28 10:17:57
【问题描述】:

好的,情况如下:

我想使用 Python 图像库为这样的图像“主题化”:

主题颜色:"#33B5E5"

输入: 输出:

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

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


图片先转为黑白,再进行主题化。

我想通过 Python 图像库获得相同的结果。 但似乎我在使用它时遇到了一些问题:

  1. 无法处理透明度
  2. 背景(主图像中的透明度)也获得了主题..

我正在尝试使用这个脚本:

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)

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

很抱歉在一个问题中发布了这么多问题,但我无能为力:$

希望大家理解。

【问题讨论】:

  • 尝试使用Image.composite() 而不是Image.blend()
  • composite(image1, image2, mask),用什么做遮罩??
  • 您能否展示一个您希望从输入获得的输出的具体示例(仅一个或多个像素的输入和输出,包括 alpha,而不是图形)?我想我理解透明度问题,但不理解从一种颜色到另一种颜色的“主题图像”的意思。
  • 当然,我可以用 ImageMagick 做到这一点: IN: mupload.nl/img/olpiyj9is.png OUT: mupload.nl/img/fiaoq6gk5.png
  • 抱歉,虽然总体上有助于显示您想要发生的事情,但这两张图片(以及您问题中的两张)不足以准确确定它们之间的像素级别发生了什么- 需要对其进行描述,以确定 PIL 是否可以实现等效功能。也许如果您更新了您的问题并提供了有关您在 ImageMagick 中所做的详细信息...

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


【解决方案1】:

注意:此答案有一个 Python 3/pillow fork 的 PIL 版本here

更新 4:猜猜我的答案的上一个更新毕竟不是最后一个。尽管将其转换为专门使用 PIL 是一项重大改进,但有几件事似乎应该有更好、更不尴尬的方法,只要 PIL 有能力。

好吧,在仔细阅读了文档以及一些源代码之后,我意识到我想做的事情实际上是可能的。权衡是现在它必须手动构建查找表,因此整体代码略长。然而结果是它只需要调用一个相对慢的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 层的,下面是类似的,没有一层。

【讨论】:

  • 嘿 :) 我回家后会试试这个.. 但我也需要用它为其他图像设置主题,所以每次的亮度都不一样......提前谢谢,我会试试这个:)
  • 尽管它对您来说可能看起来像什么,但其中没有针对任何特定亮度的硬编码。使用的常量是用于从 RGB 转换为灰度的标准 NTSC 公式的一部分。
  • 确认从每种颜色到每种颜色的工作。万分感谢!!咻!
  • 还有一件事:如果图像中没有 alpha 怎么办。它会报错:“ValueError: need more than 3 values to unpack”。
  • @NaitikSoni:Python 2 vs 3 问题。您需要将每个 map(…) 调用显式转换为 list(map(…))
【解决方案2】:

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

  1. 使用Image.split()获取原始alpha层
  2. 转换为灰度
  3. 使用ImageOps.colorize着色
  4. 放回原来的 alpha 层

结果代码:

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

【讨论】:

  • 阿尔法放回工作谢谢 :) 但是,在使用你的脚本 2 次后,图像已经完全变黑了......有什么建议吗?如果它之后工作,我会给你“工作解决方案”复选框:)
  • 我想补充一点,我认为灰度是问题所在.. 每次都变暗... 还是谢谢
  • 您可以使用gray = ImageOps.autocontrast(gray)调整灰度后的亮度。我注意到您附加的android图片也变暗了,那是因为有些区域非常亮(rgb部分)在图像底部具有透明 alpha 的像素。这使得 colorize 使用那里最亮的颜色,并且因为它被 alpha“掩盖”,所以其他一切都变暗了..我会修复它..
  • 虽然这不是我的最终解决方案,但我要感谢你的解决方案 :)
  • 我认为您误解了为什么 colorize() 不能很好地工作,因为我在灰度图像上使用 multiply() 得到了类似的结果,它不做任何遮罩。我读过的解决方案是通过按旧颜色的光度(表观亮度)和新颜色的比率缩放着色结果来保持光度,以补偿否则发生的表观变暗。由于在逐个像素的基础上执行此操作相当昂贵,因此您只需使用白色的亮度与浅色的亮度之比即可。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-01-12
  • 1970-01-01
  • 2014-11-22
  • 2018-12-01
  • 1970-01-01
  • 2011-05-11
  • 1970-01-01
相关资源
最近更新 更多