Skip to content

Commit fffb392

Browse files
authored
Merge pull request #140 from djkapner/fix_poly_scale
Fix poly scale
2 parents 3154610 + 0bc91d3 commit fffb392

File tree

4 files changed

+119
-32
lines changed

4 files changed

+119
-32
lines changed

renderapi/transform/leaf/affine_models.py

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from .transform import Transform, logger
2+
from .common import calc_first_order_properties
23
import numpy as np
34
from renderapi.errors import ConversionError, EstimationError
45

@@ -306,32 +307,9 @@ def inverse_tform(self, points):
306307
return self.convert_points_vector_to_array(pt, Nd)
307308

308309
def calc_properties(self):
309-
if self.force_shear == 'x':
310-
sy = np.sqrt(self.M[1, 0] ** 2 + self.M[1, 1] ** 2)
311-
theta = np.arctan2(self.M[1, 0], self.M[1, 1])
312-
rc = np.cos(theta)
313-
rs = np.sin(theta)
314-
sx = rc * self.M[0, 0] - rs * self.M[0, 1]
315-
if rs != 0:
316-
cx = (self.M[0, 0] - sx*rc) / (sx * rs)
317-
else:
318-
cx = (self.M[0, 1] - sx*rs) / (sx * rc)
319-
cy = 0.0
320-
else:
321-
# shear in y direction
322-
sx = np.sqrt(self.M[0, 0] ** 2 + self.M[0, 1] ** 2)
323-
theta = np.arctan2(-self.M[0, 1], self.M[0, 0])
324-
rc = np.cos(theta)
325-
rs = np.sin(theta)
326-
sy = rs * self.M[1, 0] + rc * self.M[1, 1]
327-
if rs != 0:
328-
cy = (self.M[1, 1] - sy * rc) / (-sy * rs)
329-
else:
330-
cy = (self.M[1, 0] - sy * rs) / (sy * rc)
331-
cx = 0.0
332-
# room for other cases, for example cx = cy
333-
334-
return sx, sy, cx, cy, theta
310+
return calc_first_order_properties(
311+
self.M[0:2, 0:2],
312+
force_shear=self.force_shear)
335313

336314
@property
337315
def scale(self):

renderapi/transform/leaf/common.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import numpy as np
2+
3+
4+
def calc_first_order_properties(M, force_shear='x'):
5+
"""calculate scale shear and rotation from a 2x2 matrix
6+
could be M[0:2, 0:2] from an AffineModel or params[:, 1:3]
7+
from a Polynomial2DTransform
8+
9+
Parameters
10+
----------
11+
M : numpy array
12+
2x2
13+
force_shear : str
14+
'x' or 'y'
15+
16+
Returns
17+
-------
18+
sx : float
19+
scale in x direction
20+
sy : float
21+
scale in y direction
22+
cx : float
23+
shear in x direction
24+
cy : float
25+
shear in y direction
26+
theta : float
27+
rotation angle in radians
28+
"""
29+
if force_shear == 'x':
30+
sy = np.sqrt(M[1, 0] ** 2 + M[1, 1] ** 2)
31+
theta = np.arctan2(M[1, 0], M[1, 1])
32+
rc = np.cos(theta)
33+
rs = np.sin(theta)
34+
sx = rc * M[0, 0] - rs * M[0, 1]
35+
if rs != 0:
36+
cx = (M[0, 0] - sx*rc) / (sx * rs)
37+
else:
38+
cx = (M[0, 1] - sx*rs) / (sx * rc)
39+
cy = 0.0
40+
elif force_shear == 'y':
41+
# shear in y direction
42+
sx = np.sqrt(M[0, 0] ** 2 + M[0, 1] ** 2)
43+
theta = np.arctan2(-M[0, 1], M[0, 0])
44+
rc = np.cos(theta)
45+
rs = np.sin(theta)
46+
sy = rs * M[1, 0] + rc * M[1, 1]
47+
if rs != 0:
48+
cy = (M[1, 1] - sy * rc) / (-sy * rs)
49+
else:
50+
cy = (M[1, 0] - sy * rs) / (sy * rc)
51+
cx = 0.0
52+
else:
53+
raise ValueError("%s not a valid option for force_shear."
54+
" should be 'x' or 'y'")
55+
# room for other cases, for example cx = cy
56+
57+
return sx, sy, cx, cy, theta

