Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/283.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 使用PIL将RGBA PNG转换为RGB_Python_Png_Jpeg_Python Imaging Library_Rgba - Fatal编程技术网

Python 使用PIL将RGBA PNG转换为RGB

Python 使用PIL将RGBA PNG转换为RGB,python,png,jpeg,python-imaging-library,rgba,Python,Png,Jpeg,Python Imaging Library,Rgba,我使用PIL将Django上传的透明PNG图像转换为JPG文件。输出看起来坏了 源文件 代码 或 结果 在这两种情况下,生成的图像如下所示: import Image import numpy as np FNAME = 'logo.png' img = Image.open(FNAME).convert('RGBA') x = np.array(img) r, g, b, a = np.rollaxis(x, axis = -1) r[a == 0] = 255 g[a == 0] = 2

我使用PIL将Django上传的透明PNG图像转换为JPG文件。输出看起来坏了

源文件

代码 或

结果 在这两种情况下,生成的图像如下所示:

import Image
import numpy as np

FNAME = 'logo.png'
img = Image.open(FNAME).convert('RGBA')
x = np.array(img)
r, g, b, a = np.rollaxis(x, axis = -1)
r[a == 0] = 255
g[a == 0] = 255
b[a == 0] = 255
x = np.dstack([r, g, b, a])
img = Image.fromarray(x, 'RGBA')
img.save('/tmp/out.jpg')

有办法解决这个问题吗?我想要白色的背景,以前透明的背景


解决方案 多亏了这些出色的答案,我想出了以下函数集合:

import Image
import numpy as np


def alpha_to_color(image, color=(255, 255, 255)):
    """Set all fully transparent pixels of an RGBA image to the specified color.
    This is a very simple solution that might leave over some ugly edges, due
    to semi-transparent areas. You should use alpha_composite_with color instead.

    Source: http://stackoverflow.com/a/9166671/284318

    Keyword Arguments:
    image -- PIL RGBA Image object
    color -- Tuple r, g, b (default 255, 255, 255)

    """ 
    x = np.array(image)
    r, g, b, a = np.rollaxis(x, axis=-1)
    r[a == 0] = color[0]
    g[a == 0] = color[1]
    b[a == 0] = color[2] 
    x = np.dstack([r, g, b, a])
    return Image.fromarray(x, 'RGBA')


def alpha_composite(front, back):
    """Alpha composite two RGBA images.

    Source: http://stackoverflow.com/a/9166671/284318

    Keyword Arguments:
    front -- PIL RGBA Image object
    back -- PIL RGBA Image object

    """
    front = np.asarray(front)
    back = np.asarray(back)
    result = np.empty(front.shape, dtype='float')
    alpha = np.index_exp[:, :, 3:]
    rgb = np.index_exp[:, :, :3]
    falpha = front[alpha] / 255.0
    balpha = back[alpha] / 255.0
    result[alpha] = falpha + balpha * (1 - falpha)
    old_setting = np.seterr(invalid='ignore')
    result[rgb] = (front[rgb] * falpha + back[rgb] * balpha * (1 - falpha)) / result[alpha]
    np.seterr(**old_setting)
    result[alpha] *= 255
    np.clip(result, 0, 255)
    # astype('uint8') maps np.nan and np.inf to 0
    result = result.astype('uint8')
    result = Image.fromarray(result, 'RGBA')
    return result


def alpha_composite_with_color(image, color=(255, 255, 255)):
    """Alpha composite an RGBA image with a single color image of the
    specified color and the same size as the original image.

    Keyword Arguments:
    image -- PIL RGBA Image object
    color -- Tuple r, g, b (default 255, 255, 255)

    """
    back = Image.new('RGBA', size=image.size, color=color + (255,))
    return alpha_composite(image, back)


def pure_pil_alpha_to_color_v1(image, color=(255, 255, 255)):
    """Alpha composite an RGBA Image with a specified color.

    NOTE: This version is much slower than the
    alpha_composite_with_color solution. Use it only if
    numpy is not available.

    Source: http://stackoverflow.com/a/9168169/284318

    Keyword Arguments:
    image -- PIL RGBA Image object
    color -- Tuple r, g, b (default 255, 255, 255)

    """ 
    def blend_value(back, front, a):
        return (front * a + back * (255 - a)) / 255

    def blend_rgba(back, front):
        result = [blend_value(back[i], front[i], front[3]) for i in (0, 1, 2)]
        return tuple(result + [255])

    im = image.copy()  # don't edit the reference directly
    p = im.load()  # load pixel array
    for y in range(im.size[1]):
        for x in range(im.size[0]):
            p[x, y] = blend_rgba(color + (255,), p[x, y])

    return im

