Support more affine expression forms in Image.point

In modes I and F, Image.point only supported affine expressions of the
forms (lambda x:) x * a, x + a, and x * a + b. Expressions like 1 - x
had to be written x * -1 + 1.

This rewrite, though still limited to affine transformations, supports
far more expression forms, including 1 - x, (2 * x + 1) / 3, etc.
This commit is contained in:
Ben Rudiak-Gould 2022-04-30 22:58:44 -07:00
parent 9d988dab6a
commit 4e12ccc63e
2 changed files with 40 additions and 33 deletions

View File

@ -18,10 +18,18 @@ def test_sanity():
im.point(lambda x: x * 1)
im.point(lambda x: x + 1)
im.point(lambda x: x * 1 + 1)
im.point(lambda x: 0.1 + 0.2 * x)
im.point(lambda x: -x)
im.point(lambda x: x - 0.5)
im.point(lambda x: 1 - x / 2)
im.point(lambda x: (2 + x) / 3)
im.point(lambda x: 0.5)
with pytest.raises(TypeError):
im.point(lambda x: x - 1)
im.point(lambda x: x * x)
with pytest.raises(TypeError):
im.point(lambda x: x / 1)
im.point(lambda x: 1 / x)
with pytest.raises(TypeError):
im.point(lambda x: x // 2)
def test_16bit_lut():

View File

@ -431,45 +431,44 @@ def _getencoder(mode, encoder_name, args, extra=()):
# Simple expression analyzer
def coerce_e(value):
return value if isinstance(value, _E) else _E(value)
# _Affine(m, b) represents the polynomial m x + b
class _Affine:
def __init__(self, m, b):
self.m = m
self.b = b
class _E:
def __init__(self, data):
self.data = data
def __neg__(self):
return _Affine(-self.m, -self.b)
def __add__(self, other):
return _E((self.data, "__add__", coerce_e(other).data))
if isinstance(other, _Affine):
return _Affine(self.m + other.m, self.b + other.b)
return _Affine(self.m, self.b + other)
__radd__ = __add__
def __sub__(self, other):
return self + -other
def __rsub__(self, other):
return other + -self
def __mul__(self, other):
return _E((self.data, "__mul__", coerce_e(other).data))
if isinstance(other, _Affine):
return NotImplemented
return _Affine(self.m * other, self.b * other)
__rmul__ = __mul__
def __truediv__(self, other):
if isinstance(other, _Affine):
return NotImplemented
return _Affine(self.m / other, self.b / other)
def _getscaleoffset(expr):
stub = ["stub"]
data = expr(_E(stub)).data
try:
(a, b, c) = data # simplified syntax
if a is stub and b == "__mul__" and isinstance(c, numbers.Number):
return c, 0.0
if a is stub and b == "__add__" and isinstance(c, numbers.Number):
return 1.0, c
except TypeError:
pass
try:
((a, b, c), d, e) = data # full syntax
if (
a is stub
and b == "__mul__"
and isinstance(c, numbers.Number)
and d == "__add__"
and isinstance(e, numbers.Number)
):
return c, e
except TypeError:
pass
raise ValueError("illegal expression")
a = expr(_Affine(1.0, 0.0))
return (a.m, a.b) if isinstance(a, _Affine) else (0.0, a)
# --------------------------------------------------------------------