Merge branch 'master' into reduce
|
@ -5,6 +5,12 @@ Changelog (Pillow)
|
|||
7.0.0 (unreleased)
|
||||
------------------
|
||||
|
||||
- Change default resize resampling filter from NEAREST to BICUBIC #4255
|
||||
[homm]
|
||||
|
||||
- Better thumbnail aspect ratio preservation #4256
|
||||
[homm]
|
||||
|
||||
- Add La mode packing and unpacking #4248
|
||||
[homm]
|
||||
|
||||
|
|
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):
|
||||
# test getheader/getdata against legacy values
|
||||
# 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.info = {"background": 0}
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from PIL import Image
|
||||
|
||||
from .helper import PillowTestCase, hopper
|
||||
|
||||
|
||||
|
@ -13,7 +15,7 @@ class TestImageGetData(PillowTestCase):
|
|||
|
||||
def test_roundtrip(self):
|
||||
def getdata(mode):
|
||||
im = hopper(mode).resize((32, 30))
|
||||
im = hopper(mode).resize((32, 30), Image.NEAREST)
|
||||
data = im.getdata()
|
||||
return data[0], len(data), len(list(data))
|
||||
|
||||
|
|
|
@ -212,6 +212,11 @@ class TestImagingCoreResampleAccuracy(PillowTestCase):
|
|||
for channel in case.split():
|
||||
self.check_case(channel, self.make_sample(data, (12, 12)))
|
||||
|
||||
def test_box_filter_correct_range(self):
|
||||
im = Image.new("RGB", (8, 8), "#1688ff").resize((100, 100), Image.BOX)
|
||||
ref = Image.new("RGB", (100, 100), "#1688ff")
|
||||
self.assert_image_equal(im, ref)
|
||||
|
||||
|
||||
class CoreResampleConsistencyTest(PillowTestCase):
|
||||
def make_case(self, mode, fill):
|
||||
|
|
|
@ -150,3 +150,12 @@ class TestImageResize(PillowTestCase):
|
|||
# Test unknown resampling filter
|
||||
with hopper() as im:
|
||||
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)))
|
||||
|
|
|
@ -43,6 +43,11 @@ class TestImageThumbnail(PillowTestCase):
|
|||
im.thumbnail((33, 33))
|
||||
self.assertEqual(im.size, (21, 33)) # ratio is 0.6363636364
|
||||
|
||||
def test_float(self):
|
||||
im = Image.new("L", (128, 128))
|
||||
im.thumbnail((99.9, 99.9))
|
||||
self.assertEqual(im.size, (100, 100))
|
||||
|
||||
def test_no_resize(self):
|
||||
# Check that draft() can resize the image to the destination size
|
||||
with Image.open("Tests/images/hopper.jpg") as im:
|
||||
|
|
|
@ -159,7 +159,8 @@ class TestImageDraw(PillowTestCase):
|
|||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
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
|
||||
draw.bitmap((10, 10), small)
|
||||
|
|
|
@ -24,7 +24,7 @@ class TestImageFile(PillowTestCase):
|
|||
def test_parser(self):
|
||||
def roundtrip(format):
|
||||
|
||||
im = hopper("L").resize((1000, 1000))
|
||||
im = hopper("L").resize((1000, 1000), Image.NEAREST)
|
||||
if format in ("MSP", "XBM"):
|
||||
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
|
||||
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
|
||||
===========
|
||||
|
@ -92,3 +103,9 @@ Use instead:
|
|||
|
||||
with Image.open("hopper.png") as im:
|
||||
im.save("out.jpg")
|
||||
|
||||
Better thumbnail aspect ratio preservation
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When calculating the new dimensions in ``Image.thumbnail``, round to the
|
||||
nearest integer, instead of always rounding down.
|
||||
|
|
|
@ -1761,7 +1761,7 @@ class Image:
|
|||
|
||||
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.
|
||||
|
||||
|
@ -1771,8 +1771,9 @@ class Image:
|
|||
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.BICUBIC` or :py:attr:`PIL.Image.LANCZOS`.
|
||||
If omitted, or if the image has mode "1" or "P", it is
|
||||
set :py:attr:`PIL.Image.NEAREST`.
|
||||
Default filter is :py:attr:`PIL.Image.BICUBIC`.
|
||||
If the image has mode "1" or "P", it is
|
||||
always set to :py:attr:`PIL.Image.NEAREST`.
|
||||
See: :ref:`concept-filters`.
|
||||
:param box: An optional 4-tuple of floats giving the region
|
||||
of the source image which should be scaled.
|
||||
|
@ -1875,7 +1876,7 @@ class Image:
|
|||
environment), or :py:attr:`PIL.Image.BICUBIC`
|
||||
(cubic spline interpolation in a 4x4 environment).
|
||||
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
|
||||
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
|
||||
|
@ -2171,10 +2172,10 @@ class Image:
|
|||
x, y = self.size
|
||||
if x > size[0]:
|
||||
y = max(round(y * size[0] / x), 1)
|
||||
x = size[0]
|
||||
x = round(size[0])
|
||||
if y > size[1]:
|
||||
x = max(round(x * size[1] / y), 1)
|
||||
y = size[1]
|
||||
y = round(size[1])
|
||||
size = x, y
|
||||
|
||||
if size == self.size:
|
||||
|
|
|
@ -221,7 +221,7 @@ def colorize(image, black, white, mid=None, blackpoint=0, whitepoint=255, midpoi
|
|||
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
|
||||
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
|
||||
(width, height) tuple.
|
||||
: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 centering: Control the position of the original image within the
|
||||
padded version.
|
||||
|
@ -280,7 +280,7 @@ def crop(image, border=0):
|
|||
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.
|
||||
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 factor: The expansion factor, as a float.
|
||||
:param resample: An optional resampling filter. Same values possible as
|
||||
in the PIL.Image.resize function.
|
||||
:param resample: What resampling method to use. Default is
|
||||
:py:attr:`PIL.Image.BICUBIC`. See :ref:`concept-filters`.
|
||||
:returns: An :py:class:`~PIL.Image.Image` object.
|
||||
"""
|
||||
if factor == 1:
|
||||
|
@ -363,7 +363,7 @@ def expand(image, border=0, fill=0):
|
|||
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
|
||||
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
|
||||
(width, height) tuple.
|
||||
: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
|
||||
four edges. The value is a decimal percentage (use 0.01 for
|
||||
one percent). The default value is 0 (no border).
|
||||
|
|
|
@ -13,7 +13,7 @@ struct filter {
|
|||
|
||||
static inline double box_filter(double x)
|
||||
{
|
||||
if (x >= -0.5 && x < 0.5)
|
||||
if (x > -0.5 && x <= 0.5)
|
||||
return 1.0;
|
||||
return 0.0;
|
||||
}
|
||||
|
|