Skip to content

Commit

Permalink
Merge branch 'master' into improve-intro-tutorial
Browse files Browse the repository at this point in the history
  • Loading branch information
bendichter authored Nov 25, 2024
2 parents 508734b + da75e95 commit 98d6d65
Show file tree
Hide file tree
Showing 7 changed files with 336 additions and 94 deletions.
2 changes: 1 addition & 1 deletion +tests/+util/getPythonPath.m
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
function pythonPath = getPythonPath()
envPath = fullfile('+tests', 'env.mat');

if isfile(envPath)
if isfile(fullfile(misc.getMatnwbDir, envPath))
Env = load(envPath, '-mat');
if isfield(Env, 'pythonPath')
pythonPath = Env.pythonPath;
Expand Down
85 changes: 67 additions & 18 deletions tutorials/html/icephys.html

Large diffs are not rendered by default.

176 changes: 123 additions & 53 deletions tutorials/html/ophys.html

Large diffs are not rendered by default.

Binary file modified tutorials/icephys.mlx
Binary file not shown.
Binary file modified tutorials/ophys.mlx
Binary file not shown.
53 changes: 51 additions & 2 deletions tutorials/private/mcode/icephys.m
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@
% * <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/IZeroClampSeries.html
% |*IZeroClampSeries*|>
%%
% Below we create a simple example stimulus/response recording data pair.
% Below we create a simple example stimulus/response recording data pair for
% a voltage clamp recording.

ccss = types.core.VoltageClampStimulusSeries( ...
'data', [1, 2, 3, 4, 5], ...
Expand All @@ -80,7 +81,6 @@
'sweep_number', uint64(15), ...
'stimulus_description', 'N/A' ...
);

nwbfile.stimulus_presentation.set('ccss', ccss);

vcs = types.core.VoltageClampSeries( ...
Expand All @@ -97,6 +97,55 @@
'sweep_number', uint64(15) ...
);
nwbfile.acquisition.set('vcs', vcs);
%%
% You can add stimulus/response recording data pair from a current clamp recording
% in the same way:

% Create a CurrentClampStimulusSeries object
ccss = types.core.CurrentClampStimulusSeries(...
'data', [1, 2, 3, 4, 5], ...
'starting_time', 123.6, ...
'starting_time_rate', 10e3, ...
'electrode', types.untyped.SoftLink(electrode), ...
'gain', 0.02, ...
'sweep_number', uint16(16), ...
'stimulus_description', 'N/A' ...
);
nwbfile.stimulus_presentation.set('ccss', ccss);

% Create a CurrentClampSeries object
ccs = types.core.CurrentClampSeries(...
'data', [0.1, 0.2, 0.3, 0.4, 0.5], ...
'data_conversion', 1e-12, ...
'data_resolution', NaN, ...
'starting_time', 123.6, ...
'starting_time_rate', 20e3, ...
'electrode', types.untyped.SoftLink(electrode), ...
'gain', 0.02, ...
'bias_current', 1e-12, ...
'bridge_balance', 70e6, ...
'capacitance_compensation', 1e-12, ...
'stimulus_description', 'N/A', ...
'sweep_number', uint16(16) ...
);
nwbfile.acquisition.set('ccs', ccs);

%%
% <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/IZeroClampSeries.html
% |*IZeroClampSeries*|> is used when the current is clamped to 0.

