2023-12-21 14:13:31 +03:00
|
|
|
from __future__ import annotations
|
2024-01-20 14:23:03 +03:00
|
|
|
|
2024-06-23 23:59:00 +03:00
|
|
|
from typing import TYPE_CHECKING, Any
|
2024-02-12 01:28:53 +03:00
|
|
|
|
2020-01-27 14:46:52 +03:00
|
|
|
import pytest
|
2022-04-09 01:53:27 +03:00
|
|
|
from packaging.version import parse as parse_version
|
2020-08-07 13:28:33 +03:00
|
|
|
|
2012-10-16 00:26:38 +04:00
|
|
|
from PIL import Image
|
|
|
|
|
2020-01-27 14:46:52 +03:00
|
|
|
from .helper import hopper
|
2019-07-06 23:40:53 +03:00
|
|
|
|
2021-04-03 05:20:58 +03:00
|
|
|
numpy = pytest.importorskip("numpy", reason="NumPy not installed")
|
|
|
|
|
2014-09-05 14:03:56 +04:00
|
|
|
im = hopper().resize((128, 100))
|
2012-10-16 00:26:38 +04:00
|
|
|
|
2024-06-23 23:59:00 +03:00
|
|
|
if TYPE_CHECKING:
|
|
|
|
import numpy.typing as npt
|
|
|
|
|
2014-06-10 13:10:47 +04:00
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_toarray() -> None:
|
2024-02-12 01:28:53 +03:00
|
|
|
def test(mode: str) -> tuple[tuple[int, ...], str, int]:
|
2021-04-03 05:20:58 +03:00
|
|
|
ai = numpy.array(im.convert(mode))
|
|
|
|
return ai.shape, ai.dtype.str, ai.nbytes
|
2020-01-27 14:46:52 +03:00
|
|
|
|
2024-06-23 23:59:00 +03:00
|
|
|
def test_with_dtype(dtype: npt.DTypeLike) -> None:
|
2021-07-01 14:09:40 +03:00
|
|
|
ai = numpy.array(im, dtype=dtype)
|
2024-09-05 14:29:19 +03:00
|
|
|
assert ai.dtype.type is dtype
|
2021-07-01 14:09:40 +03:00
|
|
|
|
2021-04-03 05:20:58 +03:00
|
|
|
# assert test("1") == ((100, 128), '|b1', 1600))
|
|
|
|
assert test("L") == ((100, 128), "|u1", 12800)
|
2020-01-27 14:46:52 +03:00
|
|
|
|
|
|
|
# FIXME: wrong?
|
2021-04-03 05:20:58 +03:00
|
|
|
assert test("I") == ((100, 128), Image._ENDIAN + "i4", 51200)
|
2020-01-27 14:46:52 +03:00
|
|
|
# FIXME: wrong?
|
2021-04-03 05:20:58 +03:00
|
|
|
assert test("F") == ((100, 128), Image._ENDIAN + "f4", 51200)
|
|
|
|
|
|
|
|
assert test("LA") == ((100, 128, 2), "|u1", 25600)
|
|
|
|
assert test("RGB") == ((100, 128, 3), "|u1", 38400)
|
|
|
|
assert test("RGBA") == ((100, 128, 4), "|u1", 51200)
|
|
|
|
assert test("RGBX") == ((100, 128, 4), "|u1", 51200)
|
2020-01-27 14:46:52 +03:00
|
|
|
|
2021-07-01 15:06:13 +03:00
|
|
|
test_with_dtype(numpy.float64)
|
2021-07-01 14:09:40 +03:00
|
|
|
test_with_dtype(numpy.uint8)
|
|
|
|
|
2022-09-17 13:11:55 +03:00
|
|
|
with Image.open("Tests/images/truncated_jpeg.jpg") as im_truncated:
|
|
|
|
if parse_version(numpy.__version__) >= parse_version("1.23"):
|
2022-04-09 01:53:27 +03:00
|
|
|
with pytest.raises(OSError):
|
|
|
|
numpy.array(im_truncated)
|
2022-09-17 13:11:55 +03:00
|
|
|
else:
|
2024-08-23 12:38:07 +03:00
|
|
|
with pytest.warns(DeprecationWarning):
|
2022-09-17 13:11:55 +03:00
|
|
|
numpy.array(im_truncated)
|
2020-01-27 14:46:52 +03:00
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_fromarray() -> None:
|
2020-01-27 14:46:52 +03:00
|
|
|
class Wrapper:
|
2021-08-12 14:50:09 +03:00
|
|
|
"""Class with API matching Image.fromarray"""
|
2020-01-27 14:46:52 +03:00
|
|
|
|
2024-02-12 01:28:53 +03:00
|
|
|
def __init__(self, img: Image.Image, arr_params: dict[str, Any]) -> None:
|
2020-01-27 14:46:52 +03:00
|
|
|
self.img = img
|
|
|
|
self.__array_interface__ = arr_params
|
|
|
|
|
2024-02-12 01:28:53 +03:00
|
|
|
def tobytes(self) -> bytes:
|
2020-01-27 14:46:52 +03:00
|
|
|
return self.img.tobytes()
|
|
|
|
|
2024-02-12 01:28:53 +03:00
|
|
|
def test(mode: str) -> tuple[str, tuple[int, int], bool]:
|
2020-01-27 14:46:52 +03:00
|
|
|
i = im.convert(mode)
|
2021-04-03 05:20:58 +03:00
|
|
|
a = numpy.array(i)
|
2020-01-27 14:46:52 +03:00
|
|
|
# Make wrapper instance for image, new array interface
|
2021-04-03 05:20:58 +03:00
|
|
|
wrapped = Wrapper(
|
|
|
|
i,
|
|
|
|
{
|
|
|
|
"shape": a.shape,
|
|
|
|
"typestr": a.dtype.str,
|
|
|
|
"version": 3,
|
|
|
|
"data": a.data,
|
|
|
|
"strides": 1, # pretend it's non-contiguous
|
|
|
|
},
|
|
|
|
)
|
2020-01-27 14:46:52 +03:00
|
|
|
out = Image.fromarray(wrapped)
|
|
|
|
return out.mode, out.size, list(i.getdata()) == list(out.getdata())
|
|
|
|
|
|
|
|
# assert test("1") == ("1", (128, 100), True)
|
|
|
|
assert test("L") == ("L", (128, 100), True)
|
|
|
|
assert test("I") == ("I", (128, 100), True)
|
|
|
|
assert test("F") == ("F", (128, 100), True)
|
|
|
|
assert test("LA") == ("LA", (128, 100), True)
|
|
|
|
assert test("RGB") == ("RGB", (128, 100), True)
|
|
|
|
assert test("RGBA") == ("RGBA", (128, 100), True)
|
|
|
|
assert test("RGBX") == ("RGBA", (128, 100), True)
|
|
|
|
|
|
|
|
# Test mode is None with no "typestr" in the array interface
|
2024-06-01 14:31:53 +03:00
|
|
|
wrapped = Wrapper(hopper("L"), {"shape": (100, 128)})
|
2020-01-27 14:46:52 +03:00
|
|
|
with pytest.raises(TypeError):
|
|
|
|
Image.fromarray(wrapped)
|
2022-05-09 11:50:54 +03:00
|
|
|
|
|
|
|
|
2024-04-03 12:00:40 +03:00
|
|
|
def test_fromarray_strides_without_tobytes() -> None:
|
|
|
|
class Wrapper:
|
|
|
|
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)})
|
|
|
|
Image.fromarray(wrapped, "L")
|
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_fromarray_palette() -> None:
|
2022-05-09 11:50:54 +03:00
|
|
|
# Arrange
|
|
|
|
i = im.convert("L")
|
|
|
|
a = numpy.array(i)
|
|
|
|
|
|
|
|
# Act
|
|
|
|
out = Image.fromarray(a, "P")
|
|
|
|
|
|
|
|
# Assert that the Python and C palettes match
|
2024-08-02 16:30:27 +03:00
|
|
|
assert out.palette is not None
|
2022-05-09 11:50:54 +03:00
|
|
|
assert len(out.palette.colors) == len(out.im.getpalette()) / 3
|