renderapi/transform/leaf/polynomial_models.py

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from .transform import Transform, logger
22
from .affine_models import AffineModel
33
import numpy as np
4+
from .common import calc_first_order_properties
45
from renderapi.errors import ConversionError, EstimationError, RenderError
56

67
try:
@@ -29,7 +30,8 @@ class Polynomial2DTransform(Transform):
2930

3031
def __init__(self, dataString=None, src=None, dst=None, order=2,
3132
force_polynomial=True, params=None, identity=False,
32-
labels=None, transformId=None, json=None, **kwargs):
33+
labels=None, transformId=None, json=None,
34+
force_shear='x', **kwargs):
3335
"""Initialize Polynomial2DTransform
3436
This provides 5 different ways to initialize the transform which are
3537
mutually exclusive and applied in the order specified here.
@@ -58,6 +60,7 @@ def __init__(self, dataString=None, src=None, dst=None, order=2,
5860
5961
6062
"""
63+
self.force_shear = force_shear
6164
if json is not None:
6265
self.from_dict(json)
6366
else:
@@ -89,15 +92,38 @@ def order(self):
8992
no_coeffs = len(self.params.ravel())
9093
return int((abs(np.sqrt(4 * no_coeffs + 1)) - 3) / 2)
9194

95+
def calc_properties(self):
96+
if self.order == 0:
97+
return 1.0, 1.0, 0.0, 0.0, 0.0
98+
return calc_first_order_properties(
99+
self.params[:, 1:3],
100+
force_shear=self.force_shear)
101+
92102
@property
93103
def scale(self):
94104
"""tuple of scale for x, y"""
95-
if self.order > 0:
96-
scale = (self.params[0, 1], self.params[1, 2])
105+
sx, sy, cx, cy, theta = self.calc_properties()
106+
return (sx, sy)
107+
108+
@property
109+
def shear(self):
110+
"""shear"""
111+
sx, sy, cx, cy, theta = self.calc_properties()
112+
if self.force_shear == 'x':
113+
return cx
97114
else:
98-
# translation only has no scale impact
99-
scale = (1.0, 1.0)
100-
return scale
115+
return cy
116+
117+
@property
118+
def translation(self):
119+
"""tuple of translation in x, y"""
120+
return tuple(self.params[:, 0])
121+
122+
@property
123+
def rotation(self):
124+
"""counter-clockwise rotation"""
125+
sx, sy, cx, cy, theta = self.calc_properties()
126+
return theta
101127

102128
@property
103129
def dataString(self):

test/test_transform.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,3 +1011,29 @@ def test_adaptive_estimate():
10111011
dsta = tf.tform(src)
10121012
dstb = ntf.tform(src)
10131013
assert(np.linalg.norm(dsta - dstb, axis=1).max() <= tol)
1014+
1015+
1016+
def test_polynomial_shear():
1017+
# make sure it gives the same answer as affine
1018+
M = np.array([
1019+
[1.1, -0.2, 7.0],
1020+
[0.3, 0.8, -9.0],
1021+
[0.0, 0.0, 1.0]])
1022+
1023+
atf = renderapi.transform.AffineModel()
1024+
atf.M = M
1025+
1026+
params = np.zeros((2, 3))
1027+
params[:, 1:] = M[0:2, 0:2]
1028+
params[:, 0] = M[0:2, 2]
1029+
1030+
ptf = renderapi.transform.Polynomial2DTransform(params=params)
1031+
1032+
for shear in ['x', 'y']:
1033+
atf.force_shear = shear
1034+
ptf.force_shear = shear
1035+
1036+
assert atf.scale == ptf.scale
1037+
assert atf.shear == ptf.shear
1038+
assert atf.rotation == ptf.rotation
1039+
assert atf.translation == ptf.translation

0 commit comments

Comments
 (0)