Calcium imaging data¶
This tutorial will demonstrate how to write calcium imaging data. The workflow demonstrated here involves three main steps:
- Acquiring two-photon images
- Image segmentation
- Fluorescence and dF/F response
This tutorial assumes that transforming data between these three states is done by users–PyNWB does not provide analysis functionality.
The following examples will reference variables that may not be defined within the block they are used in. For clarity, we define them here:
from datetime import datetime from dateutil.tz import tzlocal from pynwb import NWBFile from pynwb.ophys import TwoPhotonSeries, OpticalChannel, ImageSegmentation, Fluorescence from pynwb.device import Device
Creating and Writing NWB files¶
When creating a NWB file, the first step is to create the
nwbfile = NWBFile('my first synthetic recording', 'EXAMPLE_ID', datetime.now(tzlocal()), experimenter='Dr. Bilbo Baggins', lab='Bag End Laboratory', institution='University of Middle Earth at the Shire', experiment_description=('I went on an adventure with thirteen ' 'dwarves to reclaim vast treasures.'), session_id='LONELYMTN')
Adding metadata about acquisition¶
Before you can add your data, you will need to provide some information about how that data was generated. This amounts describing the device, imaging plane and the optical channel used.
device = Device('imaging_device_1') nwbfile.add_device(device) optical_channel = OpticalChannel('my_optchan', 'description', 500.) imaging_plane = nwbfile.create_imaging_plane('my_imgpln', optical_channel, description='a very interesting part of the brain', device=device, excitation_lambda=600., imaging_rate=300., indicator='GFP', location='my favorite brain location', reference_frame='A frame to refer to', grid_spacing=(.01, .01))
Adding two-photon image data¶
From here you have two options. The first option is to supply the image data to PyNWB, using the data argument. The other option is the provide a path the images. These two options have trade-offs, so it is worth spending time considering how you want to store this data .
image_series = TwoPhotonSeries(name='test_iS', dimension=, external_file=['images.tiff'], imaging_plane=imaging_plane, starting_frame=, format='tiff', starting_time=0.0, rate=1.0) nwbfile.add_acquisition(image_series)
Storing image segmentation output¶
Now that the raw data is stored, you can add the image segmentation results. This is done with the
ImageSegmentation data interface. This class has the ability to store segmentation
from one or more imaging planes; hence the
mod = nwbfile.create_processing_module('ophys', 'contains optical physiology processed data') img_seg = ImageSegmentation() mod.add(img_seg) ps = img_seg.create_plane_segmentation('output from segmenting my favorite imaging plane', imaging_plane, 'my_planeseg', image_series)
Now that you have your
PlaneSegmentation object, you can add the resulting ROIs.
This is done using the method
add_roi. The first argument to this
method is the pixel_mask and the second method is the image_mask. The NWB schema allows for either argument
to be provided.
w, h = 3, 3 pix_mask1 = [(0, 0, 1.1), (1, 1, 1.2), (2, 2, 1.3)] img_mask1 = [[0.0 for x in range(w)] for y in range(h)] img_mask1 = 1.1 img_mask1 = 1.2 img_mask1 = 1.3 ps.add_roi(pixel_mask=pix_mask1, image_mask=img_mask1) pix_mask2 = [(0, 0, 2.1), (1, 1, 2.2)] img_mask2 = [[0.0 for x in range(w)] for y in range(h)] img_mask2 = 2.1 img_mask2 = 2.2 ps.add_roi(pixel_mask=pix_mask2, image_mask=img_mask2)
Storing fluorescence measurements¶
Now that ROIs are stored, you can store fluorescence (or dF/F ) data for these regions of interest.
This type of data is stored using the
RoiResponseSeries class. You will not need
to instantiate this class directly to create objects of this type, but it is worth noting that this is the
class you will work with after you read data back in.
First, create a data interface to store this data in
fl = Fluorescence() mod.add(fl)
Because this data stores information about specific ROIs, you will need to provide a reference to the ROIs
that you will be storing data for. This is done using a
DynamicTableRegion, which can be
rt_region = ps.create_roi_table_region('the first of two ROIs', region=)
Now that you have an
DynamicTableRegion, you can create your an
data = [0., 1., 2., 3., 4., 5., 6., 7., 8., 9.] timestamps = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9] rrs = fl.create_roi_response_series('my_rrs', data, rt_region, unit='lumens', timestamps=timestamps)
from pynwb import NWBHDF5IO with NWBHDF5IO('ophys_example.nwb', 'w') as io: io.write(nwbfile)
Reading an NWBFile¶
io = NWBHDF5IO('ophys_example.nwb', 'r') nwbfile = io.read()
Getting your data out¶
After you read the NWB file, you can access individual components of your data file.
To get the
ProcessingModule back, you can index into the
processing attribute with the name of the
mod = nwbfile.processing['ophys']
Now you can retrieve the
ImageSegmentation object by indexing into the
ProcessingModule with the name of the
In our case, this is just “ImageSegmentation”, since we did not provide a name and kept the default name.
ps = mod['ImageSegmentation'].get_plane_segmentation()
Once you have the original
PlaneSegmentation object, you can retrieve your
image masks and pixel masks using standard indexing.
img_mask1 = ps['image_mask'] pix_mask1 = ps['pixel_mask'] img_mask2 = ps['image_mask'] pix_mask2 = ps['pixel_mask']
To get back the fluorescence time series data, first access the
Fluorescence object we added
(like we did above with
ImageSegmentation) and then retrieve the
rrs = mod['Fluorescence'].get_roi_response_series() # get the data... rrs_data = rrs.data rrs_timestamps = rrs.timestamps rrs_rois = rrs.rois # and now do something cool!
|||If you pass in the image data directly, you will not need to worry about distributing the image files with your NWB file. However, we recognize that common image formats have tools built around them, so working with the original file formats can make one’s life much simpler. NWB currently does not have the ability to read and parse native image formats. It is up to downstream users to read these file formats.|
|||You can also store dF/F data using the |
|||Neurodata sets can be very large, so individual components of the dataset are only loaded into memory when
you requst them. This functionality is only possible if closing of the |
|||If you added more than one |