如何使用PIL将logo圆角化而不带有白色背景(透明)?

13

我有一个正方形的标志,需要将其圆角化,搜索了一段时间并找到了以下代码“可行”:

def round_corner_jpg(image, radius):
    """generate round corner for image"""
    mask = Image.new('RGB', image.size)
    #mask = Image.new('RGB', (image.size[0] - radius, image.size[1] - radius))
    #mask = Image.new('L', image.size, 255)
    draw = aggdraw.Draw(mask)
    brush = aggdraw.Brush('black')
    width, height = mask.size
    draw.rectangle((0,0,width,height), aggdraw.Brush('white'))
    #upper-left corner
    draw.pieslice((0,0,radius*2, radius*2), 90, 180, None, brush)
    #upper-right corner
    draw.pieslice((width - radius*2, 0, width, radius*2), 0, 90, None, brush)
    #bottom-left corner
    draw.pieslice((0, height - radius * 2, radius*2, height),180, 270, None, brush)
    #bottom-right corner
    draw.pieslice((width - radius * 2, height - radius * 2, width, height), 270, 360, None, brush)
    #center rectangle
    draw.rectangle((radius, radius, width - radius, height - radius), brush)
    #four edge rectangle
    draw.rectangle((radius, 0, width - radius, radius), brush)
    draw.rectangle((0, radius, radius, height-radius), brush)
    draw.rectangle((radius, height-radius, width-radius, height), brush)
    draw.rectangle((width-radius, radius, width, height-radius), brush)
    draw.flush()
    del draw
    return ImageChops.add(mask, image)

然后我保存了返回的图像对象,但是在角落处有白色背景像这样。如何去掉白色背景或使其不可见?提前感谢您的帮助~
编辑:以下是fraxel的代码,谢谢~
def add_corners(im, rad):
    circle = Image.new('L', (rad * 2, rad * 2), 0)
    draw = ImageDraw.Draw(circle)
    draw.ellipse((0, 0, rad * 2, rad * 2), fill=255)
    alpha = Image.new('L', im.size, "white")
    w, h = im.size
    alpha.paste(circle.crop((0, 0, rad, rad)), (0, 0))
    alpha.paste(circle.crop((0, rad, rad, rad * 2)), (0, h - rad))
    alpha.paste(circle.crop((rad, 0, rad * 2, rad)), (w - rad, 0))
    alpha.paste(circle.crop((rad, rad, rad * 2, rad * 2)), (w - rad, h - rad))
    im.putalpha(alpha)
    return im


