简体   繁体   English

如何使用 python 的枕头使裁剪后的 gif 角透明?

[英]How can i make my cropped gif corners transparent with python's pillow?

I'm trying to edit an avatar image from my discord users with a GaussianBlur filter, crop it to a circle and overlay an image.我正在尝试使用GaussianBlur过滤器编辑来自我的不和谐用户的avatar图像,将其裁剪为圆形并覆盖图像。 So all of these things work, but the cropped gifs have corners, I don't want them.所以所有这些都可以工作,但是裁剪的 gif 有角落,我不想要它们。 It should be transparent.它应该是透明的。 I searched a lot for a solution but can't find it.我搜索了很多解决方案,但找不到。 I'm inexperienced in coding with python's pillow library and don't know how I could fix it.我没有使用 python 的枕头库进行编码的经验,不知道如何修复它。

My current code transforms this (PICTURE 1):我当前的代码对此进行了转换(图 1):

在此处输入图片说明

into this (PICTURE 2):进入这个(图2):

在此处输入图片说明

but it should be this (working for static images but GIFs should keep their animation at the end):但应该是这样的(适用于静态图像,但 GIF 应该在最后保留它们的动画):

在此处输入图片说明

Like you can see my cropped GIF image has white corners.就像你可以看到我裁剪的 GIF 图像有白角一样。 It doesn't contain them if PICTURE 1 is a PNG.如果 PICTURE 1 是 PNG,则它不包含它们。 So how can I remove these white corners?那么如何去除这些白角呢?

