Skip to content

Only deprecate fromarray mode for changing data types #9063

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions Tests/test_image_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,8 @@ def __init__(self, arr_params: dict[str, Any]) -> None:
self.__array_interface__ = arr_params

with pytest.raises(ValueError):
wrapped = Wrapper({"shape": (1, 1), "strides": (1, 1)})
with pytest.warns(DeprecationWarning, match="'mode' parameter"):
Image.fromarray(wrapped, "L")
wrapped = Wrapper({"shape": (1, 1), "strides": (1, 1), "typestr": "|u1"})
Image.fromarray(wrapped, "L")


def test_fromarray_palette() -> None:
Expand All @@ -112,9 +111,16 @@ def test_fromarray_palette() -> None:
a = numpy.array(i)

# Act
with pytest.warns(DeprecationWarning, match="'mode' parameter"):
out = Image.fromarray(a, "P")
out = Image.fromarray(a, "P")

# Assert that the Python and C palettes match
assert out.palette is not None
assert len(out.palette.colors) == len(out.im.getpalette()) / 3


def test_deprecation() -> None:
a = numpy.array(im.convert("L"))
with pytest.warns(
DeprecationWarning, match="'mode' parameter for changing data types"
):
Image.fromarray(a, "1")
7 changes: 5 additions & 2 deletions docs/deprecations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,11 @@ Image.fromarray mode parameter

.. deprecated:: 11.3.0

The ``mode`` parameter in :py:meth:`~PIL.Image.fromarray()` has been deprecated. The
mode can be automatically determined from the object's shape and type instead.
Using the ``mode`` parameter in :py:meth:`~PIL.Image.fromarray()` to change data types
has been deprecated. Since pixel values do not contain information about palettes or
color spaces, the parameter can still be used to place grayscale L mode data within a
P mode image, or read RGB data as YCbCr for example. If omitted, the mode will be
automatically determined from the object's shape and type.

Saving I mode images as PNG
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
7 changes: 7 additions & 0 deletions docs/releasenotes/11.3.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ Image.fromarray mode parameter
The ``mode`` parameter in :py:meth:`~PIL.Image.fromarray()` has been deprecated. The
mode can be automatically determined from the object's shape and type instead.

.. note::

Since pixel values do not contain information about palettes or color spaces, part
of this functionality was restored in Pillow 12.0.0. The parameter can be used to
place grayscale L mode data within a P mode image, or read RGB data as YCbCr for
example.

Saving I mode images as PNG
^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
9 changes: 9 additions & 0 deletions docs/releasenotes/12.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,12 @@ others prepare for 3.14, and to ensure Pillow could be used immediately at the r
of 3.14.0 final (2025-10-07, :pep:`745`).

Pillow 12.0.0 now officially supports Python 3.14.

Image.fromarray mode parameter
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

In Pillow 11.3.0, the ``mode`` parameter in :py:meth:`~PIL.Image.fromarray()` was
deprecated. Part of this functionality has been restored in Pillow 12.0.0. Since pixel
values do not contain information about palettes or color spaces, the parameter can be
used to place grayscale L mode data within a P mode image, or read RGB data as YCbCr
for example.
80 changes: 39 additions & 41 deletions src/PIL/Image.py
Original file line number Diff line number Diff line change
Expand Up @@ -3258,19 +3258,10 @@ def fromarray(obj: SupportsArrayInterface, mode: str | None = None) -> Image:
transferred. This means that P and PA mode images will lose their palette.

:param obj: Object with array interface
:param mode: Optional mode to use when reading ``obj``. Will be determined from
type if ``None``. Deprecated.

This will not be used to convert the data after reading, but will be used to
change how the data is read::

from PIL import Image
import numpy as np
a = np.full((1, 1), 300)
im = Image.fromarray(a, mode="L")
im.getpixel((0, 0)) # 44
im = Image.fromarray(a, mode="RGB")
im.getpixel((0, 0)) # (44, 1, 0)
:param mode: Optional mode to use when reading ``obj``. Since pixel values do not
contain information about palettes or color spaces, this can be used to place
grayscale L mode data within a P mode image, or read RGB data as YCbCr for
example.

