def setUp(self): self.dtype = np.float32 # Test Volume v = Volume( np.load(os.path.join(DATA_DIR, "clean70SRibosome_vol.npy")).astype( self.dtype)).downsample(32) # Create Sim object. # Creates 10 projects so there is something to feed FSPCABasis. self.src = Simulation(L=v.resolution, n=10, vols=v, dtype=v.dtype) # Original projection image to transform. self.orig_img = self.src.images(0, 1) # Rotate 90 degrees in cartesian coordinates using third party tool. self.rt90_img = Image(np.rot90(self.orig_img.asnumpy(), axes=(1, 2))) # Prepare a Fourier Bessel Basis self.basis = FFBBasis2D((self.orig_img.res, ) * 2, dtype=self.dtype) self.v1 = self.basis.evaluate_t(self.orig_img) self.v2 = self.basis.evaluate_t(self.rt90_img) # These should _not_ be equal or the test is pointless. self.assertFalse(np.allclose(self.v1, self.v2)) # Prepare a FSPCA Basis too. self.fspca_basis = FSPCABasis(self.src, self.basis)
def src_backward(self, mean_vol, noise_variance, shrink_method=None): """ Apply adjoint mapping to source :return: The sum of the outer products of the mean-subtracted images in `src`, corrected by the expected noise contribution and expressed as coefficients of `basis`. """ covar_b = np.zeros((self.L, self.L, self.L, self.L, self.L, self.L), dtype=self.as_type) for i in range(0, self.n, self.batch_size): im = self.src.images(i, self.batch_size) batch_n = im.shape[-1] im_centered = im - self.src.vol_forward(mean_vol, i, self.batch_size) im_centered_b = np.zeros((self.L, self.L, self.L, batch_n), dtype=self.as_type) for j in range(batch_n): im_centered_b[:, :, :, j] = self.src.im_backward( Image(im_centered[:, :, j]), i + j) im_centered_b = vol_to_vec(im_centered_b) covar_b += vecmat_to_volmat( im_centered_b @ im_centered_b.T) / self.n covar_b_coeff = self.basis.mat_evaluate_t(covar_b) return self._shrink(covar_b_coeff, noise_variance, shrink_method)
def _images(self, start=0, num=np.inf, indices=None, batch_size=512): """ Internal function to return a set of images after denoising :param start: The inclusive start index from which to return images. :param num: The exclusive end index up to which to return images. :param num: The indices of images to return. :return: an `Image` object after denoisng. """ if indices is None: indices = np.arange(start, min(start + num, self.n)) else: start = indices.min() end = indices.max() nimgs = len(indices) im = np.empty((nimgs, self.L, self.L)) logger.info(f"Loading {nimgs} images complete") for istart in range(start, end + 1, batch_size): imgs_denoised = self.denoiser.images(istart, batch_size) iend = min(istart + batch_size, end + 1) im[istart:iend] = imgs_denoised.data return Image(im)
def eval_filters(self, im_orig, start=0, num=np.inf, indices=None): if not isinstance(im_orig, Image): logger.warning( f"eval_filters passed {type(im_orig)} instead of Image instance" ) # for now just convert it im = Image(im_orig) im = im_orig.copy() if indices is None: indices = np.arange(start, min(start + num, self.n)) for i, filt in enumerate(self.unique_filters): idx_k = np.where(self.filter_indices[indices] == i)[0] if len(idx_k) > 0: im[idx_k] = Image(im[idx_k]).filter(filt).asnumpy() return im
def output( self, classes, classes_refl, rot, shifts=None, coefs=None, ): """ Return class averages. :param classes: class indices (refering to src). (n_img, n_nbor) :param classes_refl: Bool representing whether to reflect image in `classes` :param rot: Array of in-plane rotation angles (Radians) of image in `classes` :param shifts: Optional array of shifts for image in `classes`. :coefs: Optional Fourier bessel coefs (avoids recomputing). :return: Stack of Synthetic Class Average images as Image instance. """ logger.info(f"Select {self.n_classes} Classes from Nearest Neighbors") # generate indices for random sample (can do something smart with corr later). # For testing just take the first n_classes so it matches earlier plots for manual comparison # This is assumed to be reasonably random. selection = np.arange(self.n_classes) imgs = self.src.images(0, self.src.n) fb_avgs = np.empty((self.n_classes, self.fb_basis.count), dtype=self.src.dtype) for i in tqdm(range(self.n_classes)): j = selection[i] # Get the neighbors neighbors_ids = classes[j] # Get coefs in Fourier Bessel Basis if not provided as an argument. if coefs is None: neighbors_imgs = Image(imgs[neighbors_ids]) if shifts is not None: neighbors_imgs.shift(shifts[i]) neighbors_coefs = self.fb_basis.evaluate_t(neighbors_imgs) else: neighbors_coefs = coefs[neighbors_ids] if shifts is not None: neighbors_coefs = self.fb_basis.shift( neighbors_coefs, shifts[i]) # Rotate in Fourier Bessel neighbors_coefs = self.fb_basis.rotate(neighbors_coefs, rot[j], classes_refl[j]) # Averaging in FB fb_avgs[i] = np.mean(neighbors_coefs, axis=0) # Now we convert the averaged images from FB to Cartesian. return ArrayImageSource(self.fb_basis.evaluate(fb_avgs))
def _forward(self, im, indices): im = im.copy() for i, idx in enumerate(indices): # Note: The following random seed behavior is directly taken from MATLAB Cov3D code. random_seed = self.seed + 191 * (idx + 1) im_s = randn(2 * im.res, 2 * im.res, seed=random_seed) im_s = Image(im_s).filter(self.noise_filter)[:, :, 0] im[:, :, i] += im_s[:im.res, :im.res] return im
def eval_filters(self, im_orig, start=0, num=np.inf, indices=None): im = im_orig.copy() if indices is None: indices = np.arange(start, min(start + num, self.n)) unique_filters = set(self.filters) for f in unique_filters: idx_k = np.where(self.filters[indices] == f)[0] if len(idx_k) > 0: im[:, :, idx_k] = Image(im[:, :, idx_k]).filter(f).asnumpy() return im
def setUp(self): with importlib_resources.path(tests.saved_test_data, "sample_data_model.star") as path: self.starfile = StarFile(path) # Independent Image object for testing Image source methods L = 768 self.im = Image(misc.face(gray=True).astype("float64")[:L, :L]) self.img_src = ArrayImageSource(self.im) # We also want to flex the stack logic. self.n = 21 im_stack = np.broadcast_to(self.im.data, (self.n, L, L)) # make each image methodically different im_stack = np.multiply(im_stack, np.arange(self.n)[:, None, None]) self.im_stack = Image(im_stack) self.img_src_stack = ArrayImageSource(self.im_stack) # Create a tmpdir object for this test instance self._tmpdir = tempfile.TemporaryDirectory() # Get the directory from the name attribute of the instance self.tmpdir = self._tmpdir.name
def vol_forward(self, vol, start, num): """ Apply forward image model to volume :param vol: A volume of size L-by-L-by-L. :param start: Start index of image to consider :param num: Number of images to consider :return: The images obtained from volume by projecting, applying CTFs, translating, and multiplying by the amplitude. """ all_idx = np.arange(start, min(start + num, self.n)) im = vol_project(vol, self.rots[all_idx, :, :]) im = self.eval_filters(im, start, num) im = Image(im).shift(self.offsets[all_idx, :]) im *= np.broadcast_to(self.amplitudes[all_idx], (self.L, self.L, len(all_idx))) return im
def testFBBasis2DEvaluate_t(self): v = np.load(os.path.join(DATA_DIR, "fbbasis_coefficients_8_8.npy")).T # RCOPT # While FB can accept arrays, prefable to pass FB2D and FFB2D Image instances. img = Image(v.astype(self.dtype)) result = self.basis.evaluate_t(img) self.assertTrue( np.allclose( result, [ 0.10761825, 0.12291151, 0.00836345, -0.0619454, -0.0483326, 0.01053718, 0.03977641, 0.03420101, -0.0060131, -0.02970658, -0.0151334, -0.00017575, -0.03987446, -0.00257069, -0.0006621, -0.00975174, 0.00108047, 0.00072022, 0.00753342, 0.00604493, 0.00024362, -0.01711248, -0.01387371, 0.00112805, 0.02407385, 0.00376325, 0.00081128, 0.00951368, -0.00557536, 0.01087579, 0.00255393, -0.00525156, -0.00839695, 0.00802198, ], atol=utest_tolerance(self.dtype), ) )
def testRotate(self): # Now low res (8x8) had problems; # better with odd (7x7), but still not good. # We'll use a higher res test image. # fh = np.load(os.path.join(DATA_DIR, 'ffbbasis2d_xcoeff_in_8_8.npy'))[:7,:7] # Use a real data volume to generate a clean test image. v = Volume( np.load(os.path.join(DATA_DIR, "clean70SRibosome_vol.npy")).astype( np.float64)) src = Simulation(L=v.resolution, n=1, vols=v, dtype=v.dtype) # Extract, this is the original image to transform. x1 = src.images(0, 1) # Rotate 90 degrees in cartesian coordinates. x2 = Image(np.rot90(x1.asnumpy(), axes=(1, 2))) # Express in an FB basis basis = FFBBasis2D((x1.res, ) * 2, dtype=x1.dtype) v1 = basis.evaluate_t(x1) v2 = basis.evaluate_t(x2) v3 = basis.evaluate_t(x1) v4 = basis.evaluate_t(x1) # Reflect in the FB basis space v4 = basis.rotate(v1, 0, refl=[True]) # Rotate in the FB basis space v3 = basis.rotate(v1, 2 * np.pi) v1 = basis.rotate(v1, -np.pi / 2) # Evaluate back into cartesian y1 = basis.evaluate(v1) y2 = basis.evaluate(v2) y3 = basis.evaluate(v3) y4 = basis.evaluate(v4) # Rotate 90 self.assertTrue(np.allclose(y1[0], y2[0], atol=1e-4)) # 2*pi Identity self.assertTrue( np.allclose(x1[0], y3[0], atol=utest_tolerance(self.dtype))) # Refl (flipped using flipud) self.assertTrue(np.allclose(np.flipud(x1[0]), y4[0], atol=1e-4))
def _images(self, start=0, num=np.inf, indices=None): if indices is None: indices = np.arange(start, min(start + num, self.n)) else: start = indices.min() logger.info(f"Loading {len(indices)} images from STAR file") def load_single_mrcs(filepath, df): arr = mrcfile.open(filepath).data # if the stack only contains one image, arr will have shape (resolution, resolution) # the code below reshapes it to (1, resolution, resolution) if len(arr.shape) == 2: arr = arr.reshape((1,) + arr.shape) data = arr[df["__mrc_index"] - 1, :, :] return df.index, data n_workers = self.n_workers if n_workers < 0: n_workers = cpu_count() - 1 df = self._metadata.loc[indices] im = np.empty( (len(indices), self._original_resolution, self._original_resolution), dtype=self.dtype, ) groups = df.groupby("__mrc_filepath") n_workers = min(n_workers, len(groups)) with futures.ThreadPoolExecutor(n_workers) as executor: to_do = [] for filepath, _df in groups: future = executor.submit(load_single_mrcs, filepath, _df) to_do.append(future) for future in futures.as_completed(to_do): data_indices, data = future.result() im[data_indices - start] = data logger.info(f"Loading {len(indices)} images complete") return Image(im)
def images(self, start, num, *args, **kwargs): """ Return images from this ImageSource as an Image object. :param start: The inclusive start index from which to return images. :param num: The exclusive end index up to which to return images. :param args: Any additional positional arguments to pass on to the `ImageSource`'s underlying `_images` method. :param kwargs: Any additional keyword arguments to pass on to the `ImageSource`'s underlying `_images` method. :return: an `Image` object. """ indices = np.arange(start, min(start + num, self.n)) if self._im is not None: logger.info(f'Loading images from cache') im = Image(self._im[:, :, indices]) else: im = self._images(indices=indices, *args, **kwargs) im = self.generation_pipeline.forward(im, indices=indices) logger.info(f'Loaded {len(indices)} images') return im
def estimate_psd(self, blocks, tapers_1d): """ Estimate the power spectrum of the micrograph using the multi-taper method :param blocks: 3-D NumPy array containing windows extracted from the micrograph in the preprocess function. :param tapers_1d: NumPy array of data tapers. :return: NumPy array of estimated power spectrum. """ num_1d_tapers = tapers_1d.shape[-1] tapers_1d = tapers_1d.astype(complex_type(self.dtype), copy=False) blocks_mt = np.zeros(blocks[0, :, :].shape, dtype=self.dtype) blocks_tapered = np.zeros(blocks[0, :, :].shape, dtype=complex_type(self.dtype)) taper_2d = np.zeros((blocks.shape[1], blocks.shape[2]), dtype=complex_type(self.dtype)) for ax1 in range(num_1d_tapers): for ax2 in range(num_1d_tapers): np.matmul( tapers_1d[:, ax1, np.newaxis], tapers_1d[:, ax2, np.newaxis].T, out=taper_2d, ) for m in range(blocks.shape[0]): np.multiply(blocks[m, :, :], taper_2d, out=blocks_tapered) blocks_mt_post_fft = fft.fftn(blocks_tapered, axes=(-2, -1)) blocks_mt += abs2(blocks_mt_post_fft) blocks_mt /= blocks.shape[0]**2 blocks_mt /= tapers_1d.shape[0]**2 amplitude_spectrum = fft.fftshift( blocks_mt) # max difference 10^-13, max relative difference 10^-14 return Image(amplitude_spectrum)
def __init__(self, im, metadata=None, angles=None): """ Initialize from an `Image` object :param im: An `Image` or Numpy array object representing image data served up by this `ImageSource`. In the case of a Numpy array, attempts to create an 'Image' object. :param metadata: A Dataframe of metadata information corresponding to this ImageSource's images :param angles: Optional n-by-3 array of rotation angles corresponding to `im`. """ if not isinstance(im, Image): logger.info( "Attempting to create an Image object from Numpy array.") try: im = Image(im) except Exception as e: raise RuntimeError( "Creating Image object from Numpy array failed." f" Original error: {str(e)}") super().__init__(L=im.res, n=im.n_images, dtype=im.dtype, metadata=metadata, memory=None) self._cached_im = im # Create filter indices, these are required to pass unharmed through filter eval code # that is potentially called by other methods later. self.filter_indices = np.zeros(self.n) self.unique_filters = [IdentityFilter()] # Optionally populate angles/rotations. if angles is not None: if angles.shape != (self.n, 3): raise ValueError(f"Angles should be shape {(self.n, 3)}") # This will populate `_rotations`, # which is exposed by properties `angles` and `rots`. self.angles = angles
def projections(self, start=0, num=np.inf, indices=None): """ Return projections of generated volumes, without applying filters/shifts/amplitudes/noise :param start: start index (0-indexed) of the start image to return :param num: Number of images to return. If None, *all* images are returned. :param indices: A numpy array of image indices. If specified, start and num are ignored. :return: An ndarray of shape (L, L, num), L being the size of each image. """ if indices is None: indices = np.arange(start, min(start + num, self.n)) im = np.zeros((self.L, self.L, len(indices))) states = self.states[indices] unique_states = np.unique(states) for k in unique_states: vol_k = self.vols[:, :, :, k - 1] idx_k = np.where(states == k)[0] rot = self.rots[indices[idx_k], :, :] im_k = vol_project(vol_k, rot) im[:, :, idx_k] = im_k return Image(im)
def _read(self): with mrcfile.open(self.filepath, permissive=self.permissive) as mrc: im = mrc.data if im.dtype != self.dtype: logger.info(f"Micrograph read casting {self.filepath}" f" data to {self.dtype} from {im.dtype}.") im = im.astype(self.dtype) # NOTE: For multiple mrc files, mrcfile returns an ndarray with # (shape n_images, height, width) # Discard outer pixels im = im[..., self.margin_top:-self.margin_bottom if self. margin_bottom is not None else None, self.margin_left:-self.margin_right if self. margin_right is not None else None, ] if self.square: side_length = min(im.shape[-2], im.shape[-1]) im = im[..., :side_length, :side_length] if self.shrink_factor is not None: size = tuple((np.array(im.shape) / config.apple.mrc_shrink_factor).astype(int)) im = np.array( PILImage.fromarray(im).resize(size, PILImage.BICUBIC)) if self.gauss_filter_size is not None: im = signal.correlate( im, Micrograph.gaussian_filter(self.gauss_filter_size, self.gauss_filter_sigma), "same", ) self.im = Image(im) self.shape = self.im.shape
def setUp(self): self.dtype = np.float32 self.resolution = 8 self.n = 1024 # Generate a stack of images self.sim = sim = Simulation( n=self.n, L=self.resolution, unique_filters=[IdentityFilter()], seed=0, dtype=self.dtype, # We'll use random angles offsets=np.zeros((self.n, 2)), # No offsets amplitudes=np.ones((self.n)), # Constant amplitudes ) # Expose images as numpy array. self.ims_np = sim.images(0, sim.n).asnumpy() self.im = Image(self.ims_np) # Vol estimation requires a 3D basis self.basis = FBBasis3D((self.resolution,) * 3, dtype=self.dtype)
def projections(self, start=0, num=np.inf, indices=None): """ Return projections of generated volumes, without applying filters/shifts/amplitudes/noise :param start: start index (0-indexed) of the start image to return :param num: Number of images to return. If None, *all* images are returned. :param indices: A numpy array of image indices. If specified, start and num are ignored. :return: An Image instance. """ if indices is None: indices = np.arange(start, min(start + num, self.n)) im = np.zeros((len(indices), self._original_L, self._original_L), dtype=self.dtype) states = self.states[indices] unique_states = np.unique(states) for k in unique_states: idx_k = np.where(states == k)[0] rot = self.rots[indices[idx_k], :, :] im_k = self.vols.project(vol_idx=k - 1, rot_matrices=rot) im[idx_k, :, :] = im_k.asnumpy() return Image(im)
def _images(self, start=0, num=np.inf, indices=None): if indices is None: indices = np.arange(start, min(start + num, self.n)) else: start = indices.min() logger.info(f'Loading {len(indices)} images from STAR file') def load_single_mrcs(filepath, df): arr = mrcfile.open(filepath).data data = arr[df['__mrc_index'] - 1, :, :].T return df.index, data n_workers = self.n_workers if n_workers < 0: n_workers = cpu_count() - 1 df = self._metadata.loc[indices] im = np.empty((self._original_resolution, self._original_resolution, len(indices))) groups = df.groupby('__mrc_filepath') n_workers = min(n_workers, len(groups)) with futures.ThreadPoolExecutor(n_workers) as executor: to_do = [] for filepath, _df in groups: future = executor.submit(load_single_mrcs, filepath, _df) to_do.append(future) for future in futures.as_completed(to_do): data_indices, data = future.result() im[:, :, data_indices-start] = data logger.info(f'Loading {len(indices)} images complete') return Image(im)
def _images(self, start=0, num=np.inf, indices=None): if indices is None: indices = np.arange(start, min(start + num, self.n)) return Image(self.im[:, :, indices])
def setUp(self): # numpy array for top-level functions that directly expect it self.im_np = misc.face( gray=True).astype('float64')[:768, :768][:, :, np.newaxis] # Independent Image object for testing Image methods self.im = Image(misc.face(gray=True).astype('float64')[:768, :768])
n_pixels = min(stock_img.shape) stock_img = stock_img[0:n_pixels, 0:n_pixels] # Normalize to [0,1] stock_img /= np.max(stock_img) # %% # Add Noise to the Image # ---------------------- # Now that we have an example array, we will begin using the ASPIRE toolkit. # First we will make an ASPIRE Image instance out of our data. # This is a light wrapper over the numpy array. Many ASPIRE internals # are built around an ``Image`` class. # Construct the Image class by passing it an array of data. img = Image(stock_img) # Downsample (just to speeds things up) new_resolution = img.res // 4 img = img.downsample(new_resolution) # We will begin processing by adding some noise. # We would like to create uniform noise for a 2d image with prescibed variance, noise_var = np.var(img.asnumpy()) * 5 noise_filter = ScalarFilter(dim=2, value=noise_var) # Then create a NoiseAdder. noise = NoiseAdder(seed=123, noise_filter=noise_filter) # We can apply the NoiseAdder to our image data. img_with_noise = noise.forward(img)
def evaluate_t(self, x): """ Evaluate coefficient in FB basis from those in standard 2D coordinate basis :param x: The Image instance representing coefficient array in the standard 2D coordinate basis to be evaluated. :return v: The evaluation of the coefficient array `v` in the FB basis. This is an array of vectors whose last dimension equals `self.count` and whose first dimension correspond to `x.n_images`. """ if x.dtype != self.dtype: logger.warning( f"{self.__class__.__name__}::evaluate_t" f" Inconsistent dtypes v: {x.dtype} self: {self.dtype}") if not isinstance(x, Image): logger.warning(f"{self.__class__.__name__}::evaluate_t" " passed numpy array instead of Image.") x = Image(x) # get information on polar grids from precomputed data n_theta = np.size(self._precomp["freqs"], 2) n_r = np.size(self._precomp["freqs"], 1) freqs = np.reshape(self._precomp["freqs"], (2, n_r * n_theta)) # number of 2D image samples n_images = x.n_images x_data = x.data # resamping x in a polar Fourier gird using nonuniform discrete Fourier transform pf = nufft(x_data, 2 * pi * freqs) pf = np.reshape(pf, (n_images, n_r, n_theta)) # Recover "negative" frequencies from "positive" half plane. pf = np.concatenate((pf, pf.conjugate()), axis=2) # evaluate radial integral using the Gauss-Legendre quadrature rule for i_r in range(0, n_r): pf[:, i_r, :] = pf[:, i_r, :] * (self._precomp["gl_weights"][i_r] * self._precomp["gl_nodes"][i_r]) # 1D FFT on the angular dimension for each concentric circle pf = 2 * pi / (2 * n_theta) * xp.asnumpy(fft.fft(xp.asarray(pf))) # This only makes it easier to slice the array later. v = np.zeros((n_images, self.count), dtype=x.dtype) # go through each basis function and find the corresponding coefficient ind = 0 idx = ind + np.arange(self.k_max[0]) mask = self._indices["ells"] == 0 # include the normalization factor of angular part into radial part radial_norm = self._precomp["radial"] / np.expand_dims( self.angular_norms, 1) v[:, mask] = pf[:, :, 0].real @ radial_norm[idx].T ind = ind + np.size(idx) ind_pos = ind for ell in range(1, self.ell_max + 1): idx = ind + np.arange(self.k_max[ell]) idx_pos = ind_pos + np.arange(self.k_max[ell]) idx_neg = idx_pos + self.k_max[ell] v_ell = pf[:, :, ell] @ radial_norm[idx].T if np.mod(ell, 2) == 0: v_pos = np.real(v_ell) v_neg = -np.imag(v_ell) else: v_pos = np.imag(v_ell) v_neg = np.real(v_ell) v[:, idx_pos] = v_pos v[:, idx_neg] = v_neg ind = ind + np.size(idx) ind_pos = ind_pos + 2 * self.k_max[ell] return v
def evaluate(self, v): """ Evaluate coefficients in standard 2D coordinate basis from those in FB basis :param v: A coefficient vector (or an array of coefficient vectors) in FB basis to be evaluated. The last dimension must equal `self.count`. :return x: The evaluation of the coefficient vector(s) `x` in standard 2D coordinate basis. This is Image instance with resolution of `self.sz` and the first dimension correspond to remaining dimension of `v`. """ if v.dtype != self.dtype: logger.debug( f"{self.__class__.__name__}::evaluate" f" Inconsistent dtypes v: {v.dtype} self: {self.dtype}") sz_roll = v.shape[:-1] v = v.reshape(-1, self.count) # number of 2D image samples n_data = v.shape[0] # get information on polar grids from precomputed data n_theta = np.size(self._precomp["freqs"], 2) n_r = np.size(self._precomp["freqs"], 1) # go through each basis function and find corresponding coefficient pf = np.zeros((n_data, 2 * n_theta, n_r), dtype=complex_type(self.dtype)) mask = self._indices["ells"] == 0 ind = 0 idx = ind + np.arange(self.k_max[0], dtype=int) # include the normalization factor of angular part into radial part radial_norm = self._precomp["radial"] / np.expand_dims( self.angular_norms, 1) pf[:, 0, :] = v[:, mask] @ radial_norm[idx] ind = ind + np.size(idx) ind_pos = ind for ell in range(1, self.ell_max + 1): idx = ind + np.arange(self.k_max[ell], dtype=int) idx_pos = ind_pos + np.arange(self.k_max[ell], dtype=int) idx_neg = idx_pos + self.k_max[ell] v_ell = (v[:, idx_pos] - 1j * v[:, idx_neg]) / 2.0 if np.mod(ell, 2) == 1: v_ell = 1j * v_ell pf_ell = v_ell @ radial_norm[idx] pf[:, ell, :] = pf_ell if np.mod(ell, 2) == 0: pf[:, 2 * n_theta - ell, :] = pf_ell.conjugate() else: pf[:, 2 * n_theta - ell, :] = -pf_ell.conjugate() ind = ind + np.size(idx) ind_pos = ind_pos + 2 * self.k_max[ell] # 1D inverse FFT in the degree of polar angle pf = 2 * pi * xp.asnumpy(fft.ifft(xp.asarray(pf), axis=1)) # Only need "positive" frequencies. hsize = int(np.size(pf, 1) / 2) pf = pf[:, 0:hsize, :] for i_r in range(0, n_r): pf[..., i_r] = pf[..., i_r] * (self._precomp["gl_weights"][i_r] * self._precomp["gl_nodes"][i_r]) pf = np.reshape(pf, (n_data, n_r * n_theta)) # perform inverse non-uniformly FFT transform back to 2D coordinate basis freqs = m_reshape(self._precomp["freqs"], (2, n_r * n_theta)) x = 2 * anufft(pf, 2 * pi * freqs, self.sz, real=True) # Return X as Image instance with the last two dimensions as *self.sz x = x.reshape((*sz_roll, *self.sz)) return Image(x)
# To be consistent with the Matlab version in the numbers, we need to use the statements as below: logger.info( 'Generate random distributed rotation angles and obtain corresponding 2D clean images.' ) rots = qrand_rots(num_imgs, seed=0) imgs_clean = vol2img(sim.vols[..., 0], rots) # Assign the CTF information and index for each image h_idx = np.array([filters.index(f) for f in sim.filters]) # Evaluate CTF in the 8X8 FB basis h_ctf_fb = [filt.fb_mat(ffbbasis) for filt in filters] # Apply the CTF to the clean images. logger.info('Apply CTF filters to clean images.') imgs_ctf_clean = Image(sim.eval_filters(imgs_clean)) sim.cache(imgs_ctf_clean) # imgs_ctf_clean is an Image object. Convert to numpy array for subsequent statements imgs_ctf_clean = imgs_ctf_clean.asnumpy() # Apply the noise at the desired singal-noise ratio to the filtered clean images logger.info('Apply noise filters to clean images.') power_clean = anorm(imgs_ctf_clean)**2 / np.size(imgs_ctf_clean) noise_var = power_clean / sn_ratio imgs_noise = imgs_ctf_clean + np.sqrt(noise_var) * randn( img_size, img_size, num_imgs, seed=0) # Expand the images, both clean and noisy, in the Fourier-Bessel basis. This # can be done exactly (that is, up to numerical precision) using the # `basis.expand` function, but for our purposes, an approximation will do.
def _forward(self, im, indices): im_in = im.asnumpy() im_out = self.lambda_fun(im_in, *self.args, **self.kwargs) return Image(im_out)
def testPolarBasis2DEvaluate_t(self): x = Image( np.array( [ [ 0.00000000e00, 0.00000000e00, 0.00000000e00, 0.00000000e00, -1.08106869e-17, 0.00000000e00, 0.00000000e00, 0.00000000e00, ], [ 0.00000000e00, 0.00000000e00, -6.40456062e-03, -3.32961020e-03, -1.36887927e-02, -5.42770488e-03, 7.63680861e-03, 0.00000000e00, ], [ 0.00000000e00, 3.16377602e-03, -9.31273350e-03, 9.46128404e-03, 1.93239220e-02, 3.79891953e-02, 1.06841173e-02, -2.36467925e-03, ], [ 0.00000000e00, 1.72736955e-03, -1.00710814e-02, 4.93520304e-02, 3.77702656e-02, 6.57365438e-02, 3.94739462e-03, -4.41228496e-03, ], [ 4.01551066e-18, -3.08071647e-03, -1.61670565e-02, 8.66886286e-02, 5.09898409e-02, 7.19313349e-02, 1.68313715e-02, 5.19180892e-03, ], [ 0.00000000e00, 2.87262215e-03, -3.37732956e-02, 4.51706505e-02, 5.72215879e-02, 4.63553081e-02, 1.86552175e-03, 1.12608805e-02, ], [ 0.00000000e00, 2.77905016e-03, -2.77499404e-02, -4.02645374e-02, -1.54969139e-02, -1.66229153e-02, -2.07389259e-02, 6.64060546e-03, ], [ 0.00000000e00, 0.00000000e00, 5.20080934e-03, -1.06788196e-02, -1.14761672e-02, -1.27443126e-02, -1.15563484e-02, 0.00000000e00, ], ], dtype=self.dtype, ).T) # RCOPT pf = self.basis.evaluate_t(x) result = np.array( [ 0.38243133 + 6.66608316e-18j, 0.3249317 - 1.47839074e-01j, 0.14819172 + 3.78171168e-03j, -0.22808599 + 5.29338933e-02j, 0.38243133 + 6.66608316e-18j, 0.34595014 - 1.06355385e-01j, 0.15519289 - 4.75602164e-02j, -0.22401193 + 4.33128746e-03j, 0.38243133 + 6.66608316e-18j, 0.36957165 - 5.69575709e-02j, 0.17389327 - 5.53498385e-02j, -0.11601473 - 1.35405676e-02j, 0.38243133 + 6.66608316e-18j, 0.39045046 - 2.17911945e-03j, 0.18146449 - 1.37089189e-02j, -0.02110144 + 6.65071497e-03j, 0.38243133 + 6.66608316e-18j, 0.4063995 + 5.21354967e-02j, 0.15674204 + 3.85815662e-02j, -0.02886296 + 3.91489615e-02j, 0.38243133 + 6.66608316e-18j, 0.41872477 + 9.98946906e-02j, 0.11862477 + 5.15231952e-02j, -0.05298751 + 1.95319478e-02j, 0.38243133 + 6.66608316e-18j, 0.43013599 + 1.38307796e-01j, 0.10075763 + 1.25689289e-02j, -0.04052728 - 5.66863498e-02j, 0.38243133 + 6.66608316e-18j, 0.44144497 + 1.68826980e-01j, 0.11446016 - 4.53003874e-02j, -0.03546515 - 1.13544145e-01j, 0.38243133 + 6.66608316e-18j, 0.44960099 + 1.94794929e-01j, 0.15053714 - 8.11915305e-02j, -0.04800556 - 1.15828804e-01j, 0.38243133 + 6.66608316e-18j, 0.44872328 + 2.17957567e-01j, 0.19116871 - 7.99536373e-02j, -0.05683092 - 9.72225058e-02j, 0.38243133 + 6.66608316e-18j, 0.43379428 + 2.36681249e-01j, 0.21025378 - 5.48466438e-02j, -0.05318826 - 8.54948014e-02j, 0.38243133 + 6.66608316e-18j, 0.40485577 + 2.47073481e-01j, 0.18680217 - 3.31766116e-02j, -0.06674163 - 7.94216591e-02j, 0.38243133 + 6.66608316e-18j, 0.36865853 + 2.45913767e-01j, 0.13660805 - 3.68947359e-02j, -0.11467046 - 8.49198927e-02j, 0.38243133 + 6.66608316e-18j, 0.33597018 + 2.32971425e-01j, 0.1072859 - 6.24686168e-02j, -0.12932565 - 1.06139634e-01j, 0.38243133 + 6.66608316e-18j, 0.31616666 + 2.10791785e-01j, 0.11876919 - 7.93812474e-02j, -0.1094488 - 1.20159845e-01j, 0.38243133 + 6.66608316e-18j, 0.31313975 + 1.82190396e-01j, 0.14075481 - 5.85637416e-02j, -0.15198775 - 1.02156797e-01j, 0.38243133 - 6.66608316e-18j, 0.3249317 + 1.47839074e-01j, 0.14819172 - 3.78171168e-03j, -0.22808599 - 5.29338933e-02j, 0.38243133 - 6.66608316e-18j, 0.34595014 + 1.06355385e-01j, 0.15519289 + 4.75602164e-02j, -0.22401193 - 4.33128746e-03j, 0.38243133 - 6.66608316e-18j, 0.36957165 + 5.69575709e-02j, 0.17389327 + 5.53498385e-02j, -0.11601473 + 1.35405676e-02j, 0.38243133 - 6.66608316e-18j, 0.39045046 + 2.17911945e-03j, 0.18146449 + 1.37089189e-02j, -0.02110144 - 6.65071497e-03j, 0.38243133 - 6.66608316e-18j, 0.4063995 - 5.21354967e-02j, 0.15674204 - 3.85815662e-02j, -0.02886296 - 3.91489615e-02j, 0.38243133 - 6.66608316e-18j, 0.41872477 - 9.98946906e-02j, 0.11862477 - 5.15231952e-02j, -0.05298751 - 1.95319478e-02j, 0.38243133 - 6.66608316e-18j, 0.43013599 - 1.38307796e-01j, 0.10075763 - 1.25689289e-02j, -0.04052728 + 5.66863498e-02j, 0.38243133 - 6.66608316e-18j, 0.44144497 - 1.68826980e-01j, 0.11446016 + 4.53003874e-02j, -0.03546515 + 1.13544145e-01j, 0.38243133 - 6.66608316e-18j, 0.44960099 - 1.94794929e-01j, 0.15053714 + 8.11915305e-02j, -0.04800556 + 1.15828804e-01j, 0.38243133 - 6.66608316e-18j, 0.44872328 - 2.17957567e-01j, 0.19116871 + 7.99536373e-02j, -0.05683092 + 9.72225058e-02j, 0.38243133 - 6.66608316e-18j, 0.43379428 - 2.36681249e-01j, 0.21025378 + 5.48466438e-02j, -0.05318826 + 8.54948014e-02j, 0.38243133 - 6.66608316e-18j, 0.40485577 - 2.47073481e-01j, 0.18680217 + 3.31766116e-02j, -0.06674163 + 7.94216591e-02j, 0.38243133 - 6.66608316e-18j, 0.36865853 - 2.45913767e-01j, 0.13660805 + 3.68947359e-02j, -0.11467046 + 8.49198927e-02j, 0.38243133 - 6.66608316e-18j, 0.33597018 - 2.32971425e-01j, 0.1072859 + 6.24686168e-02j, -0.12932565 + 1.06139634e-01j, 0.38243133 - 6.66608316e-18j, 0.31616666 - 2.10791785e-01j, 0.11876919 + 7.93812474e-02j, -0.1094488 + 1.20159845e-01j, 0.38243133 - 6.66608316e-18j, 0.31313975 - 1.82190396e-01j, 0.14075481 + 5.85637416e-02j, -0.15198775 + 1.02156797e-01j, ], dtype=complex_type(self.dtype), ) self.assertTrue(np.allclose(pf, result))
import numpy as np from aspire.image import Image from aspire.operators import CTFFilter DATA_DIR = "data" img_data = np.load(os.path.join(DATA_DIR, "monuments.npy")) img_data.shape, img_data.dtype # %% # Create an Image Instance # ------------------------ # Create an ASPIRE Image instance from the data # We'll tell it to convert to floating point data as well. im = Image(img_data, dtype=np.float64) # %% # Plot the Image Stack # -------------------- # Plot the Image stack im.show() # %% # Apply a Uniform Shift # --------------------- # Apply a single shift to each image. shifts = np.array([100, 30]) im.shift(shifts).show()
# %% # Review a class # -------------- # # Select a class to review. review_class = 5 # Display the original image. noisy_src.images(review_class, 1).show() # Report the identified neighbor indices logger.info(f"Class {review_class}'s neighors: {classes[review_class]}") # Report the identified neighbors Image(noisy_src.images(0, np.inf)[classes[review_class]]).show() # Report their associated rots_refls rots_refls = ["index, Rotation, Reflection"] for i in range(classes.shape[1]): rots_refls.append( f"{i}, {rotations[review_class, i] * 180 / np.pi}, {reflections[review_class, i]}" ) rots_refls = "\n".join(rots_refls) logger.info( f"Class {review_class}'s estimated Rotations and Reflections:\n{rots_refls}" ) # Display the averaged result avgs.images(review_class, 1).show()