This code has the function of removing worthless borders from the original image and then:
- Generate a 1080x1080 image (Instagram default) to be used as a blurred background.
- Generate an image with a width of 1080 and the height adjusted accordingly to maintain the correct aspect ratio.
- 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:
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:
1 Answer 1
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()
-
\$\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, LANCZOS
that's why my code is the way you thought it was different. \$\endgroup\$Digital Farmer– Digital Farmer2022年06月27日 01:07:13 +00:00Commented Jun 27, 2022 at 1:07 -
1\$\begingroup\$ OK, that makes sense. I'm on Pillow 9.0 so that explains the difference. \$\endgroup\$Reinderien– Reinderien2022年06月27日 01:28:30 +00:00Commented Jun 27, 2022 at 1:28