See: :ref:`concept-modes` for general information about modes.
:returns: An image object.
Expand All @@ -3281,21 +3272,28 @@ def fromarray(obj: SupportsArrayInterface, mode: str | None = None) -> Image:
shape = arr["shape"]
ndim = len(shape)
strides = arr.get("strides", None)
if mode is None:
try:
typekey = (1, 1) + shape[2:], arr["typestr"]
except KeyError as e:
try:
typekey = (1, 1) + shape[2:], arr["typestr"]
except KeyError as e:
if mode is not None:
typekey = None
color_modes: list[str] = []
else:
msg = "Cannot handle this data type"
raise TypeError(msg) from e
if typekey is not None:
try:
mode, rawmode = _fromarray_typemap[typekey]
typemode, rawmode, color_modes = _fromarray_typemap[typekey]
except KeyError as e:
typekey_shape, typestr = typekey
msg = f"Cannot handle this data type: {typekey_shape}, {typestr}"
raise TypeError(msg) from e
else:
deprecate("'mode' parameter", 13)
if mode is not None:
if mode != typemode and mode not in color_modes:
deprecate("'mode' parameter for changing data types", 13)
rawmode = mode
else:
mode = typemode
if mode in ["1", "L", "I", "P", "F"]:
ndmax = 2
elif mode == "RGB":
Expand Down Expand Up @@ -3392,29 +3390,29 @@ def fromqpixmap(im: ImageQt.QPixmap) -> ImageFile.ImageFile:


_fromarray_typemap = {
# (shape, typestr) => mode, rawmode
# (shape, typestr) => mode, rawmode, color modes
# first two members of shape are set to one
((1, 1), "|b1"): ("1", "1;8"),
((1, 1), "|u1"): ("L", "L"),
((1, 1), "|i1"): ("I", "I;8"),
((1, 1), "<u2"): ("I", "I;16"),
((1, 1), ">u2"): ("I", "I;16B"),
((1, 1), "<i2"): ("I", "I;16S"),
((1, 1), ">i2"): ("I", "I;16BS"),
((1, 1), "<u4"): ("I", "I;32"),
((1, 1), ">u4"): ("I", "I;32B"),
((1, 1), "<i4"): ("I", "I;32S"),
((1, 1), ">i4"): ("I", "I;32BS"),
((1, 1), "<f4"): ("F", "F;32F"),
((1, 1), ">f4"): ("F", "F;32BF"),
((1, 1), "<f8"): ("F", "F;64F"),
((1, 1), ">f8"): ("F", "F;64BF"),
((1, 1, 2), "|u1"): ("LA", "LA"),
((1, 1, 3), "|u1"): ("RGB", "RGB"),
((1, 1, 4), "|u1"): ("RGBA", "RGBA"),
((1, 1), "|b1"): ("1", "1;8", []),
((1, 1), "|u1"): ("L", "L", ["P"]),
((1, 1), "|i1"): ("I", "I;8", []),
((1, 1), "<u2"): ("I", "I;16", []),
((1, 1), ">u2"): ("I", "I;16B", []),
((1, 1), "<i2"): ("I", "I;16S", []),
((1, 1), ">i2"): ("I", "I;16BS", []),
((1, 1), "<u4"): ("I", "I;32", []),
((1, 1), ">u4"): ("I", "I;32B", []),
((1, 1), "<i4"): ("I", "I;32S", []),
((1, 1), ">i4"): ("I", "I;32BS", []),
((1, 1), "<f4"): ("F", "F;32F", []),
((1, 1), ">f4"): ("F", "F;32BF", []),
((1, 1), "<f8"): ("F", "F;64F", []),
((1, 1), ">f8"): ("F", "F;64BF", []),
((1, 1, 2), "|u1"): ("LA", "LA", ["La", "PA"]),
((1, 1, 3), "|u1"): ("RGB", "RGB", ["YCbCr", "LAB", "HSV"]),
((1, 1, 4), "|u1"): ("RGBA", "RGBA", ["RGBa", "RGBX", "CMYK"]),
# shortcuts:
((1, 1), f"{_ENDIAN}i4"): ("I", "I"),
((1, 1), f"{_ENDIAN}f4"): ("F", "F"),
((1, 1), f"{_ENDIAN}i4"): ("I", "I", []),
((1, 1), f"{_ENDIAN}f4"): ("F", "F", []),
}


Expand Down
Loading