Skip to content

Commit dc7938b

Browse files
committed
adds project_polar_to_cartesian function, with tests
this function takes an image that was created with polar coordinates and remaps it to cartesian coordinates. it's currently fairly limited, only working on square images, but that's what we need for now
1 parent b19f254 commit dc7938b

File tree

3 files changed

+74
-1
lines changed

3 files changed

+74
-1
lines changed

TESTS/unitTests.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,6 +1160,19 @@ def test2(self):
11601160
res = pt.skew(disc, mn, v)
11611161
self.assertTrue(np.absolute(res - mres) <= np.power(10.0,-11))
11621162

1163+
1164+
class ProjectPolarTests(unittest.TestCase):
1165+
def test0(self):
1166+
# check that it runs, even though here the output will be nonsensical
1167+
img = pt.synthetic_images.disk(256)
1168+
proj_img = pt.project_polar_to_cartesian(img)
1169+
def test1(self):
1170+
# currently only works for square images
1171+
img = pt.synthetic_images.disk((256, 512))
1172+
with self.assertRaises(Exception):
1173+
pt.project_polar_to_cartesian(img)
1174+
1175+
11631176
# class cconv2Tests(unittest.TestCase):
11641177
# def test0(self):
11651178
# matPyr = scipy.io.loadmat(op.join(matfiles_path, 'cconv2_0.mat'))

pyrtools/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from .tools.convolutions import blurDn, blur, upBlur, image_gradient, rconv2
88
from .tools.display import imshow, animshow, pyrshow
99
from .tools.image_stats import image_compare, image_stats, range, skew, var, entropy
10-
from .tools.utils import rcosFn, matlab_histo, matlab_round
10+
from .tools.utils import rcosFn, matlab_histo, matlab_round, project_polar_to_cartesian
1111
from .tools.compare_matpyrtools import comparePyr, compareRecon
1212

1313
from .version import version as __version__

pyrtools/tools/utils.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import numpy as np
2+
from scipy import ndimage
23
import warnings
34

45

@@ -119,6 +120,65 @@ def rcosFn(width=1, position=0, values=(0, 1)):
119120
return (X, Y)
120121

121122

123+
def project_polar_to_cartesian(data):
124+
"""Take a function defined in polar coordinates and project it into Cartesian coordinates
125+
126+
Inspired by https://pyabel.readthedocs.io/en/latest/_modules/abel/tools/polar.html, which went
127+
the other way. Note that we currently don't implement the Cartesian to polar projection, but
128+
could do so based on this code fairly simply if it's necessary.
129+
130+
Currently, this only works for square images and we require that the original image and the
131+
reprojected image are the same size. There should be a way to avoid both of these issues, but I
132+
can't think of a way to do that right now.
133+
134+
Parameters
135+
----------
136+
data : array_like
137+
The 2d array to convert from polar to Cartesian coordinates. We assume the first dimension
138+
is the polar radius and the second is the polar angle.
139+
140+
Returns
141+
-------
142+
output : np.array
143+
The 2d array in Cartesian coordinates.
144+
145+
"""
146+
if np.isnan(data).any():
147+
data[np.isnan(data)] = 0
148+
warnings.warn("project_polar_to_cartesian won't work if there are any NaNs in the array, "
149+
"so we've replaced all NaNs with 0s")
150+
nx = data.shape[1]
151+
ny = data.shape[0]
152+
if nx != ny:
153+
raise Exception("There's an occasional bug where we don't wrap the angle correctly if nx "
154+
"and ny aren't equal, so we don't support this for now!")
155+
156+
max_radius = data.shape[0]
157+
x_i = np.linspace(-max_radius, max_radius, nx, endpoint=False)
158+
y_i = np.linspace(-max_radius, max_radius, ny, endpoint=False)
159+
x_grid, y_grid = np.meshgrid(x_i, y_i)
160+
# need to flip the y indices so that negative is at the bottom (to correspond with how we have
161+
# the polar angle -- 0 on the right)
162+
y_grid = np.flipud(y_grid)
163+
164+
r = np.sqrt(x_grid**2 + y_grid**2)
165+
166+
theta = np.arctan2(y_grid, x_grid)
167+
# having the angle run from 0 to 2 pi seems to avoid most of the discontinuities
168+
theta = np.mod(theta, 2*np.pi)
169+
# need to convert from 2pi to pixel values
170+
theta *= nx/(2*np.pi)
171+
172+
r_i, theta_i = r.flatten(), theta.flatten()
173+
# map_coordinates requires a 2xn array
174+
coords = np.vstack((r_i, theta_i))
175+
# we use mode="nearest" to deal with weird discontinuities that may pop up near the theta=0
176+
# line
177+
zi = ndimage.map_coordinates(data, coords, mode='nearest')
178+
output = zi.reshape((ny, nx))
179+
return output
180+
181+
122182
if __name__ == "__main__":
123183
X, Y = rcosFn(width=1, position=0, values=(0, 1))
124184

0 commit comments

Comments
 (0)