pythonpython-3.xpython-imaging-libraryimage-editing

Blur a region shaped like a rounded rectangle inside an Image


I want to blur a rectangle (with rounded corners) in an image using python pillow. I already found a way to blur only a certain part of a picture.

img = Image.open('assets/images/image.png')
x, y = 300, 1600

cropped_img = img.crop((x, y, 1000, 2600))

blurred_img = cropped_img.filter(ImageFilter.GaussianBlur(20))

img.paste(blurred_img, (x, y))

img.save('assets/images/new.png')
img.show()

Furthermore I found a method to add rounded corners on a rectangle(Transparency issues drawing a rectangle with rounded corners)

def round_corner(radius):
   corner = Image.new('RGBA', (radius, radius), (0, 0, 0, 0))
   draw = ImageDraw.Draw(corner)
   draw.pieslice((0, 0, radius * 2, radius * 2), 180, 270)
   return corner

def round_rectangle(rectangle, radius):
   corner = round_corner(radius)
   rectangle.paste(corner, (0, 0))
   rectangle.paste(corner.rotate(90), (0, rectangle.size[1] - radius))
   rectangle.paste(corner.rotate(180), (rectangle.size[0] - radius,   rectangle.size[1] - radius))
   rectangle.paste(corner.rotate(270), (rectangle.size[0] - radius, 0))
   return rectangle

Unfortunately, I can't find a way to combine these two source codes so that they work.

My Example Image:example image


Solution

  • What you need to do, is essentially to create a mask for the Image.paste() that only pastes those parts of the blurred image that lie inside the rounded rectangle.

    import PIL
    from PIL import Image
    from PIL import ImageFilter
    from PIL import ImageDraw
    
    # when using an image as mask only the alpha channel is important
    solid_fill =  (50,50,50,255) 
    
    
    def create_rounded_rectangle_mask(rectangle, radius):
        # create mask image. all pixels set to translucent
        i = Image.new("RGBA",rectangle.size,(0,0,0,0))
    
        # create corner
        corner = Image.new('RGBA', (radius, radius), (0, 0, 0, 0))
        draw = ImageDraw.Draw(corner)
        # added the fill = .. you only drew a line, no fill
        draw.pieslice((0, 0, radius * 2, radius * 2), 180, 270, fill = solid_fill)
    
        # max_x, max_y
        mx,my = rectangle.size
    
        # paste corner rotated as needed
        # use corners alpha channel as mask
    
        i.paste(corner, (0, 0), corner)
        i.paste(corner.rotate(90), (0, my - radius),corner.rotate(90))
        i.paste(corner.rotate(180), (mx - radius,   my - radius),corner.rotate(180))
        i.paste(corner.rotate(270), (mx - radius, 0),corner.rotate(270))
    
        # draw both inner rects
        draw = ImageDraw.Draw(i)
        draw.rectangle( [(radius,0),(mx-radius,my)],fill=solid_fill)
        draw.rectangle( [(0,radius),(mx,my-radius)],fill=solid_fill)
    
        return i
    

    Mask:

    created mask

    Apply the mask to your image:

    img = Image.open('pic.jpg')
    
    x, y = 300, 160 
    radius = 75
    
    cropped_img = img.crop((x, y, 600, 600))
    
    # the filter removes the alpha, you need to add it again by converting to RGBA
    blurred_img = cropped_img.filter(ImageFilter.GaussianBlur(20),).convert("RGBA")
    
    # paste blurred, uses alphachannel of create_rounded_rectangle_mask() as mask 
    # only those parts of the mask that have a non-zero alpha gets pasted
    img.paste(blurred_img, (x, y), create_rounded_rectangle_mask(cropped_img,radius))
    
    img.save('new2.png')
    img.show()
    

    I changed some dimensions and paths. Your code lacked the imports, I completed it to a minimal verifyable complete example.

    Resulting Image, reduzed in size