catalystneuro / ndx-holographic-stimulation

The extension of OptogeneticSeries to include 3D geometrical stimulation pattern
BSD 3-Clause "New" or "Revised" License
0 stars 0 forks source link

ndx-holographic-stimulation Extension for NWB

extension schema

Installation

To install the extension, first clone the ndx_holographic_stimulation repository to the desired folder using the command

git clone https://github.com/catalystneuro/ndx-holographic-stimulation.git

Then, to install the requisite python packages and extension, run:

python -m pip install -r requirements.txt -r requirements-dev.txt
python setup.py install

The extension can then be imported into python scripts via import ndx_holographic_stimulation.

Usage


import datetime
import numpy as np
from pynwb import NWBFile, NWBHDF5IO
from ndx_holographic_stimulation import (
    PatternedOptogeneticSeries,
    PatternedOptogeneticStimulusSite,
    OptogeneticStimulusPattern,
    SpiralScanning,
    TemporalFocusing,
    SpatialLightModulator,
    LightSource,
)
from pynwb.ophys import PlaneSegmentation, ImageSegmentation, OpticalChannel

nwbfile = NWBFile(
    session_description="session_description",
    identifier="identifier",
    session_start_time=datetime.datetime.now(datetime.timezone.utc),
)

We need to define a roi_table_region in the imaging acquisition system, to link the holographic stimulus to a set of ROIs,

recording_device = nwbfile.create_device(
    name="2P_microscope",
    description="My two-photon microscope",
    manufacturer="The best microscope manufacturer",
)

optical_channel = OpticalChannel(
    name="OpticalChannel",
    description="an optical channel",
    emission_lambda=500.0,
)
imaging_plane = nwbfile.create_imaging_plane(
    name="ImagingPlane",
    optical_channel=optical_channel,
    imaging_rate=30.0,
    description="a very interesting part of the brain",
    device=recording_device,
    excitation_lambda=600.0,
    indicator="GFP",
    location="V1",
    grid_spacing=[0.01, 0.01],
    grid_spacing_unit="meters",
    origin_coords=[1.0, 2.0, 3.0],
    origin_coords_unit="meters",
)

n_rois = 2
plane_segmentation = PlaneSegmentation(
    name="PlaneSegmentation",
    description="output from segmenting my favorite imaging plane",
    imaging_plane=imaging_plane,
)
for _ in range(n_rois):
    plane_segmentation.add_roi(image_mask=np.zeros((10, 10)))

if nwbfile is not None:
    if "ophys" not in nwbfile.processing:
        nwbfile.create_processing_module("ophys", "ophys")
    nwbfile.processing["ophys"].add(plane_segmentation)

roi_table_region = plane_segmentation.create_roi_table_region(
    region=[0, 1], description="the first of two ROIs"
)

Define optical device that provide the stimulus

# metadata for spiatial light modulator
slm_name = "spatial_light_modulator"
slm_description = "Generic description for the slm"
slm_model = "model"
slm_resolution = 1.0

spatial_light_modulator = SpatialLightModulator(
    name=slm_name,
    description=slm_description,
    model=slm_model,
    resolution=slm_resolution,
)
nwbfile.add_device(spatial_light_modulator)

# metadata for the light source
light_source_name = "light_source"
light_source_description = "Generic description for the laser"
light_source_manifacturer = "manifacturer"
stimulation_wavelength = 600.0
filter_description = "600/50"
power = 8.0
intensity = 100.0
exposure_time = 1e-6
pulse_rate = 1 / exposure_time

light_source = LightSource(
    name=light_source_name,
    stimulation_wavelength=stimulation_wavelength,  # nm
    description=light_source_description,
    filter_description=filter_description,
    peak_power=power,
    intensity=intensity,
    exposure_time=exposure_time,
    pulse_rate=pulse_rate,
)
nwbfile.add_device(light_source)

# metadata for the stimulating device
stimulating_device = nwbfile.create_device(
    name="device",
    description="Microsope used for optogenetic stimulation",
)

