Example #1
0
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
Example #2
0
    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
Example #3
0
    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
Example #4
0
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
    }
Example #5
0
    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
Example #6
0
 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'))))
Example #7
0
    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
Example #8
0
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