Inspect raw images of particles of certain 2/3D classes

Hey all,

is there a possibility to select a certain class from a 2D classification job and display the raw images of all particles belonging to that class?
The way I do it is to run a select job to select one class, run an extract job with particles of this class and then open the mrc files in the corresponding job-folder. But that is somewhat cumbersome.

1 Like

from long ago, this should be do-able in cryosparc-tools

1 Like

I would also be interested, if someone figured out how to do this.
It would help a lot to judge the quality of 2d classification when working with certain datasets.

It is indeed possible using cryosparc-tools. I’ve included a script to do this below.

I do want to point out that we do not expect single particle images to be particularly interpretable, or look like much at all. For instance, here are some particle images from a T20S dataset which goes to 2.8 Å:


from import CryoSPARC
import json
import numpy as np
from pathlib import Path
import matplotlib.pyplot as plt
with open(Path('~/instance-info.json').expanduser(), 'r') as f:
    instance_info = json.load(f)

cs = CryoSPARC(**instance_info)
assert cs.test_connection()

project_number = "P325"
workspace_number = "W4"
job_number = "J117"
class_number = 4

project = cs.find_project(project_number)
job = project.find_job(job_number)
particles = job.load_output("particles")

particle_subset = particles.query({'alignments2d/class': class_number})
particle_subset = particle_subset.query({
    'uid': np.random.choice(
            particle_subset['uid'], size = 12, replace = False

mrcs = {
    path: project.download_mrc(path)[1]
    for path in set(
        row['blob/path'] for row in particle_subset.rows()
particle_images = []
for row in particle_subset.rows():

fig, axs = plt.subplots(3, 4)
plt.subplots_adjust(wspace=0.1, hspace=0.1)
axs = axs.flatten()
for idx, ax in enumerate(axs):
    img = particle_images[idx]
    # set greyscale to cover the middle 90% of pixel values.
    # Note this means that 10% of pixels are clipped to 0 or 1.
    lims = (np.percentile(img, 5), np.percentile(img, 95))
        cmap = 'gray',
        vmin = lims[0],
        vmax = lims[1]

For more help on running this script, please see the guidance in the CryoSPARC Tools example repo.

1 Like

Hi Rich - the particles should be pretty clearly visible (and interpretable) if contrast adjusted and lowpass filtered though, right? After all, they are clearly identifiable on the original micrographs?

1 Like

Hi @olibclarke! This is a good point – I suppose I should have said “…look like much at all without additional processing”. Certainly if the particle images are lowpass filtered, we’d expect it to become clear what they are.

1 Like

Is it possible to do this on the fly for visualization (prior to the greyscale adjustment)? Or does it require running a Downsample job on the whole stack? Or some simpler adjustments - e.g. just adjusting levels a bit makes the same particles more visible (just done all at once here, not per particle):


How are the lowpass filtered particles in the extract micrographs log generated? These are much more visually interpretable:

Hi @olibclarke! Definitely can be done on the fly. We use internal code to present the images, so this won’t be exactly the same as what you see in the GUI, but using scikit-image’s Butterworth filter, we can produce this plot of low-pass filtered particle images (note the images are of different particles b/c the script picked a different random sample when I re-ran it):


from skimage.filters import butterworth

N = particle_images[0].shape[0]

def lowpass(img):
    return butterworth(
        cutoff_frequency_ratio = 6/N,
        high_pass = False,
        order = 1

fig_lp, axs_lp = plt.subplots(3, 4)
plt.subplots_adjust(wspace=0.1, hspace=0.1)
axs_lp = axs_lp.flatten()
for idx, ax in enumerate(axs_lp):
    img = lowpass(particle_images[idx])
    ax.imshow(img, cmap = 'gray')
1 Like

this is great, thanks Rich!

Realized I overlooked a minor detail — using 12/N for the cutoff_frequency_ratio will result in something a bit more like what you see in CryoSPARC: