Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added ImageOps contain() #5417

Merged
merged 8 commits into from May 1, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 18 additions & 0 deletions Tests/test_imageops.py
Expand Up @@ -37,6 +37,9 @@ def test_sanity():
ImageOps.pad(hopper("L"), (128, 128))
ImageOps.pad(hopper("RGB"), (128, 128))

ImageOps.contain(hopper("L"), (128, 128))
ImageOps.contain(hopper("RGB"), (128, 128))

ImageOps.crop(hopper("L"), 1)
ImageOps.crop(hopper("RGB"), 1)

Expand Down Expand Up @@ -99,6 +102,21 @@ def test_fit_same_ratio():
assert new_im.size == (1000, 755)


def test_contain():
hugovk marked this conversation as resolved.
Show resolved Hide resolved
# Same ratio
im = hopper()
new_size = (im.width * 2, im.height * 2)
new_im = ImageOps.contain(im, new_size)
assert new_im.size == new_size

for new_size in [
(im.width * 4, im.height * 2),
(im.width * 2, im.height * 4),
]:
new_im = ImageOps.contain(im, new_size)
assert new_im.size == (im.width * 2, im.height * 2)


def test_pad():
# Same ratio
im = hopper()
Expand Down
54 changes: 36 additions & 18 deletions src/PIL/ImageOps.py
Expand Up @@ -236,6 +236,34 @@ def colorize(image, black, white, mid=None, blackpoint=0, whitepoint=255, midpoi
return _lut(image, red + green + blue)


def contain(image, size, method=Image.BICUBIC):
"""
Returns a sized version of the image, expanded to fill the requested aspect ratio
and size.
hugovk marked this conversation as resolved.
Show resolved Hide resolved

:param image: The image to size and crop.
radarhere marked this conversation as resolved.
Show resolved Hide resolved
:param size: The requested output size in pixels, given as a
(width, height) tuple.
:param method: What resampling method to use. Default is
radarhere marked this conversation as resolved.
Show resolved Hide resolved
:py:attr:`PIL.Image.BICUBIC`. See :ref:`concept-filters`.
:return: An image.
"""

im_ratio = image.width / image.height
dest_ratio = size[0] / size[1]

if im_ratio != dest_ratio:
if im_ratio > dest_ratio:
new_height = int(image.height / image.width * size[0])
if new_height != size[1]:
size = (size[0], new_height)
else:
new_width = int(image.width / image.height * size[1])
if new_width != size[0]:
size = (new_width, size[1])
return image.resize(size, resample=method)


def pad(image, size, method=Image.BICUBIC, color=None, centering=(0.5, 0.5)):
"""
Returns a sized and padded version of the image, expanded to fill the
Expand All @@ -257,27 +285,17 @@ def pad(image, size, method=Image.BICUBIC, color=None, centering=(0.5, 0.5)):
:return: An image.
"""

im_ratio = image.width / image.height
dest_ratio = size[0] / size[1]

if im_ratio == dest_ratio:
out = image.resize(size, resample=method)
resized = contain(image, size, method)
if resized.size == size:
out = resized
else:
out = Image.new(image.mode, size, color)
if im_ratio > dest_ratio:
new_height = int(image.height / image.width * size[0])
if new_height != size[1]:
image = image.resize((size[0], new_height), resample=method)

y = int((size[1] - new_height) * max(0, min(centering[1], 1)))
out.paste(image, (0, y))
if resized.width != size[0]:
x = int((size[0] - resized.width) * max(0, min(centering[0], 1)))
out.paste(resized, (x, 0))
else:
new_width = int(image.width / image.height * size[1])
if new_width != size[0]:
image = image.resize((new_width, size[1]), resample=method)

x = int((size[0] - new_width) * max(0, min(centering[0], 1)))
out.paste(image, (x, 0))
y = int((size[1] - resized.height) * max(0, min(centering[1], 1)))
out.paste(resized, (0, y))
return out


Expand Down