4
\$\begingroup\$

This code has the function of removing worthless borders from the original image and then:

  1. Generate a 1080x1080 image (Instagram default) to be used as a blurred background.
  2. Generate an image with a width of 1080 and the height adjusted accordingly to maintain the correct aspect ratio.
  3. Superimpose 2. on the blurred background (1.)

I would like a review of the quality of the methods I used to reach the final result.

Original image example:

enter image description here

Code to Review:

from PIL import Image, ImageFilter, ImageChops
import numpy
import cv2
def remove_border(file_img):
 im = Image.open(file_img)
 bg = Image.new("RGB", im.size, im.getpixel((0,0)))
 diff = ImageChops.difference(im.convert("RGB"), bg)
 diff = ImageChops.add(diff, diff, 2.0, -30)
 bbox = diff.getbbox()
 if bbox:
 return im.crop(bbox)
def resize_blur(img_blur,sizers):
 img_blur = img_blur.resize(sizers, resample=Image.Resampling.LANCZOS)
 img_blur = img_blur.filter(ImageFilter.GaussianBlur(10))
 return img_blur
def resize_width_main(img_border,size_width):
 img_width = img_border
 basewidth = size_width
 wpercent = (basewidth/float(img_width.size[0]))
 hsize = int((float(img_width.size[1])*float(wpercent)))
 img_width = img_width.resize((basewidth,hsize), Image.Resampling.LANCZOS)
 return img_width
def center_overlay(name_file,overlay,background):
 img = numpy.asarray(overlay)
 img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
 h, w = img.shape[:2]
 back = numpy.asarray(background)
 back = cv2.cvtColor(back, cv2.COLOR_RGB2BGR)
 hh, ww = back.shape[:2]
 yoff = round((hh-h)/2)
 xoff = round((ww-w)/2)
 result = back.copy()
 result[yoff:yoff+h, xoff:xoff+w] = img
 cv2.imwrite(name_file, result)
def main():
 img_border = remove_border('resized_download.png')
 img_blur = resize_blur(img_border, (1080,1080))
 img_width = resize_width_main(img_border, 1080)
 center_overlay('resized.png', img_width, img_blur)
if __name__ == '__main__':
 main()

Final image example:

enter image description here

asked Jun 26, 2022 at 17:50
\$\endgroup\$

1 Answer 1

4
\$\begingroup\$

After upgrading to Pillow 9.1.1: I still think your imports could use some love, because there's ambiguity between Image the submodule and Image the class. (PIL chose poorly when naming the submodule.)

By convention Numpy is imported by import numpy as np. However, I am going to suggest that you not drop down to Numpy or cv at all: instead stay in Pillow and use .paste().

Add PEP484 type hints.

I suggest that you decouple your processing methods from file I/O so that they only operate on in-memory images and arrays.

This:

if bbox:
 return im.crop(bbox)

implicitly returns None if the condition is not met, but your upper code is not written to handle that. Either just drop the if and bear the consequences, or check if bbox is None and raise.

It's important for many of your function calls such as add() that you pass arguments by name, particularly for bare-literal numbers such as scale=2, offset=-30.

img_width = img_border is confusing. First of all it's an unneeded alias, and second of all it isn't a width: it's an image ("wide image", not "image width"). I would just remove this and use the original variable.

Float promotion is automatic, so this:

int((float(img_width.size[1])*float(wpercent)))

doesn't need the float() casts. You would benefit from tuple-unpacking the size here similar to what you do elsewhere.

Suggested

from PIL import ImageChops, ImageFilter
from PIL.Image import Image, Resampling
import PIL.Image
def remove_border(image: Image) -> Image:
 bg = PIL.Image.new(mode="RGB", size=image.size, color=image.getpixel((0, 0)))
 diff = ImageChops.difference(image, bg)
 diff = ImageChops.add(image1=diff, image2=diff, scale=2, offset=-30)
 bbox = diff.getbbox()
 return image.crop(bbox)
def resize_blur(blur_img: Image, sizers: tuple[int, int]) -> Image:
 return (
 blur_img
 .resize(sizers, resample=Resampling.LANCZOS)
 .filter(ImageFilter.GaussianBlur(10))
 )
def resize_width_main(border_img: Image, size_width: int) -> Image:
 width, height = border_img.size[:2]
 w_percent = size_width / width
 h_size = round(height * w_percent)
 return border_img.resize((size_width, h_size), Resampling.LANCZOS)
def center_overlay(overlay_img: Image, blur_img: Image) -> None:
 box = [
 round((xb - xo)/2)
 for xb, xo in zip(blur_img.size, overlay_img.size)
 ][:2]
 blur_img.paste(im=overlay_img, box=box)
def main() -> None:
 image = PIL.Image.open('resized_download.png')
 border_img = remove_border(image)
 blur_img = resize_blur(border_img, (1080, 1080))
 overlay_img = resize_width_main(border_img, 1080)
 center_overlay(overlay_img, blur_img)
 blur_img.save('resized.png')
if __name__ == '__main__':
 main()
answered Jun 26, 2022 at 23:19
\$\endgroup\$
2
  • \$\begingroup\$ Hi @Reinderien thanks for support, about LANCZOS when i use this method i get this message: DeprecationWarning: LANCZOS is deprecated and will be removed in Pillow 10 (2023年07月01日). Use Resampling.LANCZOS instead. from PIL.Image import Image, LANCZOSthat's why my code is the way you thought it was different. \$\endgroup\$ Commented Jun 27, 2022 at 1:07
  • 1
    \$\begingroup\$ OK, that makes sense. I'm on Pillow 9.0 so that explains the difference. \$\endgroup\$ Commented Jun 27, 2022 at 1:28

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.