|
1 | 1 | import numpy as np |
| 2 | +from scipy import ndimage |
2 | 3 | import warnings |
3 | 4 |
|
4 | 5 |
|
@@ -119,6 +120,65 @@ def rcosFn(width=1, position=0, values=(0, 1)): |
119 | 120 | return (X, Y) |
120 | 121 |
|
121 | 122 |
|
| 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 | + |
122 | 182 | if __name__ == "__main__": |
123 | 183 | X, Y = rcosFn(width=1, position=0, values=(0, 1)) |
124 | 184 |
|
|
0 commit comments