IOS corner smoothing in python

100 views Asked by At

I am working on a project that involves video frame manipulation, and one of the requirements is to add smooth rounded corners to the frames. I did some research on how to add rounded corners to images using PIL or any other image processing library, but all of them produce rough corners that look unprofessional. Here is the code I tried :-

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('sand.png')
im = round_corner_jpg(im, 50)
im.save('sand_corner.png')

enter image description here

and here is the result it produces, as can be seen in the above image, the corners are rough and have this pixelated view on them, they are not smooth just like we can do in figma using IOS corner smoothing:- enter image description here

I also saw this question that also had the same requirements, but they didn't show the code for how to implement it :- Any way to make nice antialiased round corners for images in python?

I want to make sure that there is no loss in the frame quality when adding the rounded corners and the corners should be smooth and better yet look like the corner smoothing in figma

EDIT:-

out = cv2.VideoWriter('assets/pathToSave.mp4', fourcc, fps, (1920, 1080))

def generate_frame():
        while cap.isOpened():
            code, frame = cap.read()
            if code:
                yield frame
            else:
                print("done")
                break
    
for i, frame in enumerate(generate_frame()):
    # Convert opencv frames into PIL Image
    img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    im_pil = Image.fromarray(img)
    ss = 4
    alpha = Image.new('L', (im.width*ss, im.height*ss))

    # Draw rounded rectangle and unsupersample
    d = ImageDraw.Draw(alpha)
    d.rounded_rectangle([0,0,im.width*ss-1,im.height*ss-1],radius=120,fill='white')
    alpha = alpha.resize((alpha.width//ss, alpha.height//ss), 
    resample=Image.Resampling.LANCZOS)
    alpha.save('DEBUG-alpha.png')

    # Push new alpha channel into original image and save
    im_pil.putalpha(alpha)
    im_np = np.array(im_pil.convert('RGB'))

    open_cv_image = im_np[:, :, ::-1].copy()

    outputVideo.write(open_cv_image)

I tried the above code in order to have rounded corners from an incoming video frames, but I still got rough corners :-

enter image description here

Please look at the white frame, the transparent rounded corners are a part of background on which this video is rendered upon

2

There are 2 answers

5
Mark Setchell On

Maybe this is better:

#!/usr/bin/env python3

from PIL import Image, ImageDraw

# Open image
im = Image.open('rock.jpg')

# Make alpha channel, supersampled by factor 4
ss = 4
alpha = Image.new('L', (im.width*ss, im.height*ss))

# Draw rounded rectangle and unsupersample
d = ImageDraw.Draw(alpha)
d.rounded_rectangle([0,0,im.width*ss-1,im.height*ss-1],radius=120,fill='white')
alpha = alpha.resize((alpha.width//ss, alpha.height//ss), resample=Image.Resampling.LANCZOS)
alpha.save('DEBUG-alpha.png')

# Push new alpha channel into original image and save
im.putalpha(alpha)
im.save('result.png')

enter image description here

0
bob On

I think a solution of the many good ones is to use GaussianBlur from PIL import ImageFilter . It would look something like this =>

from PIL import Image
from PIL import ImageDraw
from PIL import ImageFilter

def convert_jpg_to_png(jpg_path, png_path):
img = Image.open(jpg_path)
img.save(png_path)



def add_corners(im, rad,blur_radius):
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
#cirlce quadrents 
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))

#add blur to just cornors 
for i in range(0, w, w - rad):
    for j in range(0, h, h - rad):
        corner = alpha.crop((i, j, i + rad, j + rad))
        blurred_corner = corner.filter(ImageFilter.GaussianBlur(blur_radius))
        alpha.paste(blurred_corner, (i, j))
im.putalpha(alpha)
return im

This with this code gave me this=>

convert_jpg_to_png('mountain-landscape-2031539_1280.jpg', 'mountain-landscape-2031539_1280.png')
im = Image.open('mountain-landscape-2031539_1280.png')
im = add_corners(im, 500,5)
im.save('mountain-landscape-2031539_1280_corner.png')

A close up of the blurred border

This was the image generated from this code :D. ): unfortunately the file size is to large. Ok i will just apply it to your image here=>

im = Image.open('Beirp.png')
im = add_corners(im, 50,2)
im.save('new_idk_image.png')

This was your image that you rounded the corners with it has blurring applied:

This was your image that you rounded the corners with it has blurring applied

Gl with the coding :D.