if __name__ == '__main__':
    im = Image.open('1.jpg')
    im = add_corners(im, 100)
    im.save('out.png')`

非常抱歉..我需要将图像形状变为椭圆而不是矩形,即从图片中裁剪出白色区域,@fraxel,您为我处理的图片仍然存在白色角落。

3个回答

43
首先,请确保您正在以支持透明度的格式保存图像。 PNG 支持透明度,而 JPG 则不支持... 下面是一些相当不错的代码,将添加透明角落。它的工作原理如下:
  1. 使用 draw.ellipse() 画一个半径为rad的圆形
  2. 创建与您的图像大小相同的alpha通道图像
  3. 将我们的圆形切成四个部分(圆角),并将它们放置在alpha图像的正确角落中
  4. 使用 putalpha() 将alpha通道放入您的图像中
  5. 另存为png,从而保留透明度。
以下是代码:
import Image, ImageDraw

def add_corners(im, rad):
    circle = Image.new('L', (rad * 2, rad * 2), 0)
    draw = ImageDraw.Draw(circle)
    draw.ellipse((0, 0, rad * 2 - 1, rad * 2 - 1), fill=255)
    alpha = Image.new('L', im.size, 255)
    w, h = im.size
    alpha.paste(circle.crop((0, 0, rad, rad)), (0, 0))
    alpha.paste(circle.crop((0, rad, rad, rad * 2)), (0, h - rad))
    alpha.paste(circle.crop((rad, 0, rad * 2, rad)), (w - rad, 0))
    alpha.paste(circle.crop((rad, rad, rad * 2, rad * 2)), (w - rad, h - rad))
    im.putalpha(alpha)
    return im

im = Image.open('tiger.jpg')
im = add_corners(im, 100)
im.save('tiger.png')

示例曲边虎:

在此输入图片描述

这是使用以下代码处理的图像,使其具有透明角落:

在此输入图片描述


谢谢,但是背景上仍然有白色角落。请查看此链接以查看问题,并且我需要粉色角落不可见,在您的情况下是白色角落(因为它是白色的,与网页相同的颜色,所以您在这里看不到它)。希望您明白我的意思。 - bdictator
@bdictator - 请发布您的完整代码。您是否正在使用我的代码并将其保存为PNG格式? - fraxel
@bdictator - 你打开的图像文件是什么格式?你是如何查看图像以确定角落是否透明或白色的? - fraxel
1
@bdictator - 看看我的更新,试着下载这些图片,它们有透明的角落 :) 。win7文件管理器不是一个图像查看器(尝试使用一个合适的图像查看器),所以我猜它没有正确处理透明度。我在Linux上,无法进一步测试,祝你好运。 - fraxel
请注意,如果您使用img.show(),结果将保存为bmp格式,该格式不支持alpha通道。您可能会认为这是错误的,但当您将其保存为png文件时,您会发现它可以正常工作。 - ospider
显示剩余6条评论

1

这里有另一种仅使用NumPy的选项:

import numpy as np
import matplotlib.pyplot as plt

test_img = plt.imread("logo.png") # reads rgba image

radius = 155
shape = (2 * radius, 2 * radius)
x, y = np.indices(shape)
circ = (x - radius) ** 2 + (y - radius) ** 2 <= radius**2
array = np.zeros(shape)
array[circ] = 255
array = np.insert(array, obj=radius, values=np.full((test_img.shape[0] - 2 * radius, array.shape[1]), 255),axis=0)
array = np.insert(array, obj=radius, values=np.full((test_img.shape[1] - 2 * radius, array.shape[0]), 255), axis=1)

plt.imshow(array)
plt.axis("off")
plt.show()

test_img[:, :, 3] = array
plt.imshow(test_img)
plt.axis("off")

enter image description here enter image description here


1

你尝试过像这样使用image.putalpha(mask)来用mask替换图像的alpha通道吗?这似乎可以实现你想要的效果。mask可能需要在'L'模式下才能正确工作,而image应该是'RGBA',但也可能会自动转换。

这里的最佳答案提供了一些很好的示例: 如何使用PIL生成圆形缩略图?

你的mask图像看起来不错,但我认为你想交换'white'和'black',这样你就有一个白色的圆角矩形,恰好是你最终图像的形状,在黑色背景上。你可能还需要使用'L'模式(灰度)。

一旦你有了这个图像,你可以用image.putalpha(mask); return image替换return ImageChops.add(mask, image),这样就可以使图像仅在mask的黑色区域透明。

你可能需要先用image.convert('RGBA')将图像转换,但我认为在后来的PIL版本中这是不必要的,它会自动完成。

类似这样的内容:(抱歉现在无法测试)

def round_corner_jpg(image, radius): """生成图片的圆角""" mask = Image.new('L', image.size) # 默认为黑色 draw = aggdraw.Draw(mask) brush = aggdraw.Brush('white') width, height = mask.size #左上角 draw.pieslice((0,0,radius*2, radius*2), 90, 180, None, brush) #右上角 draw.pieslice((width - radius*2, 0, width, radius*2), 0, 90, None, brush) #左下角 draw.pieslice((0, height - radius * 2, radius*2, height),180, 270, None, brush) #右下角 draw.pieslice((width - radius * 2, height - radius * 2, width, height), 270, 360, None, brush) #中心矩形 draw.rectangle((radius, radius, width - radius, height - radius), brush) #四个边缘矩形 draw.rectangle((radius, 0, width - radius, radius), brush) draw.rectangle((0, radius, radius, height-radius), brush) draw.rectangle((radius, height-radius, width-radius, height), brush) draw.rectangle((width-radius, radius, width, height-radius), brush) draw.flush() image = image.convert('RGBA') image.putalpha(mask) return image

请查看我添加的链接以查看问题。我不认为您提供的链接是我要找的,而且我也不确定您所说的关于alpha的东西是什么意思。 - bdictator
@bdictator "alpha"通道是图像中保存透明信息的部分。image.putalpha(mask)应该仅用遮罩图像替换透明信息,其中白色遮罩颜色表示完全不透明(无透明度-原始图像),黑色表示完全透明(完全透明)。我会尝试更新答案以添加更多细节。 - mesilliac
非常感谢,但是似乎背景角落上的白色物质还是一样的。 - bdictator
检查这张照片..我使用红色来强调效果,我需要把图片上的红色东西去掉 [链接](http://www.flickr.com/photos/76230909@N03/7485468218/in/photostream) - bdictator

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接