Define the stimulus pattern and pattern table:

# metadata for  stimulus pattern
pattern_name = "generic_stimulus_pattern"
pattern_description = "beam pattern"
duration = 10e-3
number_of_stimulus_presentation = 10
inter_stimulus_interval = 0.02
# metadata for spiral scanning pattern
spiral_scanning_name = "spiral_scanning"
spiral_diameter = 15e-6
spiral_height = 10e-6
num_revolutions = 5
# metadata for temporal focusing pattern
temporal_focusing_name = "temporal_focusing"
lateral_psf = "9e-6 m ± 0.7e-6 m"
axial_psf = "32e-6 m ± 1.6e-6 m"

spiral_scanning = SpiralScanning(
    name=spiral_scanning_name,
    diameter=spiral_diameter,
    height=spiral_height,
    number_of_revolutions=num_revolutions,
    description=pattern_description,
    duration=duration,
    number_of_stimulus_presentation=number_of_stimulus_presentation,
    inter_stimulus_interval=inter_stimulus_interval,
)

nwbfile.add_lab_meta_data(spiral_scanning)

temporal_focusing = TemporalFocusing(
    name=temporal_focusing_name,
    description=pattern_description,
    lateral_point_spread_function=lateral_psf,
    axial_point_spread_function=axial_psf,
    duration=duration,
    number_of_stimulus_presentation=number_of_stimulus_presentation,
    inter_stimulus_interval=inter_stimulus_interval,
)

nwbfile.add_lab_meta_data(temporal_focusing)

generic_pattern = OptogeneticStimulusPattern(
    name=pattern_name,
    description=pattern_description,
    duration=duration,
    number_of_stimulus_presentation=number_of_stimulus_presentation,
    inter_stimulus_interval=inter_stimulus_interval,
)

nwbfile.add_lab_meta_data(generic_pattern)

Define the stimulus site: specify the effector and the stimulus pattern used

# metadata for Patterned stimulus site
site_name = "site"
site_description = "This is an example Patterned site."
effector = "ChR2"
location = "VISrl"

stim_site = PatternedOptogeneticStimulusSite(
    name=site_name,
    device=stimulating_device,
    description=site_description,
    excitation_lambda=stimulation_wavelength,  # nm
    effector=effector,
    location=location,
)
nwbfile.add_ogen_site(stim_site)

Define the stimulus sequence on the ROIs previously defined in the imaging frame coordinates

# metadata for Patterned series
series_name = "PatternedOptogeneticSeries"
series_description = "Patterned stimulus on 2 rois"
unit = "watts"
data = np.random.rand(100, n_rois)  # ntime x nroi
timestamps = np.linspace(0, 10, num=100)  # a timestamp for every frame

photostimulation = PatternedOptogeneticSeries(
    name=series_name,
    description=series_description,
    data=data,
    unit=unit,
    timestamps=timestamps,
    rois=roi_table_region,
    stimulus_pattern=generic_pattern
    site=stim_site,
    device=stimulating_device,
    light_source=light_source,
    spatial_light_modulator=spatial_light_modulator,
)

nwbfile.add_stimulus(photostimulation)

Write and read the NWBFile


nwbfile_path = "test_photostimulation_nwb.nwb"
with NWBHDF5IO(nwbfile_path, mode="w") as io:
    io.write(nwbfile)

with NWBHDF5IO(nwbfile_path, mode="r") as io:
    nwbfile_in = io.read()

Running tests

Unit and integration tests are implemented using pytest, and can be run via the command pytest from the root of the extension directory (i.e., inside ndx-holographi-stimulation/src).

Documentation

Specification

Documentation for the extension's specification, which is based on the YAML files, is generated and stored in the ./docs folder. To create it, run the following from the home directory:

cd docs
make fulldoc

This will save documentation to the ./docs/build folder, and can be accessed via the ./docs/build/html/index.html file.

This extension was created using ndx-template.