Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Identifying Regions of Interest with Segmentation

Open In Colab

The Allen Institute uses Suite2P for processing 2-Photon Calcium Imaging Data. Specifically, Suite2P takes a 2P movie, and outputs information about the putative segmented Regions of Interest (ROIs) Pachitariu et al. (2017). The most pertinent information outputted are the locations/shapes of the ROIs within the 2P Movie’s field-of-view, as well as the fluorescence of each ROI at every frame of the movie. This notebook serves as a simple demonstration of how to input a 2P Movie into Suite2P and produce cell segmentation and fluorescence output.

Environment Setup

⚠️Note: If running on a new environment, run this cell once and then restart the kernel⚠️

try:
    from databook_utils.dandi_utils import dandi_download_open
except:
    !git clone --depth 1 https://github.com/AllenInstitute/openscope_databook.git
    %cd openscope_databook
    %pip install -e .
import h5py
import os
import suite2p

import pandas as pd
import urllib.request
import numpy as np
import matplotlib.pyplot as plt

from dandi import download
from dandi import dandiapi

%matplotlib inline

Getting The Classifier

Suite2p uses a linear regression classifier in order to identify ROIs. If a classifier is not provided, suite2p uses a default classifier. However, the segmentation output is much better if a classifier trained on manually annotated data is used. The databook includes a classifier file that was trained using the Suite2p GUI. The Suite2p GUI is a very powerful platform that exceeds the limits of what can be shown in a Jupyter notebook. The cell below ensures that the classifier we need is present in the right location. In case this notebooks is being run outside the OpenScope Databook, it must be downloaded.

To make your own classifier with the Suite2p GUI, you must first load an iscell.npy file. This is one of the output files of Suite2p shown toward the end of this notebook. Then you can view both cell and non-cell ROIs by pressing the both button in the top of the GUI. From there, right-clicking ROIs will move them from either the cell or non-cell classification. After the manual classification is done, the classified data can be saved by pressing add current data to classifier in the bottom left.

classifier_path = "../../data/suite2p_classifier.npy"
if not os.path.exists(classifier_path):
    url = "https://github.com/AllenInstitute/openscope_databook/blob/e5292764ac3edb0e798196df60dd5bf24732ad78/data/suite2p_classifier.npy"
    urllib.request.urlretrieve(url, "./suite2p_classifier.npy")
    classifier_path = "./suite2p_classifier.npy"

Downloading Ophys NWB Files

Here you can download several files for a subject that you can run nway matching on. The pipeline can take in any number of input sessions, however, the input ophys data should be from the same imaging plane of the same subject. To specify your own file to use, set dandiset_id to be the dandiset id of the files you’re interested in. Also set input_dandi_filepaths to be a list of the filepaths on dandi of each file you’re interested in providing to Nway Matching. When accessing an embargoed dataset, set dandi_api_key to be your DANDI API key.

dandiset_id = "000037"
dandi_filepath = "sub-408021/sub-408021_ses-758519303_obj-raw_behavior+image+ophys.nwb"
download_loc = "."
dandi_api_key = None
client = dandiapi.DandiAPIClient(token=dandi_api_key)
dandiset = client.get_dandiset(dandiset_id)

file = dandiset.get_asset_by_path(dandi_filepath)
file_url = file.download_url

filename = dandi_filepath.split("/")[-1]
nwb_filepath = f"{download_loc}/{filename}"

h5_filepath = nwb_filepath.replace("nwb","h5")
if os.path.exists(nwb_filepath) or os.path.exists(h5_filepath):
    print("File already exists")
else:
    download.download(file_url, output_dir=download_loc)
    print(f"Downloaded file to {nwb_filepath}")
    os.rename(nwb_filepath, h5_filepath)
A newer version (0.76.5) of dandi/dandi-cli is available. You are using 0.76.4
PATH                                                      SIZE    DONE            DONE% CHECKSUM STATUS          MESSAGE
sub-408021_ses-758519303_obj-raw_behavior+image+ophys.nwb 46.1 GB 46.1 GB          100%    ok    done                   
Summary:                                                  46.1 GB 46.1 GB                        1 done                 
                                                                  100.00%                                               
Downloaded file to ./sub-408021_ses-758519303_obj-raw_behavior+image+ophys.nwb

Preparing Suite2P Input