% Create an IZeroClampSeries object
izcs = types.core.IZeroClampSeries(...
'data', [0.1, 0.2, 0.3, 0.4, 0.5], ...
'electrode', types.untyped.SoftLink(electrode), ...
'gain', 0.02, ...
'data_conversion', 1e-12, ...
'data_resolution', NaN, ...
'starting_time', 345.6, ...
'starting_time_rate', 20e3, ...
'sweep_number', uint16(17) ...
);
nwbfile.acquisition.set('izcs', izcs);
% Adding an intracellular recording
% The <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/IntracellularRecordingsTable.html
% |*IntracellularRecordingsTable*|> relates electrode, stimulus and response pairs
Expand Down
114 changes: 94 additions & 20 deletions tutorials/private/mcode/ophys.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@
% Introduction to MatNWB tutorial>, which demonstrates installing MatNWB and creating
% an NWB file with subject information, animal position, and trials, as well as
% writing and reading NWB files in MATLAB.
%
% *Please note*: The dimensions of timeseries data in MatNWB should be defined
% in the opposite order of how it is defined in the nwb-schemas. In NWB, time
% is always stored in the first dimension of the data, whereas in MatNWB data
% should be specified with time along the last dimension. This is explained in
% more detail here: <https://neurodatawithoutborders.github.io/matnwb/tutorials/html/dimensionMapNoDataPipes.html
% MatNWB <-> HDF5 Dimension Mapping>.
%% Set up the NWB file
% An NWB file represents a single session of an experiment. Each file must have
% a session_description, identifier, and session start time. Create a new |NWBFile|
Expand Down Expand Up @@ -88,13 +95,64 @@

% using internal data. this data will be stored inside the NWB file
InternalOnePhoton = types.core.OnePhotonSeries( ...
'data', ones(100, 100, 1000), ...
'data', ones(100, 100, 1000), ...
'imaging_plane', types.untyped.SoftLink(imaging_plane), ...
'starting_time', 0., ...
'starting_time_rate', 1.0, ...
'data_unit', 'normalized amplitude' ...
);
nwb.acquisition.set('1pInternal', InternalOnePhoton);
% Motion Correction (optional)
% You can also store the result of motion correction using a <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/MotionCorrection.html
% *MotionCorrection*> object, a container type that can hold one or more <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/CorrectedImageStack.html
% *CorrectedImageStack*> objects.

% Create the corrected ImageSeries
corrected = types.core.ImageSeries( ...
'description', 'A motion corrected image stack', ...
'data', ones(100, 100, 1000), ... % 3D data array
'data_unit', 'n/a', ...
'format', 'raw', ...
'starting_time', 0.0, ...
'starting_time_rate', 1.0 ...
);

% Create the xy_translation TimeSeries
xy_translation = types.core.TimeSeries( ...
'description', 'x,y translation in pixels', ...
'data', ones(2, 1000), ... % 2D data array
'data_unit', 'pixels', ...
'starting_time', 0.0, ...
'starting_time_rate', 1.0 ...
);

% Create the CorrectedImageStack
corrected_image_stack = types.core.CorrectedImageStack( ...
'corrected', corrected, ...
'original', types.untyped.SoftLink(InternalOnePhoton), ... % Ensure `InternalOnePhoton` exists
'xy_translation', xy_translation ...
);

% Create the MotionCorrection object
motion_correction = types.core.MotionCorrection();
motion_correction.correctedimagestack.set('CorrectedImageStack', corrected_image_stack);
%%
% The motion corrected data is considered processed data and will be added to
% the |processing| field of the |nwb| object using a <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/ProcessingModule.html
% |*ProcessingModule*|> called "|ophys|". First, create the <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/ProcessingModule.html
% |*ProcessingModule*|> object and then add the |motion_correction| object to
% it, naming it "|MotionCorrection|".

ophys_module = types.core.ProcessingModule( ...
'description', 'Contains optical physiology data');
ophys_module.nwbdatainterface.set('MotionCorrection', motion_correction);
%%
% Finally, add the "ophys" <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/ProcessingModule.html
% |*ProcessingModule*|> to the |nwb| (Note that we can continue adding objects
% to the "ophys" <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/ProcessingModule.html
% |*ProcessingModule*|> without needing to explicitly update the nwb):

nwb.processing.set('ophys', ophys_module);
% Plane Segmentation
% Image segmentation stores the detected regions of interest in the <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/TwoPhotonSeries.html
% |*TwoPhotonSeries*|> data. <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/ImageSegmentation.html
Expand Down Expand Up @@ -181,53 +239,69 @@
% Adding ROIs to NWB file
% Now create an <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/ImageSegmentation.html
% |*ImageSegmentation*|> object and put the |plane_segmentation| object inside
% of it, naming it <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/PlaneSegmentation.html
% |*PlaneSegmentation*|>.
% of it, naming it "|PlaneSegmentation"|.

