A few new failing tests with Pillow 10.4.0
musicinmybrain opened this issue · 2 comments
I see a few test regressions that appear to be associated with the update from Pillow 10.3.0 to 10.4.0:
$ gh repo clone mikedh/trimesh
$ cd trimesh
$ python3.12 -m venv _e
$ . _e/bin/activate
(_e) $ pip install -e .[all,test]
(_e) $ python -m pytest -v
=========================================================================================== short test summary info ============================================================================================
FAILED tests/test_boolean.py::BooleanTest::test_boolean - subprocess.CalledProcessError: Command '['/usr/bin/blender', '--background', '--python', '/tmp/tmp8kugzuuk']' returned non-zero exit status 127.
FAILED tests/test_boolean.py::BooleanTest::test_empty - subprocess.CalledProcessError: Command '['/usr/bin/blender', '--background', '--python', '/tmp/tmpuim2cl9q']' returned non-zero exit status 127.
FAILED tests/test_boolean.py::BooleanTest::test_multiple - subprocess.CalledProcessError: Command '['/usr/bin/blender', '--background', '--python', '/tmp/tmpcl7n6omm']' returned non-zero exit status 127.
FAILED tests/test_creation.py::CreationTest::test_path_sweep - ValueError: zero-size array to reduction operation maximum which has no identity
FAILED tests/test_creation.py::CreationTest::test_triangulate - AssertionError: assert np.False_
FAILED tests/test_creation.py::CreationTest::test_triangulate_plumbing - AssertionError: assert np.False_
FAILED tests/test_gltf.py::GLTFTest::test_same_name - ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
FAILED tests/test_gltf.py::GLTFTest::test_vertex_colors_import - AssertionError: Imported vertex color is not of expected value: got [ 1 0 1 255], expected [255 0 255 255]
FAILED tests/test_medial.py::MedialTests::test_medial - OverflowError: Python int too large to convert to C long
FAILED tests/test_paths.py::VectorTests::test_discrete - OverflowError: Python int too large to convert to C long
FAILED tests/test_paths.py::VectorTests::test_empty - OverflowError: Python int too large to convert to C long
FAILED tests/test_section.py::SliceTest::test_cap - assert False
FAILED tests/test_section.py::SliceTest::test_cap_coplanar - assert False
FAILED tests/test_section.py::SliceTest::test_slice_onplane - assert False
FAILED tests/test_sweep.py::test_simple_closed - AssertionError
FAILED tests/test_sweep.py::test_simple_extrude - ValueError: zero-size array to reduction operation maximum which has no identity
FAILED tests/test_sweep.py::test_simple_open - ValueError: zero-size array to reduction operation maximum which has no identity
FAILED tests/test_sweep.py::test_spline_3D - ValueError: zero-size array to reduction operation maximum which has no identity
FAILED tests/test_sweep.py::test_screw - ValueError: zero-size array to reduction operation maximum which has no identity
FAILED tests/test_texture.py::TextureTest::test_pbr_material_fusion - ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
FAILED tests/test_texture.py::TextureTest::test_upsize - ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
FAILED tests/test_unwrap.py::UnwrapTest::test_blender_unwrap - subprocess.CalledProcessError: Command '['/usr/bin/blender', '--background', '--python', '/tmp/tmpr4ccikox']' returned non-zero exit status 127.
=========================================================================== 22 failed, 598 passed, 274 warnings in 290.71s (0:04:50) ===========================================================================
Now, downgrading Pillow:
(_e) $ pip install pillow==10.3.0
(_e) $ python -m pytest -v
=========================================================================================== short test summary info ============================================================================================
FAILED tests/test_boolean.py::BooleanTest::test_boolean - subprocess.CalledProcessError: Command '['/usr/bin/blender', '--background', '--python', '/tmp/tmpl559s849']' returned non-zero exit status 127.
FAILED tests/test_boolean.py::BooleanTest::test_empty - subprocess.CalledProcessError: Command '['/usr/bin/blender', '--background', '--python', '/tmp/tmp81bp1hok']' returned non-zero exit status 127.
FAILED tests/test_boolean.py::BooleanTest::test_multiple - subprocess.CalledProcessError: Command '['/usr/bin/blender', '--background', '--python', '/tmp/tmpt_hre7jb']' returned non-zero exit status 127.
FAILED tests/test_creation.py::CreationTest::test_path_sweep - ValueError: zero-size array to reduction operation maximum which has no identity
FAILED tests/test_creation.py::CreationTest::test_triangulate - AssertionError: assert np.False_
FAILED tests/test_creation.py::CreationTest::test_triangulate_plumbing - AssertionError: assert np.False_
FAILED tests/test_gltf.py::GLTFTest::test_vertex_colors_import - AssertionError: Imported vertex color is not of expected value: got [ 1 0 1 255], expected [255 0 255 255]
FAILED tests/test_medial.py::MedialTests::test_medial - OverflowError: Python int too large to convert to C long
FAILED tests/test_paths.py::VectorTests::test_discrete - OverflowError: Python int too large to convert to C long
FAILED tests/test_paths.py::VectorTests::test_empty - OverflowError: Python int too large to convert to C long
FAILED tests/test_section.py::SliceTest::test_cap - assert False
FAILED tests/test_section.py::SliceTest::test_cap_coplanar - assert False
FAILED tests/test_section.py::SliceTest::test_slice_onplane - assert False
FAILED tests/test_sweep.py::test_simple_closed - AssertionError
FAILED tests/test_sweep.py::test_simple_extrude - ValueError: zero-size array to reduction operation maximum which has no identity
FAILED tests/test_sweep.py::test_simple_open - ValueError: zero-size array to reduction operation maximum which has no identity
FAILED tests/test_sweep.py::test_spline_3D - ValueError: zero-size array to reduction operation maximum which has no identity
FAILED tests/test_sweep.py::test_screw - ValueError: zero-size array to reduction operation maximum which has no identity
FAILED tests/test_unwrap.py::UnwrapTest::test_blender_unwrap - subprocess.CalledProcessError: Command '['/usr/bin/blender', '--background', '--python', '/tmp/tmp03x5p5xr']' returned non-zero exit status 127.
=========================================================================== 19 failed, 601 passed, 273 warnings in 292.99s (0:04:52) ===========================================================================
The regressions of interest here are therefore those failures that appear only in the first list:
FAILED tests/test_gltf.py::GLTFTest::test_same_name - ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
FAILED tests/test_texture.py::TextureTest::test_pbr_material_fusion - ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
FAILED tests/test_texture.py::TextureTest::test_upsize - ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
There isn’t really anything obviously relevant in the “curated” upstream changelog; meanwhile the raw list of changes is too verbose to easily peruse. I’ll try to keep investigating this.
Sample detailed output from a failing test:
___________________________________________________________________________________________ GLTFTest.test_same_name ____________________________________________________________________________________________
self = <tests.test_gltf.GLTFTest testMethod=test_same_name>
def test_same_name(self):
s = g.get_mesh("TestScene.gltf")
# hardcode correct bounds to check against
> bounds = s.dump(concatenate=True).bounds
tests/test_gltf.py:745:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
trimesh/scene/scene.py:893: in dump
return util.concatenate(result)
trimesh/util.py:1499: in concatenate
raise E
trimesh/util.py:1494: in concatenate
visual = is_mesh[0].visual.concatenate([m.visual for m in is_mesh[1:]])
trimesh/visual/texture.py:216: in concatenate
return concatenate(self, others)
trimesh/visual/objects.py:80: in concatenate
new_mat, new_uv = pack(materials=mats, uvs=uvs)
trimesh/visual/material.py:1014: in pack
images = resize_images(images, unpadded_sizes)
trimesh/visual/material.py:934: in resize_images
img = img.resize(size)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <PIL.Image.Image image mode=RGBA size=2x2 at 0x7FCD6A87A540>, size = array([2, 2]), resample = <Resampling.BICUBIC: 3>, box = (0, 0, 2, 2), reducing_gap = None
def resize(
self,
size: tuple[int, int],
resample: int | None = None,
box: tuple[float, float, float, float] | None = None,
reducing_gap: float | None = None,
) -> Image:
"""
Returns a resized copy of this image.
:param size: The requested size in pixels, as a 2-tuple:
(width, height).
:param resample: An optional resampling filter. This can be
one of :py:data:`Resampling.NEAREST`, :py:data:`Resampling.BOX`,
:py:data:`Resampling.BILINEAR`, :py:data:`Resampling.HAMMING`,
:py:data:`Resampling.BICUBIC` or :py:data:`Resampling.LANCZOS`.
If the image has mode "1" or "P", it is always set to
:py:data:`Resampling.NEAREST`. If the image mode specifies a number
of bits, such as "I;16", then the default filter is
:py:data:`Resampling.NEAREST`. Otherwise, the default filter is
:py:data:`Resampling.BICUBIC`. See: :ref:`concept-filters`.
:param box: An optional 4-tuple of floats providing
the source image region to be scaled.
The values must be within (0, 0, width, height) rectangle.
If omitted or None, the entire source is used.
:param reducing_gap: Apply optimization by resizing the image
in two steps. First, reducing the image by integer times
using :py:meth:`~PIL.Image.Image.reduce`.
Second, resizing using regular resampling. The last step
changes size no less than by ``reducing_gap`` times.
``reducing_gap`` may be None (no first step is performed)
or should be greater than 1.0. The bigger ``reducing_gap``,
the closer the result to the fair resampling.
The smaller ``reducing_gap``, the faster resizing.
With ``reducing_gap`` greater or equal to 3.0, the result is
indistinguishable from fair resampling in most cases.
The default value is None (no optimization).
:returns: An :py:class:`~PIL.Image.Image` object.
"""
if resample is None:
type_special = ";" in self.mode
resample = Resampling.NEAREST if type_special else Resampling.BICUBIC
elif resample not in (
Resampling.NEAREST,
Resampling.BILINEAR,
Resampling.BICUBIC,
Resampling.LANCZOS,
Resampling.BOX,
Resampling.HAMMING,
):
msg = f"Unknown resampling filter ({resample})."
filters = [
f"{filter[1]} ({filter[0]})"
for filter in (
(Resampling.NEAREST, "Image.Resampling.NEAREST"),
(Resampling.LANCZOS, "Image.Resampling.LANCZOS"),
(Resampling.BILINEAR, "Image.Resampling.BILINEAR"),
(Resampling.BICUBIC, "Image.Resampling.BICUBIC"),
(Resampling.BOX, "Image.Resampling.BOX"),
(Resampling.HAMMING, "Image.Resampling.HAMMING"),
)
]
msg += f" Use {', '.join(filters[:-1])} or {filters[-1]}"
raise ValueError(msg)
if reducing_gap is not None and reducing_gap < 1.0:
msg = "reducing_gap must be 1.0 or greater"
raise ValueError(msg)
self.load()
if box is None:
box = (0, 0) + self.size
> if self.size == size and box == (0, 0) + self.size:
E ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
_e/lib64/python3.12/site-packages/PIL/Image.py:2297: ValueError
The key difference seems to be this.
With numpy==2.0.0
, pillow==10.3.0
:
>>> from PIL import Image
>>> from numpy import array
>>> x = Image.new('RGB', (2, 4))
>>> x.resize(array([3, 7]))
<PIL.Image.Image image mode=RGB size=3x7 at 0x7FB58A5866C0>
With numpy==2.0.0
, pillow==10.4.0
:
>>> from PIL import Image
>>> from numpy import array
>>> x = Image.new('RGB', (2, 4))
>>> x.resize(array([3, 7]))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/ben/fedora/neuro-sig/python-trimesh/_e/lib64/python3.12/site-packages/PIL/Image.py", line 2297, in resize
if self.size == size and box == (0, 0) + self.size:
^^^^^^^^^^^^^^^^^
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
So the question is whether this is a regression in Pillow, or whether passing a numpy.ndarray
to Image.resize()
was always “wrong,” since https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.resize calls for a “tuple,” and it only happened to work.