And that is my currently used code:这是我目前使用的代码:

    def crop_center(pil_img, crop_width, crop_height):
        img_width, img_height = pil_img.size
        return pil_img.crop(((img_width - crop_width) // 2,
                             (img_height - crop_height) // 2,
                             (img_width + crop_width) // 2,
                             (img_height + crop_height) // 2))
    
    def crop_max_square(pil_img):
        return crop_center(pil_img, min(pil_img.size), min(pil_img.size))
    
    def mask_circle_transparent(pil_img, blur_radius, offset=0):
        offset = blur_radius * 2 + offset
        mask = Image.new("L", pil_img.size, 0)
        draw = ImageDraw.Draw(mask)
        draw.ellipse((offset, offset, pil_img.size[0] - offset, pil_img.size[1] - offset), fill=255)
        mask = mask.filter(ImageFilter.GaussianBlur(blur_radius))
    
        result = pil_img.copy()
        result.putalpha(mask)
    
        return result


    async def on_message(message):

       if ".gif" in str(message.author.avatar_url):

          frames = []

          gif = Image.open(BytesIO(await message.author.avatar_url.read()))

          for frame in ImageSequence.Iterator(gif):
              frame = frame.copy()
              frame = frame.convert("RGB")
              frame = frame.resize((256, 256))
              frame = mask_circle_transparent(frame, 4)

              background = background.resize((256, 256))

              frame.paste(background, (0, 0), background)
              frames.append(frame)

              print(str(frames))
              frames[0].save('temp_images/result.gif', save_all=True, append_images=frames[1:])

       else:

          im_square = crop_max_square(Image.open(BytesIO(await message.author.avatar_url.read())).convert("RGB"))
          im_square = im_square.resize((256, 256))
          im_thumb = mask_circle_transparent(im_square, 4)

          background = background.resize((256, 256))

          im_thumb.paste(background, (0, 0), background)
          im_thumb.show()

          im_thumb.save('temp_images/result.png')

Edit: I tested a lot now and I think frame = mask_circle_transparent(frame, 4) is the issue here, before I call that function is my frame is blank, just a background.编辑:我现在测试了很多,我认为frame = mask_circle_transparent(frame, 4)是这里的问题,在我调用该函数之前,我的框架是空白的,只是一个背景。 I don't know why.我不知道为什么。 And apparently, the corners aren't white, it's a color from the image background or something like this?显然,角落不是白色的,它是图像背景中的颜色或类似的颜色? I don't know.我不知道。

gif does not support transparency. gif 不支持透明度。 To feather/soften the edges on your gif you may have to use a white overlay layer with feathered edges instead of a mask.要羽化/柔化 gif 上的边缘,您可能必须使用带有羽化边缘的白色覆盖层而不是蒙版。

Making the smooth edges is not possible using the GIF format.使用 GIF 格式无法制作平滑的边缘。 GIF does not have an alpha channel, so partially transparency is not supported (source: How to get better transparency with GIFs? ). GIF 没有 Alpha 通道,因此不支持部分透明(来源: 如何使用 GIF 获得更好的透明度? )。 This means that the result you achieved on the static image cannot be achieved on a moving GIF.这意味着您在静态图像上获得的结果无法在移动 GIF 上获得。

However, the background in the GIF can be made transparent using this answer: transparent background in gif using Python Imageio .但是,可以使用以下答案使 GIF 中的背景透明: 使用 Python Imageio 的 gif 中的透明背景 Below you find the resulting GIF and the code to achieve this result.您可以在下面找到生成的 GIF 和实现此结果的代码。 (I had to skip a few parts of your code, since I did not have the background image and the Discord connection). (我不得不跳过你代码的一些部分,因为我没有背景图片和 Discord 连接)。

透明蜥蜴

from PIL import Image, ImageDraw, ImageFilter, ImageSequence
import numpy as np

def crop_center(pil_img, crop_width, crop_height):
    img_width, img_height = pil_img.size
    return pil_img.crop(((img_width - crop_width) // 2,
                         (img_height - crop_height) // 2,
                         (img_width + crop_width) // 2,
                         (img_height + crop_height) // 2))

def crop_max_square(pil_img):
    return crop_center(pil_img, min(pil_img.size), min(pil_img.size))

def mask_circle_transparent(pil_img, blur_radius, offset=0):
    offset = blur_radius * 2 + offset
    mask = Image.new("L", pil_img.size, 0)
    draw = ImageDraw.Draw(mask)
    draw.ellipse((offset, offset, pil_img.size[0] - offset, pil_img.size[1] - offset), fill=255)
    mask = mask.filter(ImageFilter.GaussianBlur(blur_radius))

    result = pil_img.copy()
    result.putalpha(mask)

    return result


def transparent_frame(im):
    # im = Image.open(path)
    alpha = im.getchannel('A')

    # Convert the image into P mode but only use 255 colors in the palette out of 256
    im = im.convert('RGB').convert('P', palette=Image.ADAPTIVE, colors=255)

    # Set all pixel values below 128 to 255 , and the rest to 0
    mask = Image.eval(alpha, lambda a: 255 if a <=128 else 0)

    # Paste the color of index 255 and use alpha as a mask
    im.paste(255, mask)

    # The transparency index is 255
    im.info['transparency'] = 255

    return im


def on_message(message):

   # if ".gif" in str(message.author.avatar_url):

      frames = []

      gif = Image.open('a.gif')

      for frame in ImageSequence.Iterator(gif):
          frame = frame.copy()
          frame = frame.convert("RGB")
          frame = frame.resize((256, 256))
          frame = mask_circle_transparent(frame, 4)

          # I did not have the background image that you used, so i skipped this part
          # background = background.resize((256, 256))
          # frame.paste(background, (0, 0), background)
          
          # make transparent using the transparent_frame() function
          frame = transparent_frame(frame)
          frames.append(frame)

          # print(str(frames))
          frames[0].save('result.gif', save_all=True, append_images=frames[1:], optimize=False) # optimize must be false to preserve the transparent channel

   # else:

   #    im_square = crop_max_square(Image.open(BytesIO(await message.author.avatar_url.read())).convert("RGB"))
   #    im_square = im_square.resize((256, 256))
   #    im_thumb = mask_circle_transparent(im_square, 4)

   #    background = background.resize((256, 256))

   #    im_thumb.paste(background, (0, 0), background)
   #    im_thumb.show()

   #    im_thumb.save('temp_images/result.png')
   
if __name__ == "__main__":
    on_message('hi')

GIF animations consist of multiple frames, that your image viewer cycles through to give the impression of a video or animation. GIF 动画由多个帧组成,您的图像查看器循环播放这些帧以呈现视频或动画的效果。

As such, you need to read each frame and apply your processing to it, then pass a list of frames to save at the end.因此,您需要读取每一帧并将处理应用于它,然后传递要在最后保存的帧列表

See here for reading an animated GIF, and here for writing.这里用于读取GIF动画,并在这里写。

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

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