Merge pull request #5417 from radarhere/contain

Added ImageOps contain()
This commit is contained in:
Hugo van Kemenade 2021-05-01 18:44:37 +03:00 committed by GitHub
commit 8a8ac60817
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 121 additions and 27 deletions

View File

@ -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)
@ -99,6 +102,13 @@ def test_fit_same_ratio():
assert new_im.size == (1000, 755)
@pytest.mark.parametrize("new_size", ((256, 256), (512, 256), (256, 512)))
def test_contain(new_size):
im = hopper()
new_im = ImageOps.contain(im, new_size)
assert new_im.size == (256, 256)
def test_pad():
# Same ratio
im = hopper()

View File

@ -12,6 +12,7 @@ only work on L and RGB images.
.. autofunction:: autocontrast
.. autofunction:: colorize
.. autofunction:: contain
.. autofunction:: pad
.. autofunction:: crop
.. autofunction:: scale

View File

@ -0,0 +1,64 @@
8.3.0
-----
Deprecations
============
TODO
^^^^
TODO
API Changes
===========
Changed WebP default "method" value when saving
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Previously, it was 0, for the best speed. The default has now been changed to 4, to
match WebP's default, for higher quality with still some speed optimisation.
Default resampling filter for special image modes
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Pillow 7.0 changed the default resampling filter to ``Image.BICUBIC``. However, as this
is not supported yet for images with a custom number of bits, the default filter for
those modes has been reverted to ``Image.NEAREST``.
ImageMorph incorrect mode errors
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
For ``apply()``, ``match()`` and ``get_on_pixels()``, if the image mode is not L, an
:py:exc:`Exception` was thrown. This has now been changed to a :py:exc:`ValueError`.
API Additions
=============
ImageOps.contain
^^^^^^^^^^^^^^^^
Returns a resized version of the image, set to the maximum width and height within
``size``, while maintaining the original aspect ratio.
To compare it to other ImageOps methods:
- :py:meth:`~PIL.ImageOps.fit` expands an image until is fills ``size``, cropping the
parts of the image that do not fit.
- :py:meth:`~PIL.ImageOps.pad` expands an image to fill ``size``, without cropping, but
instead filling the extra space with ``color``.
- :py:meth:`~PIL.ImageOps.contain` is similar to :py:meth:`~PIL.ImageOps.pad`, but it
does not fill the extra space. Instead, the original aspect ratio is maintained. So
unlike the other two methods, it is not guaranteed to return an image of ``size``.
Security
========
TODO
Other Changes
=============
TODO
^^^^
TODO

View File

@ -14,6 +14,7 @@ expected to be backported to earlier versions.
.. toctree::
:maxdepth: 2
8.3.0
8.2.0
8.1.2
8.1.1

View File

@ -236,15 +236,43 @@ def colorize(image, black, white, mid=None, blackpoint=0, whitepoint=255, midpoi
return _lut(image, red + green + blue)
def pad(image, size, method=Image.BICUBIC, color=None, centering=(0.5, 0.5)):
def contain(image, size, method=Image.BICUBIC):
"""
Returns a sized and padded version of the image, expanded to fill the
requested aspect ratio and size.
Returns a resized version of the image, set to the maximum width and height
within the requested size, while maintaining the original aspect ratio.
:param image: The image to size and crop.
:param image: The image to resize and crop.
:param size: The requested output size in pixels, given as a
(width, height) tuple.
:param method: What resampling method to use. Default is
:param method: Resampling method to use. Default is
: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 resized and padded version of the image, expanded to fill the
requested aspect ratio and size.
:param image: The image to resize and crop.
:param size: The requested output size in pixels, given as a
(width, height) tuple.
:param method: Resampling method to use. Default is
:py:attr:`PIL.Image.BICUBIC`. See :ref:`concept-filters`.
:param color: The background color of the padded image.
:param centering: Control the position of the original image within the
@ -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
@ -304,7 +322,7 @@ def scale(image, factor, resample=Image.BICUBIC):
:param image: The image to rescale.
:param factor: The expansion factor, as a float.
:param resample: What resampling method to use. Default is
:param resample: Resampling method to use. Default is
:py:attr:`PIL.Image.BICUBIC`. See :ref:`concept-filters`.
:returns: An :py:class:`~PIL.Image.Image` object.
"""
@ -381,15 +399,15 @@ def expand(image, border=0, fill=0):
def fit(image, size, method=Image.BICUBIC, bleed=0.0, centering=(0.5, 0.5)):
"""
Returns a sized and cropped version of the image, cropped to the
Returns a resized and cropped version of the image, cropped to the
requested aspect ratio and size.
This function was contributed by Kevin Cazabon.
:param image: The image to size and crop.
:param image: The image to resize and crop.
:param size: The requested output size in pixels, given as a
(width, height) tuple.
:param method: What resampling method to use. Default is
:param method: Resampling method to use. Default is
:py:attr:`PIL.Image.BICUBIC`. See :ref:`concept-filters`.
:param bleed: Remove a border around the outside of the image from all
four edges. The value is a decimal percentage (use 0.01 for