Typically, the 2P movie would not come prepared within an NWB file, but would be in some other form. Suite2P is capable of taking input in many forms, enumerated here. Suite2P is capable of taking input in the form of h5py files. Since NWB files are a subset of h5py, we can convert it just by renaming the file and opening it with h5py.file This will have to be undone later in order to protect the validity of the file for PyNWB. The main setting to tweak the output of the segmentation is threshold_scaling. Lowering the threshold scaling makes the segmentation algorithm more sensitive, and will result in more segmented regions in the output. Since the classifier we provide is reasonably trained, it is sufficient to filter out a lot of the resultant messiness.

nwb = h5py.File(h5_filepath)
results_folder = "./results/"
scratch_folder = "./results/"
input_movie_path = "./movie"
threshold_scaling = 0.75
# sampling_rate = nwb["acquisition/raw_suite2p_motion_corrected/imaging_plane/imaging_rate"][()]
sampling_rate = nwb["processing"]["ophys"]["ImageSegmentation"]["PlaneSegmentation"]["imaging_plane"]["imaging_rate"][()]
print("Sampling Rate:", sampling_rate)
Sampling Rate: 30.0

Running Suite2P

From there, input settings can be specified for Suite2P via the settings and db objects. The available options and their meanings are described here. The settings object holds processing parameters (like fs, tau, and threshold_scaling), while the db object holds data-location parameters (like data_path, input_format, and save_folder). Here, the sampling rate is also retrieved from the NWB file, but should probably be known information for your movies.

settings = suite2p.default_settings()
settings['fs'] = float(sampling_rate) # sampling rate of recording, determines binning for cell detection
settings['tau'] = 0.7 # timescale of gcamp to use for deconvolution
settings['detection']['threshold_scaling'] = threshold_scaling
settings['run']['do_registration'] = 0 # data was already registered
settings['classification']['classifier_path'] = classifier_path

db = suite2p.default_db()
db['save_folder'] = results_folder
db['fast_disk'] = scratch_folder
db['data_path'] = ["./"]
db['input_format'] = "h5"
db["h5py_key"] = "acquisition/motion_corrected_stack/data"

