From b9107c3493cd64ca2c5e328d00062a2eea2b35d4 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 28 Sep 2026 09:27:15 +0300 Subject: [PATCH 1/3] Add some benchmarks for ImageMath --- Tests/benchmarks.py | 65 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/Tests/benchmarks.py b/Tests/benchmarks.py index 2be1b49582e..45ff231efd0 100644 --- a/Tests/benchmarks.py +++ b/Tests/benchmarks.py @@ -14,12 +14,13 @@ import pytest -from PIL import Image, ImageChops, ImageDraw, ImageFilter, ImageFont +from PIL import Image, ImageChops, ImageDraw, ImageFilter, ImageFont, ImageMath from PIL.Image import Resampling, Transform, Transpose TYPE_CHECKING = False if TYPE_CHECKING: from collections.abc import Callable + from typing import Any BenchmarkSave = Callable[[Image.Image], None] @@ -728,6 +729,68 @@ def test_offset(bench: BenchmarkFixture, mode: str, size: tuple[int, int]) -> No bench(ImageChops.offset, im, 123, 45) +@pytest.mark.benchmark(group="imagemath") +@pytest.mark.parametrize( + "op", + [ + pytest.param(lambda a, b: a + b, id="add"), + pytest.param(lambda a, b: a * b, id="mul"), + pytest.param(lambda a, b: a / b, id="div"), + pytest.param(lambda a, b: ImageMath.imagemath_min(a, b), id="min"), + pytest.param(lambda a, b: a < b, id="lt"), + ], +) +@pytest.mark.parametrize("mode", ["I", "F"]) +@pytest.mark.parametrize("size", SIZES, ids=_format_size) +def test_imagemath_binary( + bench: BenchmarkFixture, + mode: str, + size: tuple[int, int], + op: Callable[[Any, Any], Any], +) -> None: + a = make_pillow_image(mode, size) + b = make_pillow_image(mode, size, seed=1) + result = bench( + ImageMath.lambda_eval, lambda args: op(args["a"], args["b"]), a=a, b=b + ) + assert result.size == a.size + + +@pytest.mark.benchmark(group="imagemath") +@pytest.mark.parametrize( + "op", + [ + pytest.param(lambda a: abs(a), id="abs"), + pytest.param(lambda a: -a, id="neg"), + pytest.param(lambda a: ~a, id="invert"), # int only + ], +) +@pytest.mark.parametrize("mode", ["I", "F"]) +@pytest.mark.parametrize("size", SIZES, ids=_format_size) +def test_imagemath_unary( + bench: BenchmarkFixture, mode: str, size: tuple[int, int], op: Callable[[Any], Any] +) -> None: + a = make_pillow_image(mode, size) + try: + result = bench(ImageMath.lambda_eval, lambda args: op(args["a"]), a=a) + except TypeError as e: + assert "invert" in str(e) + pytest.skip(str(e)) + assert result.size == a.size + + +@pytest.mark.benchmark(group="imagemath") +@pytest.mark.parametrize("mode", ["I", "F"]) +@pytest.mark.parametrize("size", SIZES, ids=_format_size) +def test_imagemath_scalar( + bench: BenchmarkFixture, mode: str, size: tuple[int, int] +) -> None: + a = make_pillow_image(mode, size) + bench.extra_info["label"] = [f"scalar mul {mode}"] + result = bench(ImageMath.lambda_eval, lambda args: args["a"] * 2, a=a) + assert result.size == a.size + + @pytest.mark.benchmark(group="compare") @pytest.mark.parametrize("scenario", ["equal", "one-pixel", "inverted"]) @pytest.mark.parametrize("mode", [*MODES, "I;16"]) From 6ff36ca0977e555739979baf7cc3fda04b967951 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 28 Sep 2026 11:55:53 +0300 Subject: [PATCH 2/3] Adjust type hints Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- Tests/benchmarks.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Tests/benchmarks.py b/Tests/benchmarks.py index 45ff231efd0..6af4c994b29 100644 --- a/Tests/benchmarks.py +++ b/Tests/benchmarks.py @@ -746,7 +746,7 @@ def test_imagemath_binary( bench: BenchmarkFixture, mode: str, size: tuple[int, int], - op: Callable[[Any, Any], Any], + op: Callable[[Image.Image, Image.Image], Any], ) -> None: a = make_pillow_image(mode, size) b = make_pillow_image(mode, size, seed=1) @@ -768,7 +768,10 @@ def test_imagemath_binary( @pytest.mark.parametrize("mode", ["I", "F"]) @pytest.mark.parametrize("size", SIZES, ids=_format_size) def test_imagemath_unary( - bench: BenchmarkFixture, mode: str, size: tuple[int, int], op: Callable[[Any], Any] + bench: BenchmarkFixture, + mode: str, + size: tuple[int, int], + op: Callable[[Image.Image], Any], ) -> None: a = make_pillow_image(mode, size) try: From fa03fe619134ed28551b915f548288989345b355 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 28 Sep 2026 11:59:04 +0300 Subject: [PATCH 3/3] Remove invert benchmark --- Tests/benchmarks.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Tests/benchmarks.py b/Tests/benchmarks.py index 6af4c994b29..d704dbb5af6 100644 --- a/Tests/benchmarks.py +++ b/Tests/benchmarks.py @@ -762,7 +762,6 @@ def test_imagemath_binary( [ pytest.param(lambda a: abs(a), id="abs"), pytest.param(lambda a: -a, id="neg"), - pytest.param(lambda a: ~a, id="invert"), # int only ], ) @pytest.mark.parametrize("mode", ["I", "F"]) @@ -774,11 +773,7 @@ def test_imagemath_unary( op: Callable[[Image.Image], Any], ) -> None: a = make_pillow_image(mode, size) - try: - result = bench(ImageMath.lambda_eval, lambda args: op(args["a"]), a=a) - except TypeError as e: - assert "invert" in str(e) - pytest.skip(str(e)) + result = bench(ImageMath.lambda_eval, lambda args: op(args["a"]), a=a) assert result.size == a.size