|
| 1 | +""" |
| 2 | +Abinitio Pipeline - Experimental Data |
| 3 | +===================================== |
| 4 | +
|
| 5 | +This notebook introduces a selection of |
| 6 | +components corresponding to loading real Relion picked |
| 7 | +particle Cryo-EM data and running key ASPIRE-Python |
| 8 | +Abinitio model components as a pipeline. |
| 9 | +
|
| 10 | +Specifically this pipeline uses the |
| 11 | +EMPIAR 10028 picked particles data, available here: |
| 12 | +
|
| 13 | +https://www.ebi.ac.uk/empiar/EMPIAR-10028 |
| 14 | +
|
| 15 | +https://www.ebi.ac.uk/emdb/EMD-10028 |
| 16 | +""" |
| 17 | + |
| 18 | +# %% |
| 19 | +# Imports |
| 20 | +# ------- |
| 21 | +# First import some of the usual suspects. |
| 22 | +# In addition, import some classes from |
| 23 | +# the ASPIRE package that will be used throughout this experiment. |
| 24 | + |
| 25 | +import logging |
| 26 | + |
| 27 | +import matplotlib.pyplot as plt |
| 28 | +import numpy as np |
| 29 | + |
| 30 | +from aspire.abinitio import CLSyncVoting |
| 31 | +from aspire.basis import FFBBasis2D, FFBBasis3D |
| 32 | +from aspire.classification import BFSReddyChatterjiAverager2D, RIRClass2D |
| 33 | +from aspire.denoising import DenoiserCov2D |
| 34 | +from aspire.noise import AnisotropicNoiseEstimator |
| 35 | +from aspire.reconstruction import MeanEstimator |
| 36 | +from aspire.source import RelionSource |
| 37 | + |
| 38 | +logger = logging.getLogger(__name__) |
| 39 | + |
| 40 | + |
| 41 | +# %% |
| 42 | +# Parameters |
| 43 | +# --------------- |
| 44 | +# Example simulation configuration. |
| 45 | + |
| 46 | +interactive = False # Draw blocking interactive plots? |
| 47 | +do_cov2d = True # Use CWF coefficients |
| 48 | +n_imgs = 20000 # Set to None for all images in starfile, can set smaller for tests. |
| 49 | +img_size = 32 # Downsample the images/reconstruction to a desired resolution |
| 50 | +n_classes = 1000 # How many class averages to compute. |
| 51 | +n_nbor = 50 # How many neighbors to stack |
| 52 | +starfile_in = "10028/data/shiny_2sets.star" |
| 53 | +volume_filename_prefix_out = f"10028_recon_c{n_classes}_m{n_nbor}_{img_size}.mrc" |
| 54 | +pixel_size = 1.34 |
| 55 | + |
| 56 | +# %% |
| 57 | +# Source data and Preprocessing |
| 58 | +# ----------------------------- |
| 59 | +# |
| 60 | +# `RelionSource` is used to access the experimental data via a `starfile`. |
| 61 | +# Begin by downsampling to our chosen resolution, then preprocess |
| 62 | +# to correct for CTF and noise. |
| 63 | + |
| 64 | +# Create a source object for the experimental images |
| 65 | +src = RelionSource(starfile_in, pixel_size=pixel_size, max_rows=n_imgs) |
| 66 | + |
| 67 | +# Downsample the images |
| 68 | +logger.info(f"Set the resolution to {img_size} X {img_size}") |
| 69 | +src.downsample(img_size) |
| 70 | + |
| 71 | +# Peek |
| 72 | +if interactive: |
| 73 | + src.images(0, 10).show() |
| 74 | + |
| 75 | +# Use phase_flip to attempt correcting for CTF. |
| 76 | +logger.info("Perform phase flip to input images.") |
| 77 | +src.phase_flip() |
| 78 | + |
| 79 | +# Estimate the noise and `Whiten` based on the estimated noise |
| 80 | +aiso_noise_estimator = AnisotropicNoiseEstimator(src) |
| 81 | +src.whiten(aiso_noise_estimator.filter) |
| 82 | + |
| 83 | +# Plot the noise profile for inspection |
| 84 | +if interactive: |
| 85 | + plt.imshow(aiso_noise_estimator.filter.evaluate_grid(img_size)) |
| 86 | + plt.show() |
| 87 | + |
| 88 | +# Peek, what do the whitened images look like... |
| 89 | +if interactive: |
| 90 | + src.images(0, 10).show() |
| 91 | + |
| 92 | +# # Optionally invert image contrast, depends on data convention. |
| 93 | +# # This is not needed for 10028, but included anyway. |
| 94 | +# logger.info("Invert the global density contrast") |
| 95 | +# src.invert_contrast() |
| 96 | + |
| 97 | +# %% |
| 98 | +# Optional: CWF Denoising |
| 99 | +# ----------------------- |
| 100 | +# |
| 101 | +# Optionally generate an alternative source that is denoised with `cov2d`, |
| 102 | +# then configure a customized averager. This allows the use of CWF denoised |
| 103 | +# images for classification, but stacks the original images for averages |
| 104 | +# used in the remainder of the reconstruction pipeline. |
| 105 | +# |
| 106 | +# In this example, this behavior is controlled by the `do_cov2d` boolean variable. |
| 107 | +# When disabled, the original src and default averager is used. |
| 108 | +# If you will not be using cov2d, |
| 109 | +# you may remove this code block and associated variables. |
| 110 | + |
| 111 | +classification_src = src |
| 112 | +custom_averager = None |
| 113 | +if do_cov2d: |
| 114 | + # Use CWF denoising |
| 115 | + cwf_denoiser = DenoiserCov2D(src) |
| 116 | + # Use denoised src for classification |
| 117 | + classification_src = cwf_denoiser.denoise() |
| 118 | + # Peek, what do the denoised images look like... |
| 119 | + if interactive: |
| 120 | + classification_src.images(0, 10).show() |
| 121 | + |
| 122 | + # Use regular `src` for the alignment and composition (averaging). |
| 123 | + composite_basis = FFBBasis2D((src.L,) * 2, dtype=src.dtype) |
| 124 | + custom_averager = BFSReddyChatterjiAverager2D(composite_basis, src, dtype=src.dtype) |
| 125 | + |
| 126 | + |
| 127 | +# %% |
| 128 | +# Class Averaging |
| 129 | +# ---------------------- |
| 130 | +# |
| 131 | +# Now perform classification and averaging for each class. |
| 132 | + |
| 133 | +logger.info("Begin Class Averaging") |
| 134 | + |
| 135 | +rir = RIRClass2D( |
| 136 | + classification_src, # Source used for classification |
| 137 | + fspca_components=400, |
| 138 | + bispectrum_components=300, # Compressed Features after last PCA stage. |
| 139 | + n_nbor=n_nbor, |
| 140 | + n_classes=n_classes, |
| 141 | + large_pca_implementation="legacy", |
| 142 | + nn_implementation="sklearn", |
| 143 | + bispectrum_implementation="legacy", |
| 144 | + averager=custom_averager, |
| 145 | +) |
| 146 | + |
| 147 | +classes, reflections, distances = rir.classify() |
| 148 | +avgs = rir.averages(classes, reflections, distances) |
| 149 | +if interactive: |
| 150 | + avgs.images(0, 10).show() |
| 151 | + |
| 152 | +# %% |
| 153 | +# Common Line Estimation |
| 154 | +# ---------------------- |
| 155 | +# |
| 156 | +# Next create a CL instance for estimating orientation of projections |
| 157 | +# using the Common Line with Synchronization Voting method. |
| 158 | + |
| 159 | +logger.info("Begin Orientation Estimation") |
| 160 | + |
| 161 | +orient_est = CLSyncVoting(avgs, n_theta=36) |
| 162 | +# Get the estimated rotations |
| 163 | +orient_est.estimate_rotations() |
| 164 | +rots_est = orient_est.rotations |
| 165 | + |
| 166 | +# %% |
| 167 | +# Volume Reconstruction |
| 168 | +# ---------------------- |
| 169 | +# |
| 170 | +# Using the estimated rotations, attempt to reconstruct a volume. |
| 171 | + |
| 172 | +logger.info("Begin Volume reconstruction") |
| 173 | + |
| 174 | +# Assign the estimated rotations to the class averages |
| 175 | +avgs.rots = rots_est |
| 176 | + |
| 177 | +# Create a reasonable Basis for the 3d Volume |
| 178 | +basis = FFBBasis3D((img_size,) * 3, dtype=src.dtype) |
| 179 | + |
| 180 | +# Setup an estimator to perform the back projection. |
| 181 | +estimator = MeanEstimator(avgs, basis) |
| 182 | + |
| 183 | +# Perform the estimation and save the volume. |
| 184 | +estimated_volume = estimator.estimate() |
| 185 | +estimated_volume.save(volume_filename_prefix_out, overwrite=True) |
| 186 | + |
| 187 | +# Peek at result |
| 188 | +if interactive: |
| 189 | + plt.imshow(np.sum(estimated_volume[0], axis=-1)) |
| 190 | + plt.show() |
0 commit comments