output_ops = suite2p.run_s2p(db=db, settings=settings)
c:\Users\carter.peene\Desktop\Repos\openscope_databook\.venv\Lib\site-packages\suite2p\extraction\extract.py:67: UserWarning: Sparse invariant checks are implicitly disabled. Memory errors (e.g. SEGFAULT) will occur when operating on a sparse tensor which violates the invariants, but checks incur performance overhead. To silence this warning, explicitly opt in or out. See `torch.sparse.check_sparse_tensor_invariants.__doc__` for guidance.  (Triggered internally at C:\actions-runner\_work\pytorch\pytorch\aten\src\ATen\Context.cpp:767.)
  nmasks = torch.sparse_coo_tensor(inds, torch.ones(len(row_indices), device=device),
c:\Users\carter.peene\Desktop\Repos\openscope_databook\.venv\Lib\site-packages\suite2p\extraction\extract.py:69: UserWarning: Sparse CSR tensor support is in beta state. If you miss a functionality in the sparse tensor support, please submit a feature request to https://github.com/pytorch/pytorch/issues. (Triggered internally at C:\actions-runner\_work\pytorch\pytorch\aten\src\ATen\SparseCsrTensorImpl.cpp:51.)
  nmasks = nmasks.to_sparse_csc()

Suite2P Output

Descriptions of the output of Suite2P can be found here. Below, it is shown how to access each output file. Three files, F.npy, Fneu.npy, and spks.npy are 2D arrays containing various forms of the trace data that have shape # ROIs * # Frames. The settings and intermediate values are stored as dictionaries in settings.npy and db.npy. iscell.py stores a table which, for each ROI, contains 1/0 if an ROI is a cell, and the certainty value of the classifier. Most importantly, the main statistics of each ROI are included in stat.npy. This is processed and shown as a dataframe below. Because the ROI location is stored as xpix, and ypix, which are arrays of coordinates, the code below produces an ROI submask for each ROI. The ROI submasks are more convenient for some purposes.

Traces

fluorescence = np.load("./results/plane0/F.npy")
neuropil = np.load("./results/plane0/Fneu.npy")
spikes = np.load("./results/plane0/spks.npy")

print(fluorescence.shape)
print(neuropil.shape)
print(spikes.shape)
(1090, 126741)
(1090, 126741)
(1090, 126741)
plt.plot(fluorescence[0])
<Figure size 640x480 with 1 Axes>

Options and Intermediate Outputs

db = np.load("./results/plane0/db.npy", allow_pickle=True).item()
db.keys()
dict_keys(['data_path', 'look_one_level_down', 'input_format', 'keep_movie_raw', 'nplanes', 'nrois', 'nchannels', 'swap_order', 'functional_chan', 'lines', 'dy', 'dx', 'ignore_flyback', 'subfolders', 'file_list', 'save_path0', 'fast_disk', 'save_folder', 'h5py_key', 'nwb_driver', 'nwb_series', 'force_sktiff', 'bruker_bidirectional', 'batch_size', 'first_files', 'save_path', 'settings_path', 'db_path', 'reg_file', 'iplane', 'nframes_per_folder', 'meanImg', 'nframes', 'Ly', 'Lx', 'yrange', 'xrange'])

Is-Cell Array

is_cell = np.load("./results/plane0/iscell.npy")
print(is_cell[:10])
[[1.         0.9823175 ]
 [1.         0.96954141]
 [1.         0.97251299]
 [1.         0.93598647]
 [1.         0.96457501]
 [1.         0.93742346]
 [1.         0.97842344]
 [1.         0.83166894]
 [1.         0.93664665]
 [1.         0.7722831 ]]

ROI Statistics

roi_stats = np.load("./results/plane0/stat.npy", allow_pickle=True)
stats_dict = {stat : [roi[stat] for roi in roi_stats] for stat in roi_stats[0].keys()}
def convert_pix_to_mask(xpix, ypix):
    x_loc = min(xpix)
    y_loc = min(ypix)
    rel_xpix = xpix - x_loc
    rel_ypix = ypix - y_loc
    width = max(xpix) - x_loc + 1
    height = max(ypix) - y_loc + 1
    
    mask = np.zeros((height, width))
    for y,x in zip(rel_ypix, rel_xpix):
        mask[y,x] = 1
    return mask
masks_col = []
for xpix, ypix in zip(stats_dict["xpix"], stats_dict["ypix"]):
    roi_mask = convert_pix_to_mask(xpix, ypix)
    masks_col.append(roi_mask)

# unionize dicts to ensure masks column goes first
stats_dict = {"mask": masks_col} | stats_dict
stats_df = pd.DataFrame(data=stats_dict)
print(stats_df.columns)
stats_df
Index(['mask', 'ypix', 'xpix', 'lam', 'med', 'footprint', 'npix', 'soma_crop',
       'npix_soma', 'mrs', 'mrs0', 'compact', 'radius', 'aspect_ratio',
       'npix_norm', 'npix_norm_no_crop', 'overlap', 'snr', 'skew', 'std'],
      dtype='object')
Loading...

Comparing Segmentation Output

Below we can compare multiple views of the Segmentation Output. Firstly is the main output which contains cells and non-cells. Secondly is the filtered version which is filtered to only include the ROIs which are classified as cells. The fields Lx and Ly from db.npy contain the shape of the movie, and ypix and xpix contain the pixel coordinates at which each ROI was identified. The code below iterates over each ROI and displays all xpix and ypix for that ROI into the image. These are used to generate the image of all ROI masks. The iscell.npy array is used to show only the ROIs that are cells for the filtered plot. The last plot is the segmentation from the original NWB file from the Allen Institute’s own proprietary segmentation algorithm for comparison.

Segmentation from Suite2P

im = np.zeros((db["Ly"], db["Lx"]))
for n in range(len(roi_stats)):
    ypix = roi_stats[n]["ypix"]
    xpix = roi_stats[n]["xpix"]
    im[ypix,xpix] = 1

plt.imshow(im)
<Figure size 640x480 with 1 Axes>

Segmentation from Suite2P Filtered to Cells

im = np.zeros((db["Ly"], db["Lx"]))
for i in range(len(roi_stats)):
    # only show if ROI is a cell according to classifier output
    if is_cell[i][0] == 1.0:
        ypix = roi_stats[i]["ypix"]
        xpix = roi_stats[i]["xpix"]
        im[ypix,xpix] = 1

plt.imshow(im)
<Figure size 640x480 with 1 Axes>

Segmentation From Original NWB

masks = nwb["processing/ophys/ImageSegmentation/PlaneSegmentation/image_mask"]
plt.imshow(np.logical_or.reduce(masks))
<Figure size 640x480 with 1 Axes>
References
  1. Pachitariu, M., Stringer, C., Dipoppa, M., Schröder, S., Rossi, L. F., Dalgleish, H., Carandini, M., & Harris, K. D. (2017). Suite2p: beyond 10,000 neurons with standard two-photon microscopy. bioRxiv. 10.1101/061507