Skip to content
Merged
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
63 changes: 62 additions & 1 deletion Tests/benchmarks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand Down Expand Up @@ -728,6 +729,66 @@ 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[[Image.Image, Image.Image], 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.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[[Image.Image], Any],
) -> None:
a = make_pillow_image(mode, size)
result = bench(ImageMath.lambda_eval, lambda args: op(args["a"]), a=a)
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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why isn't this just another parameter on test_imagemath_unary?
pytest.param(lambda a: a * 2, id=...),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You requested my review again, so I think you missed this comment.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, yeah, I did. It's not an unary operation in _imagingmath terms, it's a special case of binary operations.

As noted in the PR description for #10072, the binary operation with one scalar operand case could use its own class of optimizations.



@pytest.mark.benchmark(group="compare")
@pytest.mark.parametrize("scenario", ["equal", "one-pixel", "inverted"])
@pytest.mark.parametrize("mode", [*MODES, "I;16"])
Expand Down
Loading