def cie_1976_wavelength_annotations(wavelengths, fig=None, ax=None): ''' Draws lines normal to the spectral locust on a CIE 1976 diagram and writes the text for each wavelength. Args: wavelengths (`iterable`): set of wavelengths to annotate. fig (`matplotlib.figure.Figure`): figure to draw on. ax (`matplotlib.axes.Axis`): axis to draw in. Returns: `tuple` containing: `matplotlib.figure.Figure`: figure containing the annotations. `matplotlib.axes.Axis`: axis containing the annotations. Notes: see SE: https://stackoverflow.com/questions/26768934/annotation-along-a-curve-in-matplotlib ''' # some tick parameters tick_length = 0.025 text_offset = 0.06 # convert wavelength to u' v' coordinates wavelengths = np.asarray(wavelengths) idx = np.arange(1, len(wavelengths) - 1, dtype=int) wvl_lbl = wavelengths[idx] uv = XYZ_to_uvprime(wavelength_to_XYZ(wavelengths)) u, v = uv[..., 0][idx], uv[..., 1][idx] u_last, v_last = uv[..., 0][idx - 1], uv[..., 1][idx - 1] u_next, v_next = uv[..., 0][idx + 1], uv[..., 1][idx + 1] angle = atan2(v_next - v_last, u_next - u_last) + pi / 2 cos_ang, sin_ang = cos(angle), sin(angle) u1, v1 = u + tick_length * cos_ang, v + tick_length * sin_ang u2, v2 = u + text_offset * cos_ang, v + text_offset * sin_ang fig, ax = share_fig_ax(fig, ax) tick_lines = LineCollection(np.c_[u, v, u1, v1].reshape(-1, 2, 2), color='0.25', lw=1.25) ax.add_collection(tick_lines) for i in range(len(idx)): ax.text(u2[i], v2[i], str(wvl_lbl[i]), va="center", ha="center", clip_on=True) return fig, ax
def image_dist_epd_to_na(image_distance, epd): '''Computes the NA from an image distance and entrance pupil diameter Args: image_distance (float): distance from the image to the entrance pupil. epd (float): diameter of the entrance pupil. Returns: numerical aperture. The NA of the system. ''' image_distance = guarantee_array(image_distance) rho = epd / 2 marginal_ray_angle = abs(atan2(rho, image_distance)) return marginal_ray_angle
def _get_cost_rmswfe_rrmswfe_coma_or_ast_angle(db, doc_ids, other='coma'): cost, rmswfe, rrmswfe, angle = [], [], [], [] xidx, yidx = _get_idxs(other) for did in doc_ids: doc = db.get_document(did) tp = doc['truth_params'] cost.append(doc['cost_final']) rmswfe.append(doc['truth_rmswfe']) rrmswfe.append((doc['rrmswfe_final'])) angle.append(np.degrees(atan2(tp[xidx], tp[yidx]))) cost = np.asarray(cost) rmswfe = np.asarray(rmswfe) rrmswfe = np.asarray(rrmswfe) angle = np.asarray(angle) return cost, rmswfe, rrmswfe, angle
def Luv_to_chroma_hue(luv): ''' Converts L*u*v* coordiantes to a chroma and hue. Args: luv (`numpy.ndarray`): array with last dimension L*, u*, v*. Returns: `numpy.ndarray` with last dimension corresponding to C* and h. ''' luv = np.asarray(luv) u, v = luv[..., 1], luv[..., 2] C = sqrt(u**2 + v**2) h = atan2(v, u) shape = luv.shape return np.stack((C, h), axis=len(shape))
def cart_to_polar(x, y): ''' Returns the (rho,phi) coordinates of the (x,y) input points. Args: x (float): x coordinate. y (float): y coordinate. Returns: `tuple` containing: `float` or `numpy.ndarray`: radial coordinate. `float` or `numpy.ndarray`: azimuthal coordinate. ''' rho = sqrt(x**2 + y**2) phi = atan2(y, x) return rho, phi
def fit(data, num_terms=16, rms_norm=False, round_at=6): ''' Fits a number of zernike coefficients to provided data by minimizing the root sum square between each coefficient and the given data. The data should be uniformly sampled in an x,y grid. Args: data (`numpy.ndarray`): data to fit to. num_terms (`int`): number of terms to fit, fits terms 0~num_terms. rms_norm (`bool`): if true, normalize coefficients to unit RMS value. round_at (`int`): decimal place to round values at. Returns: numpy.ndarray: an array of coefficients matching the input data. ''' if num_terms > len(zernfcns): raise ValueError(f'number of terms must be less than {len(zernfcns)}') # precompute the valid indexes in the original data pts = np.isfinite(data) # set up an x/y rho/phi grid to evaluate zernikes on x, y = np.linspace(-1, 1, data.shape[1]), np.linspace(-1, 1, data.shape[0]) xv, yv = np.meshgrid(x, y) rho = sqrt(xv**2 + yv**2)[pts].flatten() phi = atan2(xv, yv)[pts].flatten() # compute each zernike term zernikes = [] for i in range(num_terms): zernikes.append(zernwrapper(i, rms_norm, rho, phi)) zerns = np.asarray(zernikes).T # use least squares to compute the coefficients coefs = np.linalg.lstsq(zerns, data[pts].flatten())[0] return coefs.round(round_at)
def test_cart_to_polar(x, y): rho, phi = coordinates.cart_to_polar(x, y) assert np.allclose(rho, sqrt(x**2 + y**2)) assert np.allclose(phi, atan2(y, x))