def make_Zernike_grads(mask, roi = None, max_order = 100, return_grids = False, return_basis = False, yx_bounds = None, test = False): if return_grids : basis, basis_grid, y, x = make_Zernike_basis(mask, roi, max_order, return_grids, yx_bounds, test) else : basis = make_Zernike_basis(mask, roi, max_order, return_grids, yx_bounds, test) # calculate the x and y gradients from numpy.polynomial import polynomial as P # just a list of [(grad_ss, grad_fs), ...] where the grads are in a polynomial basis grads = [ (P.polyder(b, axis=0), P.polyder(b, axis=1)) for b in basis ] if return_grids : # just a list of [(grad_ss, grad_fs), ...] where the grads are evaluated on a y, x grid grad_grids = [(P.polygrid2d(y, x, g[0]), P.polygrid2d(y, x, g[1])) for g in grads] if return_basis : return grads, grad_grids, basis, basis_grid else : return grads, grad_grids else : if return_basis : return grads, basis else : return grads
def test_polygrid2d(self): x1, x2, x3 = self.x y1, y2, y3 = self.y #test values tgt = np.einsum('i,j->ij', y1, y2) res = poly.polygrid2d(x1, x2, self.c2d) assert_almost_equal(res, tgt) #test shape z = np.ones((2,3)) res = poly.polygrid2d(z, z, self.c2d) assert_(res.shape == (2, 3)*2)
def test_polygrid2d(self): x1, x2, x3 = self.x y1, y2, y3 = self.y # test values tgt = np.einsum('i,j->ij', y1, y2) res = poly.polygrid2d(x1, x2, self.c2d) assert_almost_equal(res, tgt) # test shape z = np.ones((2, 3)) res = poly.polygrid2d(z, z, self.c2d) assert_(res.shape == (2, 3) * 2)
def polygrid2d(x, y, c): """ Evaluate a 2-D polynomial on the Cartesian product of x and y. This function returns the values: .. math:: p(a,b) = \\sum_{i,j} c_{i,j} * a^i * b^j where the points `(a, b)` consist of all pairs formed by taking `a` from `x` and `b` from `y`. The resulting points form a grid with `x` in the first dimension and `y` in the second. The parameters `x` and `y` are converted to arrays only if they are tuples or a lists, otherwise they are treated as a scalars. In either case, either `x` and `y` or their elements must support multiplication and addition both with themselves and with the elements of `c`. If `c` has fewer than two dimensions, ones are implicitly appended to its shape to make it 2-D. The shape of the result will be c.shape[2:] + x.shape + y.shape. Parameters ---------- x, y : array_like, compatible objects The two dimensional series is evaluated at the points in the Cartesian product of `x` and `y`. If `x` or `y` is a list or tuple, it is first converted to an ndarray, otherwise it is left unchanged and, if it isn't an ndarray, it is treated as a scalar. c : array_like Array of coefficients ordered so that the coefficients for terms of degree i,j are contained in `c[i,j]`. If `c` has dimension greater than two the remaining indices enumerate multiple sets of coefficients. Returns ------- values : ndarray, compatible object The values of the two dimensional polynomial at points in the Cartesian product of `x` and `y`. See Also -------- polyval2d, polyfit2d numpy.polynomial.polynomial.polygrid2d """ from numpy.polynomial.polynomial import polygrid2d c = np.asarray(c) if c.ndim != 2 or c.shape[0] != c.shape[1]: raise ValueError("c must be a squard 2-dim array.") return polygrid2d(x, y, c)
def polygrid2d(x, y, c): """ Evaluate a 2-D polynomial on the Cartesian product of x and y. This function returns the values: .. math:: p(a,b) = \sum_{i,j} c_{i,j} * a^i * b^j where the points `(a, b)` consist of all pairs formed by taking `a` from `x` and `b` from `y`. The resulting points form a grid with `x` in the first dimension and `y` in the second. The parameters `x` and `y` are converted to arrays only if they are tuples or a lists, otherwise they are treated as a scalars. In either case, either `x` and `y` or their elements must support multiplication and addition both with themselves and with the elements of `c`. If `c` has fewer than two dimensions, ones are implicitly appended to its shape to make it 2-D. The shape of the result will be c.shape[2:] + x.shape + y.shape. Parameters ---------- x, y : array_like, compatible objects The two dimensional series is evaluated at the points in the Cartesian product of `x` and `y`. If `x` or `y` is a list or tuple, it is first converted to an ndarray, otherwise it is left unchanged and, if it isn't an ndarray, it is treated as a scalar. c : array_like Array of coefficients ordered so that the coefficients for terms of degree i,j are contained in ``c[i,j]``. If `c` has dimension greater than two the remaining indices enumerate multiple sets of coefficients. Returns ------- values : ndarray, compatible object The values of the two dimensional polynomial at points in the Cartesian product of `x` and `y`. See Also -------- polyval2d, polyfit2d numpy.polynomial.polynomial.polygrid2d """ from numpy.polynomial.polynomial import polygrid2d c = np.asarray(c) if c.ndim != 2 or c.shape[0] != c.shape[1]: raise ValueError("c must be a squard 2-dim array.") return polygrid2d(x, y, c)
def apply_skew_poly(input_data, delta_kcoa_poly, row_array, col_array, fft_sgn, dimension, forward=False): """ Performs the skew operation on the complex array, according to the provided delta kcoa polynomial. Parameters ---------- input_data : numpy.ndarray The input data. delta_kcoa_poly : numpy.ndarray The delta kcoa polynomial to use. row_array : numpy.ndarray The row array, should agree with input_data first dimension definition. col_array : numpy.ndarray The column array, should agree with input_data second dimension definition. fft_sgn : int The fft sign to use. dimension : int The dimension to apply along. forward : bool If True, this shifts forward (i.e. skews), otherwise applies in inverse (i.e. deskew) direction. Returns ------- numpy.ndarray """ if numpy.all(delta_kcoa_poly == 0): return input_data delta_kcoa_poly_int = polynomial.polyint(delta_kcoa_poly, axis=dimension) if forward: fft_sgn *= -1 return input_data * numpy.exp( 1j * fft_sgn * 2 * numpy.pi * polynomial.polygrid2d(row_array, col_array, delta_kcoa_poly_int))
def _deskew_array(input_data, delta_kcoa_poly, row_array, col_array, fft_sgn, dimension): """ Performs deskew (centering of the spectrum on zero frequency) on a complex array. Parameters ---------- input_data : numpy.ndarray delta_kcoa_poly : numpy.ndarray row_array : numpy.ndarray col_array : numpy.ndarray fft_sgn : int Returns ------- numpy.ndarray """ delta_kcoa_poly_int = polynomial.polyint(delta_kcoa_poly, axis=dimension) return input_data*numpy.exp(1j*fft_sgn*2*numpy.pi*polynomial.polygrid2d( row_array, col_array, delta_kcoa_poly_int))
def __init__(self, cal_img: np.ndarray): self.width = int(np.size(cal_img, 1) / 2) self.height = np.size(cal_img, 0) # Normalise and convert to 8-bit im = np.zeros_like(cal_img, dtype="uint8") cv2.normalize(cal_img, im, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U) # find all spots in left and right images posl = self._find_spots(im[:, 0:self.width]) nl = len(posl) posr = self._find_spots(im[:, self.width:]) nr = len(posr) # Reduce parameter-space for determining the affine transform by # finding 6-way co-ordinated spots that lie at the centre of Penrose # stars. Find star radius, central spot positions, and centroid offsets # for left and right images. (dmidl, pos6l, diff6l) = self._find_penrose(posl) (dmidr, pos6r, diff6r) = self._find_penrose(posr) # find matched 6-way co-ordinated spot pairs pmatch6l = [] pmatch6r = [] for i in range( len(pos6l)): # loop over all 6-way co-ordinated left spots dist6 = np.hypot( pos6r[:, 1] - pos6l[i, 1], self.width - 1 - pos6r[:, 0] - pos6l[i, 0], ) # the distance of all right spots from this left spot distoffs = np.hypot(diff6l[i, 1] - diff6r[:, 1], diff6l[i, 0] + diff6r[:, 0]) # the distance between the right and left 6-spot centroids for j in range(len( pos6r)): # loop over all 6-way co-ordinated spots on right if (dist6[j] < 4 * dmidl) & (distoffs[j] < dmidl / 4): # select spots as pairs if they are close enough and if the # centroids are close enough pmatch6l = np.append(pmatch6l, pos6l[i, :]) # add to list of left and right matched spots pmatch6r = np.append(pmatch6r, pos6r[j, :]) nmatch6 = int(len(pmatch6l) / 2) pmatch6l = np.reshape(pmatch6l, (nmatch6, 2)) pmatch6r = np.reshape(pmatch6r, (nmatch6, 2)) # find affine transform based on 6-way matches [retval, p_affine] = cv2.solve(np.c_[pmatch6r, np.ones(nmatch6)], pmatch6l, flags=cv2.DECOMP_SVD) # transform all right spot positions to find matches on left posrt = np.matmul(np.c_[posr, np.ones(nr)], p_affine) pmatchl = [] pmatchr = [] for i in range(nl): dist = np.hypot(*(posl[i] - posrt).T) mind = min(dist) if mind < 5: pmatchl = np.append(pmatchl, posl[i, :]) pmatchr = np.append(pmatchr, posr[np.argmin(dist), :]) nmatch = int(len(pmatchl) / 2) pmatchl = np.reshape(pmatchl, (nmatch, 2)) pmatchr = np.reshape(pmatchr, (nmatch, 2)) # Determine polynomial transform for all matched pairs # Sum terms: 1, x, y, x^2, y^2, x*y, x^2*y, x*y^2, x^3, y^3 # Use polygrid2d: ~5x quicker, may avoid fp error accumulation, # gives same result to within < 1ppm. from numpy.polynomial.polynomial import polygrid2d p1 = np.ones(nmatch) px = pmatchl[:, 0] py = pmatchl[:, 1] rmatp = np.c_[p1, py, py * py, py * py * py, px, px * py, px * py * py, px * px, px * px * py, px * px * px, ] [retval, pcoeffs_raw] = cv2.solve(rmatp, pmatchr, flags=cv2.DECOMP_SVD) # Put raw coeffs into matrix such that element [i,j] holds Cij in # poly = sum (Cij * x^i * y^j). pcoeffs = np.zeros((4, 4, 2)) pcoeffs[(0, 0, 0, 0, 1, 1, 1, 2, 2, 3), (0, 1, 2, 3, 0, 1, 2, 0, 1, 0), :] = pcoeffs_raw xs = np.arange(self.width) ys = np.arange(self.height) deformation = polygrid2d(xs, ys, pcoeffs).T.astype(np.float32) # Convential memory deformation map self.defXcpu = deformation[..., 0].copy() self.defYcpu = deformation[..., 1].copy() # GPU memory deformation map self.defX = cv2.UMat(self.defXcpu) self.defY = cv2.UMat(self.defYcpu)
def product(a, b): c = P.polygrid2d(y[roi[0]:roi[1]+1], x[roi[2]:roi[3]+1], a) d = P.polygrid2d(y[roi[0]:roi[1]+1], x[roi[2]:roi[3]+1], b) v = np.sum(sub_mask * c * d) return v
def make_Zernike_basis(mask, roi = None, max_order = 100, return_grids = False, yx_bounds = None, test = False): """ Make Zernike basis functions, such that: np.sum( Z_i * Z_j * mask) = delta_ij Returns ------- basis_poly : list of arrays The basis functions in a polynomial basis. basis_grid : list of arrays The basis functions evaluated on the cartesian grid """ shape = mask.shape # list the Zernike indices in the Noll indexing order: # ---------------------------------------------------- Noll_indices = make_Noll_index_sequence(max_order) # set the x-y values and scale to the roi # --------------------------------------- if roi is None : roi = [0, shape[0]-1, 0, shape[1]-1] sub_mask = mask[roi[0]:roi[1]+1, roi[2]:roi[3]+1] sub_shape = sub_mask.shape if yx_bounds is None : if (roi[1] - roi[0]) > (roi[3] - roi[2]) : m = float(roi[1] - roi[0]) / float(roi[3] - roi[2]) yx_bounds = [-m, m, -1., 1.] else : m = float(roi[3] - roi[2]) / float(roi[1] - roi[0]) yx_bounds = [-1., 1., -m, m] dom = yx_bounds y = ((dom[1]-dom[0])*np.arange(shape[0]) + dom[0]*roi[1]-dom[1]*roi[0])/(roi[1]-roi[0]) x = ((dom[3]-dom[2])*np.arange(shape[1]) + dom[2]*roi[3]-dom[3]*roi[2])/(roi[3]-roi[2]) # define the area element # ----------------------- dA = (x[1] - x[0]) * (y[1] - y[0]) # generate the Zernike polynomials in a cartesian basis: # ------------------------------------------------------ Z_polynomials = [] for j in range(1, max_order+1): n, m, name = Noll_indices[j] mat, A = make_Zernike_polynomial_cartesian(n, m, order = max_order) Z_polynomials.append(mat * A * dA) # define the product method # ------------------------- from numpy.polynomial import polynomial as P def product(a, b): c = P.polygrid2d(y[roi[0]:roi[1]+1], x[roi[2]:roi[3]+1], a) d = P.polygrid2d(y[roi[0]:roi[1]+1], x[roi[2]:roi[3]+1], b) v = np.sum(sub_mask * c * d) return v basis = Gram_Schmit_orthonormalisation(Z_polynomials, product) # test the basis function if test : print '\n\nbasis_i, basis_j, product(basis_i, basis_j)' for i in range(len(basis)) : for j in range(len(basis)) : print i, j, product(basis[i], basis[j]) if return_grids : basis_grid = [P.polygrid2d(y, x, b) for b in basis] if test : print '\n\nbasis_i, basis_j, np.sum(mask * basis_i * basis_j)' for i in range(len(basis_grid)) : for j in range(len(basis_grid)) : print i, j, np.sum(mask * basis_grid[i] * basis_grid[j]) return basis, basis_grid, y, x else : return basis