img_seg = types.core.ImageSegmentation();
img_seg.planesegmentation.set('PlaneSegmentation', plane_segmentation);
%%
% Now create a <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/ProcessingModule.html
% |*ProcessingModule*|> called "ophys" and put our |img_seg| object in it, calling
% it "|ImageSegmentation"|, and add the <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/ProcessingModule.html
% |*ProcessingModule*|> to |nwb.|
% Add the |img_seg| object to the "ophys" <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/ProcessingModule.html
% |*ProcessingModule*|> we created before, naming it "|ImageSegmentation|"|.|

ophys_module = types.core.ProcessingModule( ...
'description', 'contains optical physiology data')
ophys_module.nwbdatainterface.set('ImageSegmentation', img_seg);
nwb.processing.set('ophys', ophys_module);
% Storing fluorescence of ROIs over time
% Now that ROIs are stored, you can store fluorescence dF/F data for these regions
% Now that ROIs are stored, you can store fluorescence data for these regions
% of interest. This type of data is stored using the <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/RoiResponseSeries.html
% |*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.
% |*RoiResponseSeries*|> class.
%
%
%
% First, create a data interface to store this data in
% To create a <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/RoiResponseSeries.html
% |*RoiResponseSeries*|> object, we will need to reference a set of rows from
% the <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/PlaneSegmentation.html
% |*PlaneSegmentation*|> table to indicate which ROIs correspond to which rows
% of your recorded data matrix. This is done using a <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/DynamicTableRegion.html
% |*DynamicTableRegion*|>, which is a type of link that allows you to reference
% specific rows of a <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/DynamicTable.html
% |*DynamicTable*|>, such as a <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/PlaneSegmentation.html
% |*PlaneSegmentation*|> table by row indices.
%
% First, we create a <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/DynamicTableRegion.html
% |*DynamicTableRegion*|> that references the ROIs of the <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/PlaneSegmentation.html
% |*PlaneSegmentation*|> table.

roi_table_region = types.hdmf_common.DynamicTableRegion( ...
'table', types.untyped.ObjectView(plane_segmentation), ...
'description', 'all_rois', ...
'data', (0:n_rois-1)');
%%
% Then we create a <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/RoiResponseSeries.html
% |*RoiResponseSeries*|> object to store fluorescence data for those ROIs.

roi_response_series = types.core.RoiResponseSeries( ...
'rois', roi_table_region, ...
'data', NaN(n_rois, 100), ...
'data', NaN(n_rois, 100), ... % [nRoi, nT]
'data_unit', 'lumens', ...
'starting_time_rate', 3.0, ...
'starting_time', 0.0);
%%
% To help data analysis and visualization tools know that this <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/RoiResponseSeries.html
% |*RoiResponseSeries*|> object represents fluorescence data, we will store the
% <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/RoiResponseSeries.html
% |*RoiResponseSeries*|> object inside of a <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/Fluorescence.html
% |*Fluorescence*|> object. Then we add the <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/Fluorescence.html
% |*Fluorescence*|> object into the same <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/ProcessingModule.html
% |*ProcessingModule*|> named |"ophys"| that we created earlier.

fluorescence = types.core.Fluorescence();
fluorescence.roiresponseseries.set('RoiResponseSeries', roi_response_series);

ophys_module.nwbdatainterface.set('Fluorescence', fluorescence);
%%
% Finally, the ophys <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/ProcessingModule.html
% |*ProcessingModule*|> is added to the |NwbFile|.

nwb.processing.set('ophys', ophys_module);
% *Tip*: If you want to store dF/F data instead of fluorescence data, then store
% the <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/RoiResponseSeries.html
% |*RoiResponseSeries*|> object in a <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/DfOverF.html
% |*DfOverF*|> object, which works the same way as the <https://neurodatawithoutborders.github.io/matnwb/doc/+types/+core/Fluorescence.html
% |*Fluorescence*|> class.
%% Writing the NWB file

nwb_file_name = 'ophys_tutorial.nwb';
Expand Down

0 comments on commit 98d6d65

Please sign in to comment.