mirror of
https://github.com/python-pillow/Pillow.git
synced 2026-01-09 02:01:26 +03:00
Deprecate getdata(), in favour of new get_flattened_data() (#9292)
This commit is contained in:
parent
b51a036685
commit
3baedf2648
|
|
@ -55,8 +55,8 @@ def convert_to_comparable(
|
|||
if a.mode == "P":
|
||||
new_a = Image.new("L", a.size)
|
||||
new_b = Image.new("L", b.size)
|
||||
new_a.putdata(a.getdata())
|
||||
new_b.putdata(b.getdata())
|
||||
new_a.putdata(a.get_flattened_data())
|
||||
new_b.putdata(b.get_flattened_data())
|
||||
elif a.mode == "I;16":
|
||||
new_a = a.convert("I")
|
||||
new_b = b.convert("I")
|
||||
|
|
|
|||
|
|
@ -28,9 +28,13 @@ def box_blur(image: Image.Image, radius: float = 1, n: int = 1) -> Image.Image:
|
|||
|
||||
|
||||
def assert_image(im: Image.Image, data: list[list[int]], delta: int = 0) -> None:
|
||||
it = iter(im.getdata())
|
||||
it = iter(im.get_flattened_data())
|
||||
for data_row in data:
|
||||
im_row = [next(it) for _ in range(im.size[0])]
|
||||
im_row = []
|
||||
for _ in range(im.width):
|
||||
im_v = next(it)
|
||||
assert isinstance(im_v, (int, float))
|
||||
im_row.append(im_v)
|
||||
if any(abs(data_v - im_v) > delta for data_v, im_v in zip(data_row, im_row)):
|
||||
assert im_row == data_row
|
||||
with pytest.raises(StopIteration):
|
||||
|
|
|
|||
|
|
@ -121,7 +121,6 @@ class TestFileAvif:
|
|||
assert image.size == (128, 128)
|
||||
assert image.format == "AVIF"
|
||||
assert image.get_format_mimetype() == "image/avif"
|
||||
image.getdata()
|
||||
|
||||
# generated with:
|
||||
# avifdec hopper.avif hopper_avif_write.png
|
||||
|
|
@ -143,7 +142,6 @@ class TestFileAvif:
|
|||
assert reloaded.mode == "RGB"
|
||||
assert reloaded.size == (128, 128)
|
||||
assert reloaded.format == "AVIF"
|
||||
reloaded.getdata()
|
||||
|
||||
# avifdec hopper.avif avif/hopper_avif_write.png
|
||||
assert_image_similar_tofile(
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ class LibTiffTestCase:
|
|||
|
||||
# Does the data actually load
|
||||
im.load()
|
||||
im.getdata()
|
||||
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
assert im._compression == "group4"
|
||||
|
|
|
|||
|
|
@ -60,7 +60,6 @@ class TestFileWebp:
|
|||
assert image.size == (128, 128)
|
||||
assert image.format == "WEBP"
|
||||
image.load()
|
||||
image.getdata()
|
||||
|
||||
# generated with:
|
||||
# dwebp -ppm ../../Tests/images/hopper.webp -o hopper_webp_bits.ppm
|
||||
|
|
@ -77,7 +76,6 @@ class TestFileWebp:
|
|||
assert image.size == (128, 128)
|
||||
assert image.format == "WEBP"
|
||||
image.load()
|
||||
image.getdata()
|
||||
|
||||
if mode == self.rgb_mode:
|
||||
# generated with: dwebp -ppm temp.webp -o hopper_webp_write.ppm
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ def test_read_rgba() -> None:
|
|||
assert image.size == (200, 150)
|
||||
assert image.format == "WEBP"
|
||||
image.load()
|
||||
image.getdata()
|
||||
|
||||
image.tobytes()
|
||||
|
||||
|
|
@ -60,7 +59,6 @@ def test_write_lossless_rgb(tmp_path: Path) -> None:
|
|||
assert image.size == pil_image.size
|
||||
assert image.format == "WEBP"
|
||||
image.load()
|
||||
image.getdata()
|
||||
|
||||
assert_image_equal(image, pil_image)
|
||||
|
||||
|
|
@ -83,7 +81,6 @@ def test_write_rgba(tmp_path: Path) -> None:
|
|||
assert image.size == (10, 10)
|
||||
assert image.format == "WEBP"
|
||||
image.load()
|
||||
image.getdata()
|
||||
|
||||
assert_image_similar(image, pil_image, 1.0)
|
||||
|
||||
|
|
@ -133,7 +130,6 @@ def test_write_unsupported_mode_PA(tmp_path: Path) -> None:
|
|||
assert image.format == "WEBP"
|
||||
|
||||
image.load()
|
||||
image.getdata()
|
||||
with Image.open(file_path) as im:
|
||||
target = im.convert("RGBA")
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,5 @@ def test_write_lossless_rgb(tmp_path: Path) -> None:
|
|||
assert image.size == (128, 128)
|
||||
assert image.format == "WEBP"
|
||||
image.load()
|
||||
image.getdata()
|
||||
|
||||
assert_image_equal(image, hopper(RGB_MODE))
|
||||
|
|
|
|||
|
|
@ -13,15 +13,15 @@ def test_white() -> None:
|
|||
|
||||
k = i.getpixel((0, 0))
|
||||
|
||||
L = i.getdata(0)
|
||||
a = i.getdata(1)
|
||||
b = i.getdata(2)
|
||||
L = i.get_flattened_data(0)
|
||||
a = i.get_flattened_data(1)
|
||||
b = i.get_flattened_data(2)
|
||||
|
||||
assert k == (255, 128, 128)
|
||||
|
||||
assert list(L) == [255] * 100
|
||||
assert list(a) == [128] * 100
|
||||
assert list(b) == [128] * 100
|
||||
assert L == (255,) * 100
|
||||
assert a == (128,) * 100
|
||||
assert b == (128,) * 100
|
||||
|
||||
|
||||
def test_green() -> None:
|
||||
|
|
|
|||
|
|
@ -1181,10 +1181,10 @@ class TestImageBytes:
|
|||
assert reloaded.tobytes() == source_bytes
|
||||
|
||||
@pytest.mark.parametrize("mode", Image.MODES)
|
||||
def test_getdata_putdata(self, mode: str) -> None:
|
||||
def test_get_flattened_data_putdata(self, mode: str) -> None:
|
||||
im = hopper(mode)
|
||||
reloaded = Image.new(mode, im.size)
|
||||
reloaded.putdata(im.getdata())
|
||||
reloaded.putdata(im.get_flattened_data())
|
||||
assert_image_equal(im, reloaded)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ def test_fromarray() -> None:
|
|||
},
|
||||
)
|
||||
out = Image.fromarray(wrapped)
|
||||
return out.mode, out.size, list(i.getdata()) == list(out.getdata())
|
||||
return out.mode, out.size, i.get_flattened_data() == out.get_flattened_data()
|
||||
|
||||
# assert test("1") == ("1", (128, 100), True)
|
||||
assert test("L") == ("L", (128, 100), True)
|
||||
|
|
|
|||
|
|
@ -95,10 +95,10 @@ def test_crop_zero() -> None:
|
|||
|
||||
cropped = im.crop((10, 10, 20, 20))
|
||||
assert cropped.size == (10, 10)
|
||||
assert cropped.getdata()[0] == (0, 0, 0)
|
||||
assert cropped.getpixel((0, 0)) == (0, 0, 0)
|
||||
|
||||
im = Image.new("RGB", (0, 0))
|
||||
|
||||
cropped = im.crop((10, 10, 20, 20))
|
||||
assert cropped.size == (10, 10)
|
||||
assert cropped.getdata()[2] == (0, 0, 0)
|
||||
assert cropped.getpixel((2, 0)) == (0, 0, 0)
|
||||
|
|
|
|||
|
|
@ -1,23 +1,23 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from .helper import hopper
|
||||
|
||||
|
||||
def test_sanity() -> None:
|
||||
data = hopper().getdata()
|
||||
|
||||
len(data)
|
||||
list(data)
|
||||
data = hopper().get_flattened_data()
|
||||
|
||||
assert len(data) == 128 * 128
|
||||
assert data[0] == (20, 20, 70)
|
||||
|
||||
|
||||
def test_mode() -> None:
|
||||
def getdata(mode: str) -> tuple[float | tuple[int, ...] | None, int, int]:
|
||||
im = hopper(mode).resize((32, 30), Image.Resampling.NEAREST)
|
||||
data = im.getdata()
|
||||
data = im.get_flattened_data()
|
||||
return data[0], len(data), len(list(data))
|
||||
|
||||
assert getdata("1") == (0, 960, 960)
|
||||
|
|
@ -28,3 +28,13 @@ def test_mode() -> None:
|
|||
assert getdata("RGBA") == ((11, 13, 52, 255), 960, 960)
|
||||
assert getdata("CMYK") == ((244, 242, 203, 0), 960, 960)
|
||||
assert getdata("YCbCr") == ((16, 147, 123), 960, 960)
|
||||
|
||||
|
||||
def test_deprecation() -> None:
|
||||
im = hopper()
|
||||
with pytest.warns(DeprecationWarning, match="getdata"):
|
||||
data = im.getdata()
|
||||
|
||||
assert len(data) == 128 * 128
|
||||
assert data[0] == (20, 20, 70)
|
||||
assert list(data)[0] == (20, 20, 70)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ from __future__ import annotations
|
|||
|
||||
import sys
|
||||
from array import array
|
||||
from typing import cast
|
||||
|
||||
import pytest
|
||||
|
||||
|
|
@ -12,21 +13,19 @@ from .helper import assert_image_equal, hopper
|
|||
|
||||
def test_sanity() -> None:
|
||||
im1 = hopper()
|
||||
for data in (im1.get_flattened_data(), im1.im):
|
||||
im2 = Image.new(im1.mode, im1.size, 0)
|
||||
im2.putdata(data)
|
||||
|
||||
data = list(im1.getdata())
|
||||
assert_image_equal(im1, im2)
|
||||
|
||||
im2 = Image.new(im1.mode, im1.size, 0)
|
||||
im2.putdata(data)
|
||||
# readonly
|
||||
im2 = Image.new(im1.mode, im2.size, 0)
|
||||
im2.readonly = 1
|
||||
im2.putdata(data)
|
||||
|
||||
assert_image_equal(im1, im2)
|
||||
|
||||
# readonly
|
||||
im2 = Image.new(im1.mode, im2.size, 0)
|
||||
im2.readonly = 1
|
||||
im2.putdata(data)
|
||||
|
||||
assert not im2.readonly
|
||||
assert_image_equal(im1, im2)
|
||||
assert not im2.readonly
|
||||
assert_image_equal(im1, im2)
|
||||
|
||||
|
||||
def test_long_integers() -> None:
|
||||
|
|
@ -60,22 +59,22 @@ def test_mode_with_L_with_float() -> None:
|
|||
@pytest.mark.parametrize("mode", ("I", "I;16", "I;16L", "I;16B"))
|
||||
def test_mode_i(mode: str) -> None:
|
||||
src = hopper("L")
|
||||
data = list(src.getdata())
|
||||
data = src.get_flattened_data()
|
||||
im = Image.new(mode, src.size, 0)
|
||||
im.putdata(data, 2, 256)
|
||||
|
||||
target = [2 * elt + 256 for elt in data]
|
||||
assert list(im.getdata()) == target
|
||||
target = tuple(2 * elt + 256 for elt in cast(tuple[int, ...], data))
|
||||
assert im.get_flattened_data() == target
|
||||
|
||||
|
||||
def test_mode_F() -> None:
|
||||
src = hopper("L")
|
||||
data = list(src.getdata())
|
||||
data = src.get_flattened_data()
|
||||
im = Image.new("F", src.size, 0)
|
||||
im.putdata(data, 2.0, 256.0)
|
||||
|
||||
target = [2.0 * float(elt) + 256.0 for elt in data]
|
||||
assert list(im.getdata()) == target
|
||||
target = tuple(2.0 * float(elt) + 256.0 for elt in cast(tuple[int, ...], data))
|
||||
assert im.get_flattened_data() == target
|
||||
|
||||
|
||||
def test_array_B() -> None:
|
||||
|
|
@ -86,7 +85,7 @@ def test_array_B() -> None:
|
|||
im = Image.new("L", (150, 100))
|
||||
im.putdata(arr)
|
||||
|
||||
assert len(im.getdata()) == len(arr)
|
||||
assert len(im.get_flattened_data()) == len(arr)
|
||||
|
||||
|
||||
def test_array_F() -> None:
|
||||
|
|
@ -97,7 +96,7 @@ def test_array_F() -> None:
|
|||
arr = array("f", [0.0]) * 15000
|
||||
im.putdata(arr)
|
||||
|
||||
assert len(im.getdata()) == len(arr)
|
||||
assert len(im.get_flattened_data()) == len(arr)
|
||||
|
||||
|
||||
def test_not_flattened() -> None:
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ class TestImagingCoreResize:
|
|||
r = self.resize(Image.new("RGB", (0, 0), "white"), (212, 195), resample)
|
||||
assert r.mode == "RGB"
|
||||
assert r.size == (212, 195)
|
||||
assert r.getdata()[0] == (0, 0, 0)
|
||||
assert r.getpixel((0, 0)) == (0, 0, 0)
|
||||
|
||||
def test_unknown_filter(self) -> None:
|
||||
with pytest.raises(ValueError):
|
||||
|
|
|
|||
|
|
@ -274,13 +274,13 @@ def test_simple_lab() -> None:
|
|||
# not a linear luminance map. so L != 128:
|
||||
assert k == (137, 128, 128)
|
||||
|
||||
l_data = i_lab.getdata(0)
|
||||
a_data = i_lab.getdata(1)
|
||||
b_data = i_lab.getdata(2)
|
||||
l_data = i_lab.get_flattened_data(0)
|
||||
a_data = i_lab.get_flattened_data(1)
|
||||
b_data = i_lab.get_flattened_data(2)
|
||||
|
||||
assert list(l_data) == [137] * 100
|
||||
assert list(a_data) == [128] * 100
|
||||
assert list(b_data) == [128] * 100
|
||||
assert l_data == (137,) * 100
|
||||
assert a_data == (128,) * 100
|
||||
assert b_data == (128,) * 100
|
||||
|
||||
|
||||
def test_lab_color() -> None:
|
||||
|
|
|
|||
|
|
@ -20,21 +20,19 @@ TEST_IMAGE_SIZE = (10, 10)
|
|||
|
||||
def test_numpy_to_image() -> None:
|
||||
def to_image(dtype: npt.DTypeLike, bands: int = 1, boolean: int = 0) -> Image.Image:
|
||||
data = tuple(range(100))
|
||||
if bands == 1:
|
||||
if boolean:
|
||||
data = [0, 255] * 50
|
||||
else:
|
||||
data = list(range(100))
|
||||
data = (0, 255) * 50
|
||||
a = numpy.array(data, dtype=dtype)
|
||||
a.shape = TEST_IMAGE_SIZE
|
||||
i = Image.fromarray(a)
|
||||
assert list(i.getdata()) == data
|
||||
assert i.get_flattened_data() == data
|
||||
else:
|
||||
data = list(range(100))
|
||||
a = numpy.array([[x] * bands for x in data], dtype=dtype)
|
||||
a.shape = TEST_IMAGE_SIZE[0], TEST_IMAGE_SIZE[1], bands
|
||||
i = Image.fromarray(a)
|
||||
assert list(i.getchannel(0).getdata()) == list(range(100))
|
||||
assert i.get_flattened_data(0) == tuple(range(100))
|
||||
return i
|
||||
|
||||
# Check supported 1-bit integer formats
|
||||
|
|
@ -191,7 +189,7 @@ def test_putdata() -> None:
|
|||
arr = numpy.zeros((15000,), numpy.float32)
|
||||
im.putdata(arr)
|
||||
|
||||
assert len(im.getdata()) == len(arr)
|
||||
assert len(im.get_flattened_data()) == len(arr)
|
||||
|
||||
|
||||
def test_resize() -> None:
|
||||
|
|
@ -248,7 +246,7 @@ def test_bool() -> None:
|
|||
a[0][0] = True
|
||||
|
||||
im2 = Image.fromarray(a)
|
||||
assert im2.getdata()[0] == 255
|
||||
assert im2.getpixel((0, 0)) == 255
|
||||
|
||||
|
||||
def test_no_resource_warning_for_numpy_array() -> None:
|
||||
|
|
|
|||
|
|
@ -73,6 +73,16 @@ Image._show
|
|||
``Image._show`` has been deprecated, and will be removed in Pillow 13 (2026-10-15).
|
||||
Use :py:meth:`~PIL.ImageShow.show` instead.
|
||||
|
||||
Image getdata()
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 12.1.0
|
||||
|
||||
:py:meth:`~PIL.Image.Image.getdata` has been deprecated.
|
||||
:py:meth:`~PIL.Image.Image.get_flattened_data` can be used instead. This new method is
|
||||
identical, except that it returns a tuple of pixel values, instead of an internal
|
||||
Pillow data type.
|
||||
|
||||
Removed features
|
||||
----------------
|
||||
|
||||
|
|
|
|||
|
|
@ -191,6 +191,7 @@ This helps to get the bounding box coordinates of the input image::
|
|||
.. automethod:: PIL.Image.Image.getchannel
|
||||
.. automethod:: PIL.Image.Image.getcolors
|
||||
.. automethod:: PIL.Image.Image.getdata
|
||||
.. automethod:: PIL.Image.Image.get_flattened_data
|
||||
.. automethod:: PIL.Image.Image.getexif
|
||||
.. automethod:: PIL.Image.Image.getextrema
|
||||
.. automethod:: PIL.Image.Image.getpalette
|
||||
|
|
|
|||
|
|
@ -1,6 +1,17 @@
|
|||
12.1.0
|
||||
------
|
||||
|
||||
Deprecations
|
||||
============
|
||||
|
||||
Image getdata()
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
:py:meth:`~PIL.Image.Image.getdata` has been deprecated.
|
||||
:py:meth:`~PIL.Image.Image.get_flattened_data` can be used instead. This new method is
|
||||
identical, except that it returns a tuple of pixel values, instead of an internal
|
||||
Pillow data type.
|
||||
|
||||
API changes
|
||||
===========
|
||||
|
||||
|
|
@ -13,6 +24,13 @@ To match the behaviour of :py:meth:`~PIL.ImageMorph.LutBuilder.build_lut`,
|
|||
API additions
|
||||
=============
|
||||
|
||||
Image get_flattened_data()
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
:py:meth:`~PIL.Image.Image.get_flattened_data` is identical to the deprecated
|
||||
:py:meth:`~PIL.Image.Image.getdata`, except that the new method returns a tuple of
|
||||
pixel values, instead of an internal Pillow data type.
|
||||
|
||||
Specify window in ImageGrab on macOS
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
|
|
|||
|
|
@ -753,7 +753,7 @@ def _write_multiple_frames(
|
|||
if delta.mode == "P":
|
||||
# Convert to L without considering palette
|
||||
delta_l = Image.new("L", delta.size)
|
||||
delta_l.putdata(delta.getdata())
|
||||
delta_l.putdata(delta.get_flattened_data())
|
||||
delta = delta_l
|
||||
mask = ImageMath.lambda_eval(
|
||||
lambda args: args["convert"](args["im"] * 255, "1"),
|
||||
|
|
|
|||
|
|
@ -1435,12 +1435,31 @@ class Image:
|
|||
value (e.g. 0 to get the "R" band from an "RGB" image).
|
||||
:returns: A sequence-like object.
|
||||
"""
|
||||
deprecate("Image.Image.getdata", 14, "get_flattened_data")
|
||||
|
||||
self.load()
|
||||
if band is not None:
|
||||
return self.im.getband(band)
|
||||
return self.im # could be abused
|
||||
|
||||
def get_flattened_data(
|
||||
self, band: int | None = None
|
||||
) -> tuple[tuple[int, ...], ...] | tuple[float, ...]:
|
||||
"""
|
||||
Returns the contents of this image as a tuple containing pixel values.
|
||||
The sequence object is flattened, so that values for line one follow
|
||||
directly after the values of line zero, and so on.
|
||||
|
||||
:param band: What band to return. The default is to return
|
||||
all bands. To return a single band, pass in the index
|
||||
value (e.g. 0 to get the "R" band from an "RGB" image).
|
||||
:returns: A tuple containing pixel values.
|
||||
"""
|
||||
self.load()
|
||||
if band is not None:
|
||||
return tuple(self.im.getband(band))
|
||||
return tuple(self.im)
|
||||
|
||||
def getextrema(self) -> tuple[float, float] | tuple[tuple[int, int], ...]:
|
||||
"""
|
||||
Gets the minimum and maximum pixel values for each band in
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@ def deprecate(
|
|||
raise RuntimeError(msg)
|
||||
elif when == 13:
|
||||
removed = "Pillow 13 (2026-10-15)"
|
||||
elif when == 14:
|
||||
removed = "Pillow 14 (2027-10-15)"
|
||||
else:
|
||||
msg = f"Unknown removal version: {when}. Update {__name__}?"
|
||||
raise ValueError(msg)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user