how to round_corner a logo without white background(transparent?) on it using pil?
Asked Answered
C

4

13

I got a square logo and I need to round_corner it, searched for a while and got the follow code "working":

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)

then I saved the returned image object,however it has white background in the corner like this How can i get rid of the white background or make it invisible? Thanks in advance~

EDIT: here is the code by fraxel,thanks~

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')`

I am so sorry..I need the image shape to be ellipse not rectangle,I.E. the write stuff off the pic,and @fraxel, I still can see the white corner in the pic you processed for me

Circumbendibus answered 2/7, 2012 at 3:9 Comment(0)
N
47

First off, make sure you are saving your image in a format that supports transparency. PNG does, JPG does not... Below is some pretty nice code that will add transparent corners. It works like this:

  1. Draws a circle with radius, rad, using draw.ellipse()
  2. Create an image for the alpha channel the same size as your image
  3. Chop our circle into four pieces (the rounded corners), and place them in the correct corners of the alpha image
  4. Put the alpha channel into your image using putalpha()
  5. Save as a png, thus preserving transparency.

Here is the code:

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

example curved edge tiger:

enter image description here

here is your image, processed with this code, giving transparent corners:

enter image description here

Newhouse answered 2/7, 2012 at 9:58 Comment(11)
thanks,but it still got white corner on background.Check out thislink to see the problem, and I need the pink corner invisible, in your case white corner(U cannot see it here since it`s white, the same color as web page )..Hope u understand what i meanCircumbendibus
@Circumbendibus - please post your full code. and are you using my code and saving as a png?Newhouse
@Circumbendibus - what format is the image file you are opening? and how are you viewing your image to determine whether the corners are transparent or white?Newhouse
this is the piclink i am trying to process, i rename .jpg to .png to change the image format,am I not supposed to "convert" the format this way?And I can see the white corner in win7 file manager in "big icon" styleCircumbendibus
@Circumbendibus - If you download the above image of a tiger, you can see that the corners are transparent (you can also tell this by dragging the image around the screen). Flickr converts images to .jpg so any transparency is lost.Newhouse
@Circumbendibus - See my update, try downloading the images, they have transparent corners :) . win7 file manager is not an image viewer (try using a proper image viewer), so I guess it is not handling transparency correctly. I am on linux so can't test this any further, good luck.Newhouse
OMG..U r right!! rebooted to linux and feh could perfect show the pic without white corner!!!Thanks a lot,lifesaver as U are!ThanksCircumbendibus
@Circumbendibus - cool, glad you got it sorted :) - crummy windows!!Newhouse
Note if you use img.show(), the result is saved as bmp, which do not support alpha channel, you may think it's wrong, but when you save it as png file, you know this worksKeffiyeh
This gave bad results for small radii so I adjusted the call to ellipse from (0, 0, rad * 2, rad * 2) to (0, 0, rad * 2 - 1, rad * 2 - 1) :)Ikeda
Would be good to know where Image and ImageDraw comes from. It seems to be from Pillow. pip install Pillow and then it is from PIL import Image, ImageDraw instead of import Image, ImageDraw.Chef
G
2

Here is another option using NumPy only:

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

Geryon answered 24/7, 2022 at 10:33 Comment(0)
M
1

Have you tried something like image.putalpha(mask) to replace the image's alpha channel with the mask? This seems like it should do what you want. mask might have to be in 'L' mode for this to work correctly, and image should probaby be 'RGBA', but might be automatically converted for you.

The top answer here provides some good examples: How do I generate circular thumbnails with PIL?

Your mask image looks fine, but I think you want to swap 'white' and 'black', so you have a white rounded rectangle exactly the shape you want your final image to be, on a black background. You will probably also need to use the 'L' mode (greyscale) one.

Once you have this image, you can replace the return ImageChops.add(mask, image) by image.putalpha(mask); return image and this should cause the image to be transparent in only the black areas of the mask.

You might need to convert the image first with image.convert('RGBA') but I think this is unnecessary in later versions of PIL, it does it automatically.

Something like: (sorry can't test this right now)

def round_corner_jpg(image, radius):
    """generate round corner for image"""
    mask = Image.new('L', image.size) # filled with black by default
    draw = aggdraw.Draw(mask)
    brush = aggdraw.Brush('white')
    width, height = mask.size
    #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()
    image = image.convert('RGBA')
    image.putalpha(mask)
    return image
Metacarpus answered 2/7, 2012 at 3:57 Comment(4)
,Please check out the link I added to see the problem.I don`t think the link U gave is what I am looking for and I am not sure what U said about alpha stuff..Circumbendibus
@Circumbendibus the "alpha" channel is the part of the image that holds the transparency information. image.putalpha(mask) should replace only the transparency information with the mask image, where white mask colour means full opacity (no transparency - original image), and black means full transparency (totally see-thru). I'll try updating the answer to add some more detail.Metacarpus
Thanks a lot,but it seems the same with white stuff on the backgroud cornerCircumbendibus
check this photo out..I use red color to empysize the effect and I need the red stuff off the piclinkCircumbendibus
A
0

This works with transparent images 🖼️

...but requires numpy and may be faulty.

import numpy as np
from PIL import Image, ImageDraw

#-=-=-=-#

def _find_unused_color(img_np: np.array) -> list:
    """
    Finds a color not present in the given image.

    Parameters:
        img_np (numpy.array): Numpy array representation of the image.

    Returns:
        list: [R, G, B, A] color that not present in the image.
    """
    unique_colors = np.unique(
        img_np.reshape(-1, img_np.shape[2]),
        axis = 0
    )

    for R in range(256):
        for G in range(256):
            for B in range(256):
                if not any((unique_colors == [R, G, B, 255]).all(1)):
                    return [R, G, B, 255]
 
    # fallback if all colors are used
    return [255, 0, 0, 255]

def round_rgba(img: Image.Image, radius: int) -> Image.Image:
    """
    Rounds an transparent image corners by given radius.

    Arguments:
        img (Image.Image): Picture object in which corners will be rounded.
        radius      (int): Radius of rounding. (pixels)

    Returns:
        Image.Image: RGBA picture with rounded corners.
    """
    img = img.convert("RGBA")
    mask = Image.new("L", img.size, 0)

    draw = ImageDraw.Draw(mask)
    draw.rounded_rectangle(
        [(0, 0), img.size],
        radius = radius,
        fill = 255 # may be faulty
    )

    mask_np = np.array(mask)
    img_np = np.array(img)
    unused_color = _find_unused_color(img_np)
    img_np[mask_np == 0] = unused_color

    modified_img = Image.fromarray(img_np, "RGBA")
    img_np = np.array(modified_img)
    img_np[np.all(img_np[:, :, :4] == unused_color, axis = -1)] = [0,] * 4

    return Image.fromarray(img_np, "RGBA")

The approach is to find a color that is not used in a particular image and then create a new picture with that color.

Afterward, a mask is created in the form of a square with rounded corners. Then it's pasted onto the original image and removed, as its color was not existing in the original image.


Use as following:

rounded_pic = round_rgba(
    Image.open("round.png"),
    100
)

rounded_pic.show()

Before After
Admittedly answered 5/6 at 14:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.