Merge pull request #4255 from uploadcare/default-resample-filter
Change default resize resampling filter from NEAREST to BICUBIC
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 11 KiB |
|
@ -754,7 +754,7 @@ class TestFileGif(PillowTestCase):
|
||||||
def test_getdata(self):
|
def test_getdata(self):
|
||||||
# test getheader/getdata against legacy values
|
# test getheader/getdata against legacy values
|
||||||
# Create a 'P' image with holes in the palette
|
# Create a 'P' image with holes in the palette
|
||||||
im = Image._wedge().resize((16, 16))
|
im = Image._wedge().resize((16, 16), Image.NEAREST)
|
||||||
im.putpalette(ImagePalette.ImagePalette("RGB"))
|
im.putpalette(ImagePalette.ImagePalette("RGB"))
|
||||||
im.info = {"background": 0}
|
im.info = {"background": 0}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper
|
from .helper import PillowTestCase, hopper
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,7 +15,7 @@ class TestImageGetData(PillowTestCase):
|
||||||
|
|
||||||
def test_roundtrip(self):
|
def test_roundtrip(self):
|
||||||
def getdata(mode):
|
def getdata(mode):
|
||||||
im = hopper(mode).resize((32, 30))
|
im = hopper(mode).resize((32, 30), Image.NEAREST)
|
||||||
data = im.getdata()
|
data = im.getdata()
|
||||||
return data[0], len(data), len(list(data))
|
return data[0], len(data), len(list(data))
|
||||||
|
|
||||||
|
|
|
@ -150,3 +150,12 @@ class TestImageResize(PillowTestCase):
|
||||||
# Test unknown resampling filter
|
# Test unknown resampling filter
|
||||||
with hopper() as im:
|
with hopper() as im:
|
||||||
self.assertRaises(ValueError, im.resize, (10, 10), "unknown")
|
self.assertRaises(ValueError, im.resize, (10, 10), "unknown")
|
||||||
|
|
||||||
|
def test_default_filter(self):
|
||||||
|
for mode in "L", "RGB", "I", "F":
|
||||||
|
im = hopper(mode)
|
||||||
|
self.assertEqual(im.resize((20, 20), Image.BICUBIC), im.resize((20, 20)))
|
||||||
|
|
||||||
|
for mode in "1", "P":
|
||||||
|
im = hopper(mode)
|
||||||
|
self.assertEqual(im.resize((20, 20), Image.NEAREST), im.resize((20, 20)))
|
||||||
|
|
|
@ -159,7 +159,8 @@ class TestImageDraw(PillowTestCase):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
with Image.open("Tests/images/pil123rgba.png").resize((50, 50)) as small:
|
with Image.open("Tests/images/pil123rgba.png") as small:
|
||||||
|
small = small.resize((50, 50), Image.NEAREST)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.bitmap((10, 10), small)
|
draw.bitmap((10, 10), small)
|
||||||
|
|
|
@ -24,7 +24,7 @@ class TestImageFile(PillowTestCase):
|
||||||
def test_parser(self):
|
def test_parser(self):
|
||||||
def roundtrip(format):
|
def roundtrip(format):
|
||||||
|
|
||||||
im = hopper("L").resize((1000, 1000))
|
im = hopper("L").resize((1000, 1000), Image.NEAREST)
|
||||||
if format in ("MSP", "XBM"):
|
if format in ("MSP", "XBM"):
|
||||||
im = im.convert("1")
|
im = im.convert("1")
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,17 @@ Setting the size of TIFF images
|
||||||
Setting the size of a TIFF image directly (eg. ``im.size = (256, 256)``) throws
|
Setting the size of a TIFF image directly (eg. ``im.size = (256, 256)``) throws
|
||||||
an error. Use ``Image.resize`` instead.
|
an error. Use ``Image.resize`` instead.
|
||||||
|
|
||||||
|
Default resampling filter
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The default resampling filter has been changed to the high-quality convolution
|
||||||
|
``Image.BICUBIC`` instead of ``Image.NEAREST``, for the :py:meth:`~PIL.Image.Image.resize`
|
||||||
|
method and the :py:meth:`~PIL.ImageOps.pad``, :py:meth:`~PIL.ImageOps.scale``
|
||||||
|
and :py:meth:`~PIL.ImageOps.fit`` functions.
|
||||||
|
``Image.NEAREST`` is still always used for images in "P" and "1" modes.
|
||||||
|
See :ref:`concept-filters` to learn the difference. In short,
|
||||||
|
``Image.NEAREST`` is a very fast filter, but simple and low-quality.
|
||||||
|
|
||||||
|
|
||||||
API Changes
|
API Changes
|
||||||
===========
|
===========
|
||||||
|
|
|
@ -1761,7 +1761,7 @@ class Image:
|
||||||
|
|
||||||
return m_im
|
return m_im
|
||||||
|
|
||||||
def resize(self, size, resample=NEAREST, box=None):
|
def resize(self, size, resample=BICUBIC, box=None):
|
||||||
"""
|
"""
|
||||||
Returns a resized copy of this image.
|
Returns a resized copy of this image.
|
||||||
|
|
||||||
|
@ -1771,8 +1771,9 @@ class Image:
|
||||||
one of :py:attr:`PIL.Image.NEAREST`, :py:attr:`PIL.Image.BOX`,
|
one of :py:attr:`PIL.Image.NEAREST`, :py:attr:`PIL.Image.BOX`,
|
||||||
:py:attr:`PIL.Image.BILINEAR`, :py:attr:`PIL.Image.HAMMING`,
|
:py:attr:`PIL.Image.BILINEAR`, :py:attr:`PIL.Image.HAMMING`,
|
||||||
:py:attr:`PIL.Image.BICUBIC` or :py:attr:`PIL.Image.LANCZOS`.
|
:py:attr:`PIL.Image.BICUBIC` or :py:attr:`PIL.Image.LANCZOS`.
|
||||||
If omitted, or if the image has mode "1" or "P", it is
|
Default filter is :py:attr:`PIL.Image.BICUBIC`.
|
||||||
set :py:attr:`PIL.Image.NEAREST`.
|
If the image has mode "1" or "P", it is
|
||||||
|
always set to :py:attr:`PIL.Image.NEAREST`.
|
||||||
See: :ref:`concept-filters`.
|
See: :ref:`concept-filters`.
|
||||||
:param box: An optional 4-tuple of floats giving the region
|
:param box: An optional 4-tuple of floats giving the region
|
||||||
of the source image which should be scaled.
|
of the source image which should be scaled.
|
||||||
|
@ -1842,7 +1843,7 @@ class Image:
|
||||||
environment), or :py:attr:`PIL.Image.BICUBIC`
|
environment), or :py:attr:`PIL.Image.BICUBIC`
|
||||||
(cubic spline interpolation in a 4x4 environment).
|
(cubic spline interpolation in a 4x4 environment).
|
||||||
If omitted, or if the image has mode "1" or "P", it is
|
If omitted, or if the image has mode "1" or "P", it is
|
||||||
set :py:attr:`PIL.Image.NEAREST`. See :ref:`concept-filters`.
|
set to :py:attr:`PIL.Image.NEAREST`. See :ref:`concept-filters`.
|
||||||
:param expand: Optional expansion flag. If true, expands the output
|
:param expand: Optional expansion flag. If true, expands the output
|
||||||
image to make it large enough to hold the entire rotated image.
|
image to make it large enough to hold the entire rotated image.
|
||||||
If false or omitted, make the output image the same size as the
|
If false or omitted, make the output image the same size as the
|
||||||
|
|
|
@ -221,7 +221,7 @@ def colorize(image, black, white, mid=None, blackpoint=0, whitepoint=255, midpoi
|
||||||
return _lut(image, red + green + blue)
|
return _lut(image, red + green + blue)
|
||||||
|
|
||||||
|
|
||||||
def pad(image, size, method=Image.NEAREST, color=None, centering=(0.5, 0.5)):
|
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
|
Returns a sized and padded version of the image, expanded to fill the
|
||||||
requested aspect ratio and size.
|
requested aspect ratio and size.
|
||||||
|
@ -230,7 +230,7 @@ def pad(image, size, method=Image.NEAREST, color=None, centering=(0.5, 0.5)):
|
||||||
:param size: The requested output size in pixels, given as a
|
:param size: The requested output size in pixels, given as a
|
||||||
(width, height) tuple.
|
(width, height) tuple.
|
||||||
:param method: What resampling method to use. Default is
|
:param method: What resampling method to use. Default is
|
||||||
:py:attr:`PIL.Image.NEAREST`.
|
:py:attr:`PIL.Image.BICUBIC`. See :ref:`concept-filters`.
|
||||||
:param color: The background color of the padded image.
|
:param color: The background color of the padded image.
|
||||||
:param centering: Control the position of the original image within the
|
:param centering: Control the position of the original image within the
|
||||||
padded version.
|
padded version.
|
||||||
|
@ -280,7 +280,7 @@ def crop(image, border=0):
|
||||||
return image.crop((left, top, image.size[0] - right, image.size[1] - bottom))
|
return image.crop((left, top, image.size[0] - right, image.size[1] - bottom))
|
||||||
|
|
||||||
|
|
||||||
def scale(image, factor, resample=Image.NEAREST):
|
def scale(image, factor, resample=Image.BICUBIC):
|
||||||
"""
|
"""
|
||||||
Returns a rescaled image by a specific factor given in parameter.
|
Returns a rescaled image by a specific factor given in parameter.
|
||||||
A factor greater than 1 expands the image, between 0 and 1 contracts the
|
A factor greater than 1 expands the image, between 0 and 1 contracts the
|
||||||
|
@ -288,8 +288,8 @@ def scale(image, factor, resample=Image.NEAREST):
|
||||||
|
|
||||||
:param image: The image to rescale.
|
:param image: The image to rescale.
|
||||||
:param factor: The expansion factor, as a float.
|
:param factor: The expansion factor, as a float.
|
||||||
:param resample: An optional resampling filter. Same values possible as
|
:param resample: What resampling method to use. Default is
|
||||||
in the PIL.Image.resize function.
|
:py:attr:`PIL.Image.BICUBIC`. See :ref:`concept-filters`.
|
||||||
:returns: An :py:class:`~PIL.Image.Image` object.
|
:returns: An :py:class:`~PIL.Image.Image` object.
|
||||||
"""
|
"""
|
||||||
if factor == 1:
|
if factor == 1:
|
||||||
|
@ -363,7 +363,7 @@ def expand(image, border=0, fill=0):
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)):
|
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 sized and cropped version of the image, cropped to the
|
||||||
requested aspect ratio and size.
|
requested aspect ratio and size.
|
||||||
|
@ -374,7 +374,7 @@ def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)):
|
||||||
:param size: The requested output size in pixels, given as a
|
:param size: The requested output size in pixels, given as a
|
||||||
(width, height) tuple.
|
(width, height) tuple.
|
||||||
:param method: What resampling method to use. Default is
|
:param method: What resampling method to use. Default is
|
||||||
:py:attr:`PIL.Image.NEAREST`.
|
:py:attr:`PIL.Image.BICUBIC`. See :ref:`concept-filters`.
|
||||||
:param bleed: Remove a border around the outside of the image from all
|
: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
|
four edges. The value is a decimal percentage (use 0.01 for
|
||||||
one percent). The default value is 0 (no border).
|
one percent). The default value is 0 (no border).
|
||||||
|
|