def im_downsample(im, L_ds): """ Blur and downsample image :param im: Set of images to be downsampled in the form of an array L-by-L-by-K, where K is the number of images. :param L_ds: The desired resolution of the downsampled images. Must be smaller than L. :return: An array of the form L_ds-by-L_ds-by-K consisting of the blurred and downsampled images. """ N = im.shape[0] grid = grid_2d(N) grid_ds = grid_2d(L_ds) im_ds = np.zeros((L_ds, L_ds, im.shape[2])).astype(im.dtype) # x, y values corresponding to 'grid'. This is what scipy interpolator needs to function. x = y = np.ceil(np.arange(-N / 2, N / 2)) / (N / 2) mask = (np.abs(grid['x']) < L_ds / N) & (np.abs(grid['y']) < L_ds / N) im = np.real(centered_ifft2(centered_fft2(im) * np.expand_dims(mask, 2))) for s in range(im_ds.shape[-1]): interpolator = RegularGridInterpolator((x, y), im[:, :, s], bounds_error=False, fill_value=0) im_ds[:, :, s] = interpolator(np.dstack([grid_ds['x'], grid_ds['y']])) return im_ds
def estimate_noise_psd(self): """ :return: The estimated noise variance of the images in the Source used to create this estimator. TODO: How's this initial estimate of variance different from the 'estimate' method? """ # Run estimate using saved parameters g2d = grid_2d(self.L) mask = g2d['r'] >= self.bgRadius mean_est = 0 noise_psd_est = np.zeros((self.L, self.L)).astype(self.src.dtype) for i in range(0, self.n, self.batchSize): images = self.src.images(i, self.batchSize) images_masked = (images * np.expand_dims(mask, 2)) _denominator = self.n * np.sum(mask) mean_est += np.sum(images_masked) / _denominator im_masked_f = centered_fft2(images_masked) noise_psd_est += np.sum(np.abs(im_masked_f**2), axis=2) / _denominator mid = self.L // 2 noise_psd_est[mid, mid] -= mean_est**2 return noise_psd_est
def evaluate_grid(self, L, *args, **kwargs): grid2d = grid_2d(L) omega = np.pi * np.vstack((grid2d['x'].flatten('F'), grid2d['y'].flatten('F'))) h = self.evaluate(omega, *args, **kwargs) h = m_reshape(h, grid2d['x'].shape) return h
def unique_coords_nd(N, ndim): """ Generate unique polar coordinates from 2D or 3D rectangular coordinates. :param N: length size of a square or cube. :param ndim: number of dimension, 2 or 3. :return: The unique polar coordinates in 2D or 3D """ ensure(ndim in (2, 3), 'Only two- or three-dimensional basis functions are supported.') ensure(N > 0, 'Number of grid points should be greater than 0.') if ndim == 2: grid = grid_2d(N) mask = grid['r'] <= 1 # Minor differences in r/theta/phi values are unimportant for the purpose # of this function, so round off before proceeding # TODO: numpy boolean indexing will return a 1d array (like MATLAB) # However, it always searches in row-major order, unlike MATLAB (column-major), # with no options to change the search order. The results we'll be getting back are thus not comparable. # We transpose the appropriate ndarrays before applying the mask to obtain the same behavior as MATLAB. r = grid['r'].T[mask].round(5) phi = grid['phi'].T[mask].round(5) r_unique, r_idx = np.unique(r, return_inverse=True) ang_unique, ang_idx = np.unique(phi, return_inverse=True) else: grid = grid_3d(N) mask = grid['r'] <= 1 # In Numpy, elements in the indexed array are always iterated and returned in row-major (C-style) order. # To emulate a behavior where iteration happens in Fortran order, we swap axes 0 and 2 of both the array # being indexed (r/theta/phi), as well as the mask itself. # TODO: This is only for the purpose of getting the same behavior as MATLAB while porting the code, and is # likely not needed in the final version. # Minor differences in r/theta/phi values are unimportant for the purpose of this function, # so we round off before proceeding. mask_ = np.swapaxes(mask, 0, 2) r = np.swapaxes(grid['r'], 0, 2)[mask_].round(5) theta = np.swapaxes(grid['theta'], 0, 2)[mask_].round(5) phi = np.swapaxes(grid['phi'], 0, 2)[mask_].round(5) r_unique, r_idx = np.unique(r, return_inverse=True) ang_unique, ang_idx = np.unique(np.vstack([theta, phi]), axis=1, return_inverse=True) return { 'r_unique': r_unique, 'ang_unique': ang_unique, 'r_idx': r_idx, 'ang_idx': ang_idx, 'mask': mask }
def evaluate_grid(self, L, *args, **kwargs): # Todo: remove redundancy wrt a single Filter's evaluate_grid grid2d = grid_2d(L) omega = np.pi * np.vstack( (grid2d['x'].flatten('F'), grid2d['y'].flatten('F'))) h = self.evaluate(omega, *args, **kwargs) h = m_reshape(h, grid2d['x'].shape + (len(self.filters), )) return h
def testGrid2d(self): grid2d = grid_2d(8) self.assertTrue( np.allclose(grid2d['x'], np.load(os.path.join(DATA_DIR, 'grid2d_8_x.npy')))) self.assertTrue( np.allclose(grid2d['y'], np.load(os.path.join(DATA_DIR, 'grid2d_8_y.npy')))) self.assertTrue( np.allclose(grid2d['r'], np.load(os.path.join(DATA_DIR, 'grid2d_8_r.npy')))) self.assertTrue( np.allclose(grid2d['phi'], np.load(os.path.join(DATA_DIR, 'grid2d_8_phi.npy'))))
def _estimate_noise_variance(self): """ Any additional arguments/keyword-arguments are passed on to the Source's 'images' method :return: The estimated noise variance of the images in the Source used to create this estimator. TODO: How's this initial estimate of variance different from the 'estimate' method? """ # Run estimate using saved parameters g2d = grid_2d(self.L) mask = g2d['r'] >= self.bgRadius first_moment = 0 second_moment = 0 for i in range(0, self.n, self.batchSize): images = self.src.images(start=i, num=self.batchSize) images_masked = (images * np.expand_dims(mask, 2)) _denominator = self.n * np.sum(mask) first_moment += np.sum(images_masked) / _denominator second_moment += np.sum(np.abs(images_masked**2)) / _denominator return second_moment - first_moment**2
def rotated_grids(L, rot_matrices): """ Generate rotated Fourier grids in 3D from rotation matrices :param L: The resolution of the desired grids. :param rot_matrices: An array of size 3-by-3-by-K containing K rotation matrices :return: A set of rotated Fourier grids in three dimensions as specified by the rotation matrices. Frequencies are in the range [-pi, pi]. """ # TODO: Flattening and reshaping at end may not be necessary! grid2d = grid_2d(L) num_pts = L**2 num_rots = rot_matrices.shape[-1] pts = np.pi * np.vstack([ grid2d['x'].flatten('F'), grid2d['y'].flatten('F'), np.zeros(num_pts) ]) pts_rot = np.zeros((3, num_pts, num_rots)) for i in range(num_rots): pts_rot[:, :, i] = rot_matrices[:, :, i] @ pts pts_rot = m_reshape(pts_rot, (3, L, L, num_rots)) return pts_rot