def pure_pil_alpha_to_color_v2(image, color=(255, 255, 255)):
    """Alpha composite an RGBA Image with a specified color.

    Simpler, faster version than the solutions above.

    Source: http://stackoverflow.com/a/9459208/284318

    Keyword Arguments:
    image -- PIL RGBA Image object
    color -- Tuple r, g, b (default 255, 255, 255)

    """
    image.load()  # needed for split()
    background = Image.new('RGB', image.size, color)
    background.paste(image, mask=image.split()[3])  # 3 is the alpha channel
    return background
演出 简单的非合成
alpha_to_color
函数是最快的解决方案,但会留下难看的边框,因为它不处理半透明区域

纯PIL和numpy合成解决方案都提供了很好的结果,但是
alpha\u composite\u with_color
pure\u PIL\u alpha\u to_color
快得多(8.93毫秒)。如果你的系统上有numpy,那就是最好的选择。(更新:新的纯PIL版本是所有提到的解决方案中速度最快的。)


它没有坏。它完全按照你告诉它的去做;这些像素是完全透明的黑色。您需要迭代所有像素,并将完全透明的像素转换为白色。

透明部分大多具有RGBA值(0,0,0,0)。由于JPG没有透明度,因此jpeg值设置为(0,0,0),即黑色

在圆形图标周围,存在具有非零RGB值的像素,其中A=0。因此,它们在PNG中看起来是透明的,但在JPG中是有趣的颜色

可以使用numpy将A==0的所有像素设置为R=G=B=255,如下所示:

import Image
import numpy as np

FNAME = 'logo.png'
img = Image.open(FNAME).convert('RGBA')
x = np.array(img)
r, g, b, a = np.rollaxis(x, axis = -1)
r[a == 0] = 255
g[a == 0] = 255
b[a == 0] = 255
x = np.dstack([r, g, b, a])
img = Image.fromarray(x, 'RGBA')
img.save('/tmp/out.jpg')


请注意,徽标还具有一些半透明像素,用于平滑文字和图标周围的边缘。保存为jpeg会忽略半透明度,使生成的jpeg看起来非常参差不齐

使用imagemagick的
convert
命令可以获得更高质量的结果:

convert logo.png -background white -flatten /tmp/out.jpg


要使用numpy制作质量更好的混合,您可以使用:


这里有一个纯PIL的解决方案

def blend_value(under, over, a):
    return (over*a + under*(255-a)) / 255

def blend_rgba(under, over):
    return tuple([blend_value(under[i], over[i], over[3]) for i in (0,1,2)] + [255])

white = (255, 255, 255, 255)

im = Image.open(object.logo.path)
p = im.load()
for y in range(im.size[1]):
    for x in range(im.size[0]):
        p[x,y] = blend_rgba(white, p[x,y])
im.save('/tmp/output.png')

这里有一个更简单的版本-不知道它的性能如何。主要基于我在构建对sorl缩略图的支持时发现的一些django代码片段

from PIL import Image

png = Image.open(object.logo.path)
png.load() # required for png.split()

background = Image.new("RGB", png.size, (255, 255, 255))
background.paste(png, mask=png.split()[3]) # 3 is the alpha channel

background.save('foo.jpg', 'JPEG', quality=80)
结果@80%

结果@50%

通过使用,Yuji‘Tomita’Tomita的解决方案变得更简单。如果png没有alpha通道,此代码可以避免元组索引超出范围的错误

from PIL import Image

png = Image.open(img_path).convert('RGBA')
background = Image.new('RGBA', png.size, (255,255,255))

alpha_composite = Image.alpha_composite(background, png)
alpha_composite.save('foo.jpg', 'JPEG', quality=80)
导入图像

def图2IMG(图): """ @将Matplotlib图形简要转换为RGBA格式的PIL图像并返回 @参数图a matplotlib图 @返回Python映像库(PIL)映像 """ #将图形pixmap放入numpy数组中 buf=图2数据(图) w、 h,d=buf.形状 返回Image.frombytes(“RGBA”,(w,h),buf.tostring())

def图2数据(图): """ @将Matplotlib图形简要转换为带有RGBA通道的4D numpy阵列并返回 @参数图a matplotlib图 @返回RGBA值的numpy 3D数组 """ #绘制渲染器 图canvas.draw()

def rgba2rgb(img,c=(0,0,0),path='foo.jpg',如果加载=True,则是否已保存=False): 如果尚未保存,请执行以下操作: 背景=图像。新建(“RGB”,图像大小,c) background.paste(img,mask=img.split()[3])#3是alpha通道

    background.save(path, 'JPEG', quality=100)   
    is_already_saved = True
if if_load:
    if is_already_saved:
        im = Image.open(path)
        return np.array(im)
    else:
        raise ValueError('No image to load.')


谢谢但在蓝色圆圈周围有蓝色区域。这些区域是半透明的吗?有没有办法我也能解决这些问题?谢谢,这个解释很有道理:)@DaniloBargen,你注意到转换的质量很差吗?此解决方案不考虑部分透明度。@markransem:True。你知道如何解决这个问题吗?它需要基于alpha值的完全混合(带白色)。“我一直在寻找PIL的一种自然方法,结果却一无所获。”@MarkRansom是的,我注意到了这个问题。但在我的例子中,这只会影响很小比例的输入数据,所以质量对我来说已经足够好了。谢谢,这很有效。但是numpy的解决方案似乎要快得多:(numpy:8.92ms,pil:79.7ms)似乎有另一个更快的版本使用纯pil。看到新答案。@DaniloBargen,谢谢-我很高兴看到更好的答案,如果你没有引起我的注意,我就不会看到了。看起来你的版本是最快的:谢谢!关于你的帖子,有两件事是:png.load()命令似乎没有必要,第4行应该是
background=Image.new(“RGB”,png.size,(255,255,255))
。恭喜你找到了如何使
粘贴
进行适当混合的方法。@DaniloBargen,啊!的确,它缺少大小,但是
拆分
方法需要
加载
方法。听你这么说真是太棒了,它真的很快/很简单@尤吉托米塔:谢谢你!此代码导致我出错:
元组索引超出范围
。我按照另一个问题()解决了这个问题。我必须先将PNG转换为RGBA,然后将其切片:
alpha=img.split()[-1]
然后在背景遮罩上使用它。为了加快速度,我相信
im=image.copy()
可以从
纯pil\u alpha\u删除为\u color\u v2
。(当然,在将
im
的后续实例更改为
image
之后。)@unutbu啊,当然:)谢谢。这对我来说是最好的解决方案,因为我所有的图像都没有alpha通道。当我使用此代码时,png对象的模式仍然是“RGBA”@logic1976,只需在保存之前插入一个
。convert(“RGB”)
from PIL import Image

png = Image.open(object.logo.path)
png.load() # required for png.split()

background = Image.new("RGB", png.size, (255, 255, 255))
background.paste(png, mask=png.split()[3]) # 3 is the alpha channel

background.save('foo.jpg', 'JPEG', quality=80)
from PIL import Image

png = Image.open(img_path).convert('RGBA')
background = Image.new('RGBA', png.size, (255,255,255))

alpha_composite = Image.alpha_composite(background, png)
alpha_composite.save('foo.jpg', 'JPEG', quality=80)
# Get the RGBA buffer from the figure
w,h = fig.canvas.get_width_height()
buf = np.fromstring ( fig.canvas.tostring_argb(), dtype=np.uint8 )
buf.shape = ( w, h, 4 )

# canvas.tostring_argb give pixmap in ARGB mode. Roll the ALPHA channel to have it in RGBA mode
buf = np.roll ( buf, 3, axis = 2 )
return buf
    background.save(path, 'JPEG', quality=100)   
    is_already_saved = True
if if_load:
    if is_already_saved:
        im = Image.open(path)
        return np.array(im)
    else:
        raise ValueError('No image to load.')
import numpy as np
import PIL

def convert_image(image_file):
    image = Image.open(image_file) # this could be a 4D array PNG (RGBA)
    original_width, original_height = image.size

    np_image = np.array(image)
    new_image = np.zeros((np_image.shape[0], np_image.shape[1], 3)) 
    # create 3D array

    for each_channel in range(3):
        new_image[:,:,each_channel] = np_image[:,:,each_channel]  
        # only copy first 3 channels.

    # flushing
    np_image = []
    return new_image