Metadata Templates#
An interface’s get_metadata() returns only what its source file recorded, so it does not tell you
what else the NWB file needs from you. get_metadata_template() answers that second question: it
returns the same source-derived values wrapped in the full structure the writer expects, with the
cross-references between entries already resolved and every field only you can supply left blank.
The fiber photometry, optical physiology and pose estimation interfaces are the ones that offer it today. Other modalities will follow, and this page grows a section for each.
Fill in the blanks and pass the result on:
metadata = interface.get_metadata_template()
# fill in the blanks it marks, then
interface.run_conversion(nwbfile_path="my_file.nwb", metadata=metadata)
The blanks are the checklist. What comes back blank is exactly what the source could not tell us, so nothing here is a default: fill what applies and delete what does not. A required field left blank fails the conversion rather than being guessed at, and an optional entry you do not want is deleted rather than left empty, since deleting a block is what gives you a file without that object.
The blocks below are the same structures as files, for writing metadata by hand rather than in Python.
load_dict_from_file accepts .yaml, .yml and .json alike, so either format works as the
metadata block of a conversion specification, or as a file you load and merge onto
get_metadata() yourself. See Using YAML to specify metadata for that workflow.
Both tabs hold the same content, so copy whichever suits you, fill in the null values that apply
and delete the entries that do not. The YAML is annotated and the JSON is not, since JSON has no
comments. Dictionary keys are handles rather than names in the file, so rename them freely. Where a
structure repeats once per something in your recording, two entries are shown rather than one, so that
what changes between them is visible.
Fiber Photometry#
One FiberPhotometryTable row per trace the interface writes, one optical fiber per row, and a
shared excitation source, photodetector and indicator. The rows are named in the order the series’
columns are written, and fiber_photometry_table_region has to list them in that same order, so
trace_0 and trace_1 below are the first and second column of the series.
Rename calcium_signal to whatever metadata_key the interface was constructed with. The dichroic
mirror, the two optical filters and the three device models are optional, and appear so that you know
the writer accepts them at all. A filter is a BandOpticalFilter or an EdgeOpticalFilter, never a
plain one, and its wavelengths belong to its model rather than to the filter itself.
For the same chain filled in with real values, built one block at a time and explained as it goes, see How to Annotate Fiber Photometry Metadata. That how-to also covers the layouts this block does not show: one fiber recorded at a signal and an isosbestic wavelength, and several fibers in different locations.
# Every `null` is yours to fill. Delete any entry your recording did not use.
# Every key here is a handle you may rename, not a name in the file; `name` is the name in the file.
# The equipment models: the make and catalogue specification, shared by every recording on that rig.
# All three are optional. To drop one, delete it here and the `device_model_metadata_key` pointing at it.
DeviceModels:
optical_fiber_model:
type: OpticalFiberModel
name: null
manufacturer: null
numerical_aperture: null
excitation_source_model:
type: ExcitationSourceModel
name: null
manufacturer: null
source_type: null # LED, laser
excitation_mode: null # one-photon, two-photon
photodetector_model:
type: PhotodetectorModel
name: null
manufacturer: null
detector_type: null # photodiode, PMT
# The equipment itself. One optical fiber per fiber you recorded from; the source and detector are
# shared by all of them, since one interface writes one series through one light path.
Devices:
optical_fiber_0:
type: OpticalFiber
name: null
device_model_metadata_key: optical_fiber_model # a key in DeviceModels above
fiber_insertion: # where this fiber sat, stereotaxic
insertion_position_ap_in_mm: null
insertion_position_ml_in_mm: null
insertion_position_dv_in_mm: null
depth_in_mm: null
optical_fiber_1: # one entry like this per fiber
type: OpticalFiber
name: null
device_model_metadata_key: optical_fiber_model
fiber_insertion:
insertion_position_ap_in_mm: null
insertion_position_ml_in_mm: null
insertion_position_dv_in_mm: null
depth_in_mm: null
excitation_source:
type: ExcitationSource
name: null
device_model_metadata_key: excitation_source_model
photodetector:
type: Photodetector
name: null
device_model_metadata_key: photodetector_model
# Optional optics. Delete the entry and every row reference to it if the rig had none.
# These three have model types of their own too, not shown; delete the key or point it at one.
dichroic_mirror:
type: DichroicMirror
name: null
device_model_metadata_key: null
excitation_filter:
type: BandOpticalFilter # or EdgeOpticalFilter; there is no plain OpticalFilter
name: null
device_model_metadata_key: null
emission_filter:
type: BandOpticalFilter
name: null
device_model_metadata_key: null
FiberPhotometry:
# What was expressed in the tissue, and what it fluoresces as.
FiberPhotometryIndicators:
indicator:
name: null
label: null # GCaMP6s, dLight1.1, tdTomato
# One row per column of the response series, in the order the columns are written.
FiberPhotometryTable:
name: fiber_photometry_table
description: 'Each row describes one trace: the fiber, hardware and indicator that produced it.'
rows:
trace_0:
location: null # the brain region this fiber sat in
excitation_wavelength_in_nm: null
emission_wavelength_in_nm: null
# Each of these names a key above, wiring this trace to the hardware that produced it.
indicator_metadata_key: indicator
optical_fiber_metadata_key: optical_fiber_0
excitation_source_metadata_key: excitation_source
photodetector_metadata_key: photodetector
dichroic_mirror_metadata_key: dichroic_mirror # optional, delete if unused
excitation_filter_metadata_key: excitation_filter # optional, delete if unused
emission_filter_metadata_key: emission_filter
coordinates: null # (ap, ml, dv) of the recorded volume, in mm
notes: null # optional, delete if unused
trace_1: # one entry like this per trace
location: null
excitation_wavelength_in_nm: null
emission_wavelength_in_nm: null
indicator_metadata_key: indicator
optical_fiber_metadata_key: optical_fiber_1 # the only line that differs
excitation_source_metadata_key: excitation_source
photodetector_metadata_key: photodetector
dichroic_mirror_metadata_key: dichroic_mirror
excitation_filter_metadata_key: excitation_filter
emission_filter_metadata_key: emission_filter
coordinates: null # (ap, ml, dv) of the recorded volume, in mm
notes: null
# Rename this key to the `metadata_key` the interface was constructed with.
calcium_signal:
name: FiberPhotometryResponseSeries
description: null
fiber_photometry_table_region: # the rows above, in the order the series' columns are written
- trace_0
- trace_1
{
"DeviceModels": {
"optical_fiber_model": {
"type": "OpticalFiberModel",
"name": null,
"manufacturer": null,
"numerical_aperture": null
},
"excitation_source_model": {
"type": "ExcitationSourceModel",
"name": null,
"manufacturer": null,
"source_type": null,
"excitation_mode": null
},
"photodetector_model": {
"type": "PhotodetectorModel",
"name": null,
"manufacturer": null,
"detector_type": null
}
},
"Devices": {
"optical_fiber_0": {
"type": "OpticalFiber",
"name": null,
"device_model_metadata_key": "optical_fiber_model",
"fiber_insertion": {
"insertion_position_ap_in_mm": null,
"insertion_position_ml_in_mm": null,
"insertion_position_dv_in_mm": null,
"depth_in_mm": null
}
},
"optical_fiber_1": {
"type": "OpticalFiber",
"name": null,
"device_model_metadata_key": "optical_fiber_model",
"fiber_insertion": {
"insertion_position_ap_in_mm": null,
"insertion_position_ml_in_mm": null,
"insertion_position_dv_in_mm": null,
"depth_in_mm": null
}
},
"excitation_source": {
"type": "ExcitationSource",
"name": null,
"device_model_metadata_key": "excitation_source_model"
},
"photodetector": {
"type": "Photodetector",
"name": null,
"device_model_metadata_key": "photodetector_model"
},
"dichroic_mirror": {
"type": "DichroicMirror",
"name": null,
"device_model_metadata_key": null
},
"excitation_filter": {
"type": "BandOpticalFilter",
"name": null,
"device_model_metadata_key": null
},
"emission_filter": {
"type": "BandOpticalFilter",
"name": null,
"device_model_metadata_key": null
}
},
"FiberPhotometry": {
"FiberPhotometryIndicators": {
"indicator": {
"name": null,
"label": null
}
},
"FiberPhotometryTable": {
"name": "fiber_photometry_table",
"description": "Each row describes one trace: the fiber, hardware and indicator that produced it.",
"rows": {
"trace_0": {
"location": null,
"excitation_wavelength_in_nm": null,
"emission_wavelength_in_nm": null,
"indicator_metadata_key": "indicator",
"optical_fiber_metadata_key": "optical_fiber_0",
"excitation_source_metadata_key": "excitation_source",
"photodetector_metadata_key": "photodetector",
"dichroic_mirror_metadata_key": "dichroic_mirror",
"excitation_filter_metadata_key": "excitation_filter",
"emission_filter_metadata_key": "emission_filter",
"coordinates": null,
"notes": null
},
"trace_1": {
"location": null,
"excitation_wavelength_in_nm": null,
"emission_wavelength_in_nm": null,
"indicator_metadata_key": "indicator",
"optical_fiber_metadata_key": "optical_fiber_1",
"excitation_source_metadata_key": "excitation_source",
"photodetector_metadata_key": "photodetector",
"dichroic_mirror_metadata_key": "dichroic_mirror",
"excitation_filter_metadata_key": "excitation_filter",
"emission_filter_metadata_key": "emission_filter",
"coordinates": null,
"notes": null
}
}
},
"calcium_signal": {
"name": "FiberPhotometryResponseSeries",
"description": null,
"fiber_photometry_table_region": [
"trace_0",
"trace_1"
]
}
}
}
Optical Physiology#
An imaging interface and a segmentation interface each get their own block below, and the two overlap:
both describe an imaging plane and the microscope behind it, because a segmentation is ROIs drawn on a
plane that was imaged. In a conversion that has both, keep one copy of the Devices and
ImagingPlanes entries and point the series and the segmentation at it, rather than writing the
plane twice.
For either block filled in with real values, including that shared-plane case and several segmentation pipelines on one recording, see How to Annotate Optical Physiology Data.
Imaging#
One imaging plane and one series, cross-referenced by imaging_plane_metadata_key. Rename
calcium_imaging to whatever metadata_key the interface was constructed with, in both blocks.
A multi-plane or multi-channel acquisition is several interfaces rather than several entries here, so
each one brings its own key; the optical_channel list is the exception, and holds one entry per
channel the plane was imaged in.
The last three fields of the series are what a two-photon acquisition describes. A one-photon one takes
exposure_time, binning, power and intensity in their place, and
get_metadata_template() offers whichever of the two sets matches the series being written.
# Every `null` is yours to fill. Delete any entry your recording did not use.
# Every key here is a handle you may rename, not a name in the file; `name` is the name in the file.
# The make and catalog specification of the microscope, shared by every recording on that rig.
# Optional. To drop it, delete it here and the `device_model_metadata_key` pointing at it below.
DeviceModels:
microscope_model:
name: null
manufacturer: null
model_number: null
description: null
# The microscope everything below hangs off. An interface that read one out of the source names it
# already, and then the key to fill in is the one it chose rather than `microscope`.
Devices:
microscope:
name: null
description: null
serial_number: null # of this instrument, not of the model
device_model_metadata_key: microscope_model # a key in DeviceModels above
Ophys:
# Where and how the imaging was done. One plane per interface: a multi-plane or multi-channel
# acquisition is several interfaces, each with its own key.
ImagingPlanes:
calcium_imaging:
name: null
description: null
device_metadata_key: microscope # a key in Devices above
excitation_lambda: null # in nm
indicator: null # GCaMP6s, jRGECO1a
location: null # the brain region imaged
imaging_rate: null # in Hz
optical_channel: # one entry like this per channel the plane was imaged in
- name: null
description: null
emission_lambda: null # in nm
- name: null
description: null
emission_lambda: null
# Where the plane sat in the sample, and what those coordinates are measured from. Optional;
# delete what you cannot answer. The units are stated because NWB stores both in meters.
origin_coords: null # (x, y) or (x, y, z) of the first pixel
origin_coords_unit: meters
grid_spacing: null # (x, y) or (x, y, z) distance between pixel centers
grid_spacing_unit: meters
reference_frame: null
# The imaging data itself. Rename this key to the `metadata_key` the interface was constructed with,
# here and in ImagingPlanes above.
MicroscopySeries:
calcium_imaging:
name: null
description: null
unit: null # n.a. unless the data carries physical units
imaging_plane_metadata_key: calcium_imaging # a key in ImagingPlanes above
# Optional, and what a two-photon acquisition describes. A one-photon one describes its camera
# instead, with `exposure_time`, `binning`, `power` and `intensity`.
field_of_view: null
pmt_gain: null
scan_line_rate: null
{
"DeviceModels": {
"microscope_model": {
"name": null,
"manufacturer": null,
"model_number": null,
"description": null
}
},
"Devices": {
"microscope": {
"name": null,
"description": null,
"serial_number": null,
"device_model_metadata_key": "microscope_model"
}
},
"Ophys": {
"ImagingPlanes": {
"calcium_imaging": {
"name": null,
"description": null,
"device_metadata_key": "microscope",
"excitation_lambda": null,
"indicator": null,
"location": null,
"imaging_rate": null,
"optical_channel": [
{
"name": null,
"description": null,
"emission_lambda": null
},
{
"name": null,
"description": null,
"emission_lambda": null
}
],
"origin_coords": null,
"origin_coords_unit": "meters",
"grid_spacing": null,
"grid_spacing_unit": "meters",
"reference_frame": null
}
},
"MicroscopySeries": {
"calcium_imaging": {
"name": null,
"description": null,
"unit": null,
"imaging_plane_metadata_key": "calcium_imaging",
"field_of_view": null,
"pmt_gain": null,
"scan_line_rate": null
}
}
}
}
Segmentation#
The plane segmentation, its traces and its summary images all sit under one key, because the writer
resolves all three through the interface’s metadata_key; rename calcium_segmentation in all four
blocks together.
A metadata_key is a handle you use to reference one block from another. calcium_segmentation and
microscope are never written to the file, and their whole role is to be pointed at, so rename them
freely as long as everything pointing at them is renamed with them.
The inner keys of RoiResponses and SegmentationImages do not follow that model. They are the
names of the traces and images this segmentation produced, they cannot belong to any other entry, and
nothing points at them: the writer matches them against the arrays it is about to write. So raw,
dff, neuropil, deconvolved, denoised and baseline for traces, and mean and
correlation for images, are roiextractors’ names rather than yours. Rename dff and it matches
nothing: the trace is not written, and you get a warning saying so.
What you choose is the name inside each entry. dff says which trace you are describing and
name: DfOverF says what it is called in the file. Delete the ones your pipeline did not produce,
and note that get_metadata_template() reads the same two extractor methods, so it offers only the
traces and images the file actually holds.
# Every `null` is yours to fill. Delete any entry your recording did not use.
# Every key here is a handle you may rename, not a name in the file; `name` is the name in the file.
# The one exception is the trace and image names under RoiResponses and SegmentationImages, which are
# roiextractors' own vocabulary and are noted where they appear.
# The make and catalog specification of the microscope, shared by every recording on that rig.
# Optional. To drop it, delete it here and the `device_model_metadata_key` pointing at it below.
DeviceModels:
microscope_model:
name: null
manufacturer: null
model_number: null
description: null
# The microscope everything below hangs off. An interface that read one out of the source names it
# already, and then the key to fill in is the one it chose rather than `microscope`.
Devices:
microscope:
name: null
description: null
serial_number: null # of this instrument, not of the model
device_model_metadata_key: microscope_model # a key in DeviceModels above
Ophys:
# The plane the ROIs were segmented on. A segmentation file usually says nothing about the optics,
# so this whole block is yours; it describes the imaging the pipeline ran on, not the pipeline.
ImagingPlanes:
calcium_segmentation:
name: null
description: null
device_metadata_key: microscope # a key in Devices above
excitation_lambda: null # in nm
indicator: null # GCaMP6s, jRGECO1a
location: null # the brain region imaged
imaging_rate: null # in Hz
optical_channel: # one entry like this per channel the plane was imaged in
- name: null
description: null
emission_lambda: null # in nm
- name: null
description: null
emission_lambda: null
# Where the plane sat in the sample, and what those coordinates are measured from. Optional;
# delete what you cannot answer. The units are stated because NWB stores both in meters.
origin_coords: null # (x, y) or (x, y, z) of the first pixel
origin_coords_unit: meters
grid_spacing: null # (x, y) or (x, y, z) distance between pixel centers
grid_spacing_unit: meters
reference_frame: null
# The ROIs. Rename this key to the `metadata_key` the interface was constructed with, and rename it
# in the three blocks below too: the writer resolves all four through the one key.
PlaneSegmentations:
calcium_segmentation:
name: null
description: null
imaging_plane_metadata_key: calcium_segmentation # a key in ImagingPlanes above
# One entry per trace the pipeline produced. `raw`, `dff`, `neuropil`, `deconvolved`, `denoised` and
# `baseline` are the names roiextractors reads traces under, so these inner keys are the one set here
# you cannot rename. Delete the ones your pipeline did not produce: a trace the file does not hold
# writes nothing and warns.
RoiResponses:
calcium_segmentation:
raw:
name: null
description: null
unit: null # n.a. unless the traces carry physical units
dff:
name: null
description: null
unit: null
neuropil:
name: null
description: null
unit: null
deconvolved:
name: null
description: null
unit: null
# The summary images, keyed the same way: `mean` and `correlation` are roiextractors' names for them.
SegmentationImages:
calcium_segmentation:
mean:
name: null
description: null
correlation:
name: null
description: null
{
"DeviceModels": {
"microscope_model": {
"name": null,
"manufacturer": null,
"model_number": null,
"description": null
}
},
"Devices": {
"microscope": {
"name": null,
"description": null,
"serial_number": null,
"device_model_metadata_key": "microscope_model"
}
},
"Ophys": {
"ImagingPlanes": {
"calcium_segmentation": {
"name": null,
"description": null,
"device_metadata_key": "microscope",
"excitation_lambda": null,
"indicator": null,
"location": null,
"imaging_rate": null,
"optical_channel": [
{
"name": null,
"description": null,
"emission_lambda": null
},
{
"name": null,
"description": null,
"emission_lambda": null
}
],
"origin_coords": null,
"origin_coords_unit": "meters",
"grid_spacing": null,
"grid_spacing_unit": "meters",
"reference_frame": null
}
},
"PlaneSegmentations": {
"calcium_segmentation": {
"name": null,
"description": null,
"imaging_plane_metadata_key": "calcium_segmentation"
}
},
"RoiResponses": {
"calcium_segmentation": {
"raw": {
"name": null,
"description": null,
"unit": null
},
"dff": {
"name": null,
"description": null,
"unit": null
},
"neuropil": {
"name": null,
"description": null,
"unit": null
},
"deconvolved": {
"name": null,
"description": null,
"unit": null
}
}
},
"SegmentationImages": {
"calcium_segmentation": {
"mean": {
"name": null,
"description": null
},
"correlation": {
"name": null,
"description": null
}
}
}
}
}
Pose Estimation#
One PoseEstimation container and the Skeleton naming its body parts, cross-referenced by
skeleton_metadata_key, plus the camera they hang off. Rename pose_estimation to whatever
metadata_key the interface was constructed with, in both blocks.
A pose file records coordinates and confidences and almost nothing else, so this block is blanker than
the others: what the coordinates are measured from, what unit they are in, what the confidence value
means and which body parts are joined are all yours to state. nodes is the exception, since the
tracker named the body parts, and its order is what edges indexes into.
One container is one camera view of one subject. Several animals in a recording is one file each rather than several containers, and several views of one animal is one interface each, so a second view brings its own key here and normally points at the same skeleton.
For the same structure filled in with real values, built one block at a time, see How to Annotate Pose Estimation Metadata.
# Every `null` is yours to fill. Delete any entry your recording did not use.
# Every key here is a handle you may rename, not a name in the file; `name` is the name in the file.
# The make and catalog specification of the camera, shared by every recording on that rig.
# Optional. To drop it, delete it here and the `device_model_metadata_key` pointing at it below.
DeviceModels:
camera_model:
name: null
manufacturer: null
model_number: null
description: null
# The camera that filmed the frames the tracker ran on. No pose format records one, so nothing is
# written unless you say so. Optional as a whole: if a video interface writes the recording into the
# same file, link it with `source_video_metadata_key` below instead, since that `ImageSeries` carries
# its own camera. To drop it, delete it here and the `device_metadata_key` pointing at it.
Devices:
camera:
name: null
description: null
serial_number: null # of this camera, not of the model
device_model_metadata_key: camera_model # a key in DeviceModels above
Pose:
# Which body parts exist and which are joined. `nodes` is the one field a pose file answers, and the
# tracker's order is what `edges` indexes into, so do not reorder it.
Skeletons:
pose_estimation:
name: null
nodes: # the tracker's body parts, in the order its series are written
- head
- neck
- left_shoulder
edges: # pairs of indices into `nodes` above: [0, 1] joins head to neck, and
# [1, 2] joins neck to left_shoulder
- [0, 1]
- [1, 2]
subject: null # the individual within the source; blank links to the file's own subject
# The keypoints themselves. Rename this key to the `metadata_key` the interface was constructed
# with, here and in Skeletons above.
PoseEstimations:
pose_estimation:
name: null
description: null
source_software: null # DeepLabCut, SLEAP, Lightning Pose
source_software_version: null
scorer: null # the trained model that produced these coordinates
skeleton_metadata_key: pose_estimation # a key in Skeletons above
device_metadata_key: camera # a key in Devices above
# The video the tracker ran on, when a video interface wrote it into this same file. These
# address `metadata["Behavior"]["ExternalVideos"]`, and the link is a reference to the object
# rather than a path, which is what ndx-pose is standardising on.
source_video_metadata_key: null
labeled_video_metadata_key: null
# One entry per body part, keyed by the tracker's name for it. These keys are matched against
# the keypoints being written, so renaming one means its series is not described.
PoseEstimationSeries:
head:
name: null
description: null
unit: null # pixels, or mm once the coordinates are calibrated
# Required by ndx-pose, and the field worth filling above all others: left blank, the file
# says "(0,0) is unknown." about every coordinate in it.
reference_frame: null # e.g. (0,0) is the top left corner of the video.
confidence_definition: null # what the tracker's confidence value means
neck:
name: null
description: null
unit: null
reference_frame: null
confidence_definition: null
left_shoulder:
name: null
description: null
unit: null
reference_frame: null
confidence_definition: null
{
"DeviceModels": {
"camera_model": {
"name": null,
"manufacturer": null,
"model_number": null,
"description": null
}
},
"Devices": {
"camera": {
"name": null,
"description": null,
"serial_number": null,
"device_model_metadata_key": "camera_model"
}
},
"Pose": {
"Skeletons": {
"pose_estimation": {
"name": null,
"nodes": [
"head",
"neck",
"left_shoulder"
],
"edges": [
[
0,
1
],
[
1,
2
]
],
"subject": null
}
},
"PoseEstimations": {
"pose_estimation": {
"name": null,
"description": null,
"source_software": null,
"source_software_version": null,
"scorer": null,
"skeleton_metadata_key": "pose_estimation",
"device_metadata_key": "camera",
"source_video_metadata_key": null,
"labeled_video_metadata_key": null,
"PoseEstimationSeries": {
"head": {
"name": null,
"description": null,
"unit": null,
"reference_frame": null,
"confidence_definition": null
},
"neck": {
"name": null,
"description": null,
"unit": null,
"reference_frame": null,
"confidence_definition": null
},
"left_shoulder": {
"name": null,
"description": null,
"unit": null,
"reference_frame": null,
"confidence_definition": null
}
}
}
}
}
}