Return boolean from ImageMath comparison operations

This commit is contained in:
Andrew Murray 2024-07-18 22:52:51 +10:00
parent 73dfe67736
commit d991df651b
4 changed files with 120 additions and 205 deletions

View File

@ -187,8 +187,9 @@ def test_compare() -> None:
)
== "I 2"
)
assert pixel(ImageMath.lambda_eval(lambda args: args["A"] == 1, **images)) == "I 1"
assert pixel(ImageMath.lambda_eval(lambda args: args["A"] == 2, **images)) == "I 0"
for i in range(1, 3):
assert ImageMath.lambda_eval(lambda args: args["A"] == i, **images) is False
assert ImageMath.lambda_eval(lambda args: args["A"] != i, **images) is True
def test_one_image_larger() -> None:
@ -310,198 +311,108 @@ def test_bitwise_rightshift() -> None:
def test_logical_eq() -> None:
assert (
pixel(ImageMath.lambda_eval(lambda args: args["A"] == args["A"], A=A)) == "I 1"
)
assert (
pixel(ImageMath.lambda_eval(lambda args: args["B"] == args["B"], B=B)) == "I 1"
)
assert (
pixel(ImageMath.lambda_eval(lambda args: args["A"] == args["B"], A=A, B=B))
== "I 0"
)
assert (
pixel(ImageMath.lambda_eval(lambda args: args["B"] == args["A"], A=A, B=B))
== "I 0"
)
assert ImageMath.lambda_eval(lambda args: args["A"] == args["A"], A=A) is True
assert ImageMath.lambda_eval(lambda args: args["B"] == args["B"], B=B) is True
assert ImageMath.lambda_eval(lambda args: args["A"] == args["B"], A=A, B=B) is False
assert ImageMath.lambda_eval(lambda args: args["B"] == args["A"], A=A, B=B) is False
def test_logical_ne() -> None:
assert (
pixel(ImageMath.lambda_eval(lambda args: args["A"] != args["A"], A=A)) == "I 0"
)
assert (
pixel(ImageMath.lambda_eval(lambda args: args["B"] != args["B"], B=B)) == "I 0"
)
assert (
pixel(ImageMath.lambda_eval(lambda args: args["A"] != args["B"], A=A, B=B))
== "I 1"
)
assert (
pixel(ImageMath.lambda_eval(lambda args: args["B"] != args["A"], A=A, B=B))
== "I 1"
)
assert ImageMath.lambda_eval(lambda args: args["A"] != args["A"], A=A) is False
assert ImageMath.lambda_eval(lambda args: args["B"] != args["B"], B=B) is False
assert ImageMath.lambda_eval(lambda args: args["A"] != args["B"], A=A, B=B) is True
assert ImageMath.lambda_eval(lambda args: args["B"] != args["A"], A=A, B=B) is True
def test_logical_lt() -> None:
assert (
pixel(ImageMath.lambda_eval(lambda args: args["A"] < args["A"], A=A)) == "I 0"
)
assert (
pixel(ImageMath.lambda_eval(lambda args: args["B"] < args["B"], B=B)) == "I 0"
)
assert (
pixel(ImageMath.lambda_eval(lambda args: args["A"] < args["B"], A=A, B=B))
== "I 1"
)
assert (
pixel(ImageMath.lambda_eval(lambda args: args["B"] < args["A"], A=A, B=B))
== "I 0"
)
assert ImageMath.lambda_eval(lambda args: args["A"] < args["A"], A=A) is False
assert ImageMath.lambda_eval(lambda args: args["B"] < args["B"], B=B) is False
assert ImageMath.lambda_eval(lambda args: args["A"] < args["B"], A=A, B=B) is True
assert ImageMath.lambda_eval(lambda args: args["B"] < args["A"], A=A, B=B) is False
def test_logical_le() -> None:
assert (
pixel(ImageMath.lambda_eval(lambda args: args["A"] <= args["A"], A=A)) == "I 1"
)
assert (
pixel(ImageMath.lambda_eval(lambda args: args["B"] <= args["B"], B=B)) == "I 1"
)
assert (
pixel(ImageMath.lambda_eval(lambda args: args["A"] <= args["B"], A=A, B=B))
== "I 1"
)
assert (
pixel(ImageMath.lambda_eval(lambda args: args["B"] <= args["A"], A=A, B=B))
== "I 0"
)
assert ImageMath.lambda_eval(lambda args: args["A"] <= args["A"], A=A) is True
assert ImageMath.lambda_eval(lambda args: args["B"] <= args["B"], B=B) is True
assert ImageMath.lambda_eval(lambda args: args["A"] <= args["B"], A=A, B=B) is True
assert ImageMath.lambda_eval(lambda args: args["B"] <= args["A"], A=A, B=B) is False
def test_logical_gt() -> None:
assert (
pixel(ImageMath.lambda_eval(lambda args: args["A"] > args["A"], A=A)) == "I 0"
)
assert (
pixel(ImageMath.lambda_eval(lambda args: args["B"] > args["B"], B=B)) == "I 0"
)
assert (
pixel(ImageMath.lambda_eval(lambda args: args["A"] > args["B"], A=A, B=B))
== "I 0"
)
assert (
pixel(ImageMath.lambda_eval(lambda args: args["B"] > args["A"], A=A, B=B))
== "I 1"
)
assert ImageMath.lambda_eval(lambda args: args["A"] > args["A"], A=A) is False
assert ImageMath.lambda_eval(lambda args: args["B"] > args["B"], B=B) is False
assert ImageMath.lambda_eval(lambda args: args["A"] > args["B"], A=A, B=B) is False
assert ImageMath.lambda_eval(lambda args: args["B"] > args["A"], A=A, B=B) is True
def test_logical_ge() -> None:
assert (
pixel(ImageMath.lambda_eval(lambda args: args["A"] >= args["A"], A=A)) == "I 1"
)
assert (
pixel(ImageMath.lambda_eval(lambda args: args["B"] >= args["B"], B=B)) == "I 1"
)
assert (
pixel(ImageMath.lambda_eval(lambda args: args["A"] >= args["B"], A=A, B=B))
== "I 0"
)
assert (
pixel(ImageMath.lambda_eval(lambda args: args["B"] >= args["A"], A=A, B=B))
== "I 1"
)
assert ImageMath.lambda_eval(lambda args: args["A"] >= args["A"], A=A) is True
assert ImageMath.lambda_eval(lambda args: args["B"] >= args["B"], B=B) is True
assert ImageMath.lambda_eval(lambda args: args["A"] >= args["B"], A=A, B=B) is False
assert ImageMath.lambda_eval(lambda args: args["B"] >= args["A"], A=A, B=B) is True
def test_logical_equal() -> None:
assert (
pixel(
ImageMath.lambda_eval(lambda args: args["equal"](args["A"], args["A"]), A=A)
)
== "I 1"
ImageMath.lambda_eval(lambda args: args["equal"](args["A"], args["A"]), A=A)
is True
)
assert (
pixel(
ImageMath.lambda_eval(lambda args: args["equal"](args["B"], args["B"]), B=B)
)
== "I 1"
ImageMath.lambda_eval(lambda args: args["equal"](args["B"], args["B"]), B=B)
is True
)
assert (
pixel(
ImageMath.lambda_eval(lambda args: args["equal"](args["Z"], args["Z"]), Z=Z)
)
== "I 1"
ImageMath.lambda_eval(lambda args: args["equal"](args["Z"], args["Z"]), Z=Z)
is True
)
assert (
pixel(
ImageMath.lambda_eval(
lambda args: args["equal"](args["A"], args["B"]), A=A, B=B
)
ImageMath.lambda_eval(
lambda args: args["equal"](args["A"], args["B"]), A=A, B=B
)
== "I 0"
is False
)
assert (
pixel(
ImageMath.lambda_eval(
lambda args: args["equal"](args["B"], args["A"]), A=A, B=B
)
ImageMath.lambda_eval(
lambda args: args["equal"](args["B"], args["A"]), A=A, B=B
)
== "I 0"
is False
)
assert (
pixel(
ImageMath.lambda_eval(
lambda args: args["equal"](args["A"], args["Z"]), A=A, Z=Z
)
ImageMath.lambda_eval(
lambda args: args["equal"](args["A"], args["Z"]), A=A, Z=Z
)
== "I 0"
is False
)
def test_logical_not_equal() -> None:
assert (
pixel(
ImageMath.lambda_eval(
lambda args: args["notequal"](args["A"], args["A"]), A=A
)
)
== "I 0"
ImageMath.lambda_eval(lambda args: args["notequal"](args["A"], args["A"]), A=A)
is False
)
assert (
pixel(
ImageMath.lambda_eval(
lambda args: args["notequal"](args["B"], args["B"]), B=B
)
)
== "I 0"
ImageMath.lambda_eval(lambda args: args["notequal"](args["B"], args["B"]), B=B)
is False
)
assert (
pixel(
ImageMath.lambda_eval(
lambda args: args["notequal"](args["Z"], args["Z"]), Z=Z
)
)
== "I 0"
ImageMath.lambda_eval(lambda args: args["notequal"](args["Z"], args["Z"]), Z=Z)
is False
)
assert (
pixel(
ImageMath.lambda_eval(
lambda args: args["notequal"](args["A"], args["B"]), A=A, B=B
)
ImageMath.lambda_eval(
lambda args: args["notequal"](args["A"], args["B"]), A=A, B=B
)
== "I 1"
is True
)
assert (
pixel(
ImageMath.lambda_eval(
lambda args: args["notequal"](args["B"], args["A"]), A=A, B=B
)
ImageMath.lambda_eval(
lambda args: args["notequal"](args["B"], args["A"]), A=A, B=B
)
== "I 1"
is True
)
assert (
pixel(
ImageMath.lambda_eval(
lambda args: args["notequal"](args["A"], args["Z"]), A=A, Z=Z
)
ImageMath.lambda_eval(
lambda args: args["notequal"](args["A"], args["Z"]), A=A, Z=Z
)
== "I 1"
is True
)

View File

@ -104,8 +104,8 @@ def test_convert() -> None:
def test_compare() -> None:
assert pixel(ImageMath.unsafe_eval("min(A, B)", **images)) == "I 1"
assert pixel(ImageMath.unsafe_eval("max(A, B)", **images)) == "I 2"
assert pixel(ImageMath.unsafe_eval("A == 1", **images)) == "I 1"
assert pixel(ImageMath.unsafe_eval("A == 2", **images)) == "I 0"
assert ImageMath.unsafe_eval("A == 1", **images) is False
assert ImageMath.unsafe_eval("A == 2", **images) is False
def test_one_image_larger() -> None:
@ -169,60 +169,60 @@ def test_bitwise_rightshift() -> None:
def test_logical_eq() -> None:
assert pixel(ImageMath.unsafe_eval("A==A", A=A)) == "I 1"
assert pixel(ImageMath.unsafe_eval("B==B", B=B)) == "I 1"
assert pixel(ImageMath.unsafe_eval("A==B", A=A, B=B)) == "I 0"
assert pixel(ImageMath.unsafe_eval("B==A", A=A, B=B)) == "I 0"
assert ImageMath.unsafe_eval("A==A", A=A) is True
assert ImageMath.unsafe_eval("B==B", B=B) is True
assert ImageMath.unsafe_eval("A==B", A=A, B=B) is False
assert ImageMath.unsafe_eval("B==A", A=A, B=B) is False
def test_logical_ne() -> None:
assert pixel(ImageMath.unsafe_eval("A!=A", A=A)) == "I 0"
assert pixel(ImageMath.unsafe_eval("B!=B", B=B)) == "I 0"
assert pixel(ImageMath.unsafe_eval("A!=B", A=A, B=B)) == "I 1"
assert pixel(ImageMath.unsafe_eval("B!=A", A=A, B=B)) == "I 1"
assert ImageMath.unsafe_eval("A!=A", A=A) is False
assert ImageMath.unsafe_eval("B!=B", B=B) is False
assert ImageMath.unsafe_eval("A!=B", A=A, B=B) is True
assert ImageMath.unsafe_eval("B!=A", A=A, B=B) is True
def test_logical_lt() -> None:
assert pixel(ImageMath.unsafe_eval("A<A", A=A)) == "I 0"
assert pixel(ImageMath.unsafe_eval("B<B", B=B)) == "I 0"
assert pixel(ImageMath.unsafe_eval("A<B", A=A, B=B)) == "I 1"
assert pixel(ImageMath.unsafe_eval("B<A", A=A, B=B)) == "I 0"
assert ImageMath.unsafe_eval("A<A", A=A) is False
assert ImageMath.unsafe_eval("B<B", B=B) is False
assert ImageMath.unsafe_eval("A<B", A=A, B=B) is True
assert ImageMath.unsafe_eval("B<A", A=A, B=B) is False
def test_logical_le() -> None:
assert pixel(ImageMath.unsafe_eval("A<=A", A=A)) == "I 1"
assert pixel(ImageMath.unsafe_eval("B<=B", B=B)) == "I 1"
assert pixel(ImageMath.unsafe_eval("A<=B", A=A, B=B)) == "I 1"
assert pixel(ImageMath.unsafe_eval("B<=A", A=A, B=B)) == "I 0"
assert ImageMath.unsafe_eval("A<=A", A=A) is True
assert ImageMath.unsafe_eval("B<=B", B=B) is True
assert ImageMath.unsafe_eval("A<=B", A=A, B=B) is True
assert ImageMath.unsafe_eval("B<=A", A=A, B=B) is False
def test_logical_gt() -> None:
assert pixel(ImageMath.unsafe_eval("A>A", A=A)) == "I 0"
assert pixel(ImageMath.unsafe_eval("B>B", B=B)) == "I 0"
assert pixel(ImageMath.unsafe_eval("A>B", A=A, B=B)) == "I 0"
assert pixel(ImageMath.unsafe_eval("B>A", A=A, B=B)) == "I 1"
assert ImageMath.unsafe_eval("A>A", A=A) is False
assert ImageMath.unsafe_eval("B>B", B=B) is False
assert ImageMath.unsafe_eval("A>B", A=A, B=B) is False
assert ImageMath.unsafe_eval("B>A", A=A, B=B) is True
def test_logical_ge() -> None:
assert pixel(ImageMath.unsafe_eval("A>=A", A=A)) == "I 1"
assert pixel(ImageMath.unsafe_eval("B>=B", B=B)) == "I 1"
assert pixel(ImageMath.unsafe_eval("A>=B", A=A, B=B)) == "I 0"
assert pixel(ImageMath.unsafe_eval("B>=A", A=A, B=B)) == "I 1"
assert ImageMath.unsafe_eval("A>=A", A=A) is True
assert ImageMath.unsafe_eval("B>=B", B=B) is True
assert ImageMath.unsafe_eval("A>=B", A=A, B=B) is False
assert ImageMath.unsafe_eval("B>=A", A=A, B=B) is True
def test_logical_equal() -> None:
assert pixel(ImageMath.unsafe_eval("equal(A, A)", A=A)) == "I 1"
assert pixel(ImageMath.unsafe_eval("equal(B, B)", B=B)) == "I 1"
assert pixel(ImageMath.unsafe_eval("equal(Z, Z)", Z=Z)) == "I 1"
assert pixel(ImageMath.unsafe_eval("equal(A, B)", A=A, B=B)) == "I 0"
assert pixel(ImageMath.unsafe_eval("equal(B, A)", A=A, B=B)) == "I 0"
assert pixel(ImageMath.unsafe_eval("equal(A, Z)", A=A, Z=Z)) == "I 0"
assert ImageMath.unsafe_eval("equal(A, A)", A=A) is True
assert ImageMath.unsafe_eval("equal(B, B)", B=B) is True
assert ImageMath.unsafe_eval("equal(Z, Z)", Z=Z) is True
assert ImageMath.unsafe_eval("equal(A, B)", A=A, B=B) is False
assert ImageMath.unsafe_eval("equal(B, A)", A=A, B=B) is False
assert ImageMath.unsafe_eval("equal(A, Z)", A=A, Z=Z) is False
def test_logical_not_equal() -> None:
assert pixel(ImageMath.unsafe_eval("notequal(A, A)", A=A)) == "I 0"
assert pixel(ImageMath.unsafe_eval("notequal(B, B)", B=B)) == "I 0"
assert pixel(ImageMath.unsafe_eval("notequal(Z, Z)", Z=Z)) == "I 0"
assert pixel(ImageMath.unsafe_eval("notequal(A, B)", A=A, B=B)) == "I 1"
assert pixel(ImageMath.unsafe_eval("notequal(B, A)", A=A, B=B)) == "I 1"
assert pixel(ImageMath.unsafe_eval("notequal(A, Z)", A=A, Z=Z)) == "I 1"
assert ImageMath.unsafe_eval("notequal(A, A)", A=A) is False
assert ImageMath.unsafe_eval("notequal(B, B)", B=B) is False
assert ImageMath.unsafe_eval("notequal(Z, Z)", Z=Z) is False
assert ImageMath.unsafe_eval("notequal(A, B)", A=A, B=B) is True
assert ImageMath.unsafe_eval("notequal(B, A)", A=A, B=B) is True
assert ImageMath.unsafe_eval("notequal(A, Z)", A=A, Z=Z) is True

View File

@ -42,7 +42,7 @@ Example: Using the :py:mod:`~PIL.ImageMath` module
shown in the above example.
:param \**kw: Values to add to the function's dictionary, mapping image names to
Image instances.
:return: An image, an integer value, a floating point value,
:return: An image, an integer value, a floating point value, a boolean,
or a pixel tuple, depending on the expression.
.. py:function:: unsafe_eval(expression, options, **kw)
@ -68,7 +68,7 @@ Example: Using the :py:mod:`~PIL.ImageMath` module
shown in the above example.
:param \**kw: Values to add to the evaluation context, mapping image names to Image
instances.
:return: An image, an integer value, a floating point value,
:return: An image, an integer value, a floating point value, a boolean,
or a pixel tuple, depending on the expression.
Expression syntax

View File

@ -176,23 +176,27 @@ class _Operand:
return self.apply("rshift", self, other)
# logical
def __eq__(self, other):
return self.apply("eq", self, other)
def __eq__(self, other: object) -> bool:
if not isinstance(other, _Operand):
return False
return bool(self.apply("eq", self, other))
def __ne__(self, other):
return self.apply("ne", self, other)
def __ne__(self, other: object) -> bool:
if not isinstance(other, _Operand):
return True
return bool(self.apply("ne", self, other))
def __lt__(self, other: _Operand | float) -> _Operand:
return self.apply("lt", self, other)
def __lt__(self, other: _Operand | float) -> bool:
return bool(self.apply("lt", self, other))
def __le__(self, other: _Operand | float) -> _Operand:
return self.apply("le", self, other)
def __le__(self, other: _Operand | float) -> bool:
return bool(self.apply("le", self, other))
def __gt__(self, other: _Operand | float) -> _Operand:
return self.apply("gt", self, other)
def __gt__(self, other: _Operand | float) -> bool:
return bool(self.apply("gt", self, other))
def __ge__(self, other: _Operand | float) -> _Operand:
return self.apply("ge", self, other)
def __ge__(self, other: _Operand | float) -> bool:
return bool(self.apply("ge", self, other))
# conversions
@ -205,12 +209,12 @@ def imagemath_float(self: _Operand) -> _Operand:
# logical
def imagemath_equal(self: _Operand, other: _Operand | float | None) -> _Operand:
return self.apply("eq", self, other, mode="I")
def imagemath_equal(self: _Operand, other: _Operand | float | None) -> bool:
return bool(self.apply("eq", self, other, mode="I"))
def imagemath_notequal(self: _Operand, other: _Operand | float | None) -> _Operand:
return self.apply("ne", self, other, mode="I")
def imagemath_notequal(self: _Operand, other: _Operand | float | None) -> bool:
return bool(self.apply("ne", self, other, mode="I"))
def imagemath_min(self: _Operand, other: _Operand | float | None) -> _Operand:
@ -253,7 +257,7 @@ def lambda_eval(
You can instead use one or more keyword arguments.
:param **kw: Values to add to the function's dictionary.
:return: The expression result. This is usually an image object, but can
also be an integer, a floating point value, or a pixel tuple,
also be an integer, a floating point value, a boolean, or a pixel tuple,
depending on the expression.
"""
@ -298,7 +302,7 @@ def unsafe_eval(
You can instead use one or more keyword arguments.
:param **kw: Values to add to the evaluation context.
:return: The evaluated expression. This is usually an image object, but can
also be an integer, a floating point value, or a pixel tuple,
also be an integer, a floating point value, a boolean, or a pixel tuple,
depending on the expression.
"""
@ -357,7 +361,7 @@ def eval(
can either use a dictionary, or one or more keyword
arguments.
:return: The evaluated expression. This is usually an image object, but can
also be an integer, a floating point value, or a pixel tuple,
also be an integer, a floating point value, a boolean, or a pixel tuple,
depending on the expression.
.. deprecated:: 10.3.0