@@ -51,36 +51,11 @@ def _ensure_individuals_in_header(df, dummy_name):
5151 return df
5252
5353
54- def convert_h5_to_nwb (config , h5file , individual_name = "ind1" ):
55- """
56- Convert a DeepLabCut (DLC) video prediction, h5 data file to Neurodata Without Borders (NWB). Also
57- takes project config, to store relevant metadata.
58-
59- Parameters
60- ----------
61- config : str
62- Path to a project config.yaml file
63-
64- h5file : str
65- Path to a h5 data file
66-
67- individual_name : str
68- Name of the subject (whose pose is predicted) for single-animal DLC project.
69- For multi-animal projects, the names from the DLC project will be used directly.
70-
71- TODO: allow one to overwrite those names, with a mapping?
72-
73- Returns
74- -------
75- list of str
76- List of paths to the newly created NWB data files.
77- By default NWB files are stored in the same folder as the h5file.
78-
79- """
54+ def _get_pes_args (config_file , h5file , individual_name ):
8055 if "DLC" not in h5file or not h5file .endswith (".h5" ):
8156 raise IOError ("The file passed in is not a DeepLabCut h5 data file." )
8257
83- cfg = auxiliaryfunctions .read_config (config )
58+ cfg = auxiliaryfunctions .read_config (config_file )
8459
8560 vidname , scorer = os .path .split (h5file )[- 1 ].split ("DLC" )
8661 scorer = "DLC" + os .path .splitext (scorer )[0 ]
@@ -123,37 +98,105 @@ def convert_h5_to_nwb(config, h5file, individual_name="ind1"):
12398 ) # setting timestamps to dummy TODO: extract timestamps in DLC?
12499 else :
125100 timestamps = get_movie_timestamps (video [0 ])
101+ return scorer , df , video , paf_graph , timestamps , cfg
102+
103+
104+ def _write_pes_to_nwbfile (nwbfile , animal , df_animal , scorer , video , paf_graph , timestamps ):
105+ pose_estimation_series = []
106+ for kpt , xyp in df_animal .groupby (level = "bodyparts" , axis = 1 , sort = False ):
107+ data = xyp .to_numpy ()
108+
109+ pes = PoseEstimationSeries (
110+ name = f"{ animal } _{ kpt } " ,
111+ description = f"Keypoint { kpt } from individual { animal } ." ,
112+ data = data [:, :2 ],
113+ unit = "pixels" ,
114+ reference_frame = "(0,0) corresponds to the bottom left corner of the video." ,
115+ timestamps = timestamps ,
116+ confidence = data [:, 2 ],
117+ confidence_definition = "Softmax output of the deep neural network." ,
118+ )
119+ pose_estimation_series .append (pes )
120+
121+ pe = PoseEstimation (
122+ pose_estimation_series = pose_estimation_series ,
123+ description = "2D keypoint coordinates estimated using DeepLabCut." ,
124+ original_videos = [video [0 ]],
125+ # TODO check if this is a mandatory arg in ndx-pose (can skip if video is not found_
126+ dimensions = [list (map (int , video [1 ].split ("," )))[1 ::2 ]],
127+ scorer = scorer ,
128+ source_software = "DeepLabCut" ,
129+ source_software_version = __version__ ,
130+ nodes = [pes .name for pes in pose_estimation_series ],
131+ edges = paf_graph ,
132+ )
133+ if 'behavior' in nwbfile .processing :
134+ behavior_pm = nwbfile .processing ["behavior" ]
135+ else :
136+ behavior_pm = nwbfile .create_processing_module (
137+ name = "behavior" , description = "processed behavioral data"
138+ )
139+ behavior_pm .add (pe )
140+ return nwbfile
141+
142+
143+ def write_subject_to_nwb (nwbfile , h5file , individual_name , config_file ):
144+ """
145+ Given, subject name, write h5file to an existing nwbfile.
146+
147+ Parameters
148+ ----------
149+ nwbfile: pynwb.NWBFile
150+ nwbfile to write the subject specific pose estimation series.
151+ h5file : str
152+ Path to a h5 data file
153+ individual_name : str
154+ Name of the subject (whose pose is predicted) for single-animal DLC project.
155+ For multi-animal projects, the names from the DLC project will be used directly.
156+ config_file : str
157+ Path to a project config.yaml file
158+ config_dict : dict
159+ dict containing configuration options. Provide this as alternative to config.yml file.
160+
161+ Returns
162+ -------
163+ nwbfile: pynwb.NWBFile
164+ nwbfile with pes written in the behavior module
165+ """
166+ scorer , df , video , paf_graph , timestamps , _ = _get_pes_args (config_file , h5file , individual_name )
167+ df_animal = df .groupby (level = "individuals" , axis = 1 ).get_group (individual_name )
168+ return _write_pes_to_nwbfile (nwbfile , individual_name , df_animal , scorer , video , paf_graph , timestamps )
169+
170+
171+ def convert_h5_to_nwb (config , h5file , individual_name = "ind1" ):
172+ """
173+ Convert a DeepLabCut (DLC) video prediction, h5 data file to Neurodata Without Borders (NWB). Also
174+ takes project config, to store relevant metadata.
175+
176+ Parameters
177+ ----------
178+ config : str
179+ Path to a project config.yaml file
180+
181+ h5file : str
182+ Path to a h5 data file
183+
184+ individual_name : str
185+ Name of the subject (whose pose is predicted) for single-animal DLC project.
186+ For multi-animal projects, the names from the DLC project will be used directly.
187+
188+ TODO: allow one to overwrite those names, with a mapping?
189+
190+ Returns
191+ -------
192+ list of str
193+ List of paths to the newly created NWB data files.
194+ By default NWB files are stored in the same folder as the h5file.
126195
196+ """
197+ scorer , df , video , paf_graph , timestamps , cfg = _get_pes_args (config , h5file , individual_name )
127198 output_paths = []
128199 for animal , df_ in df .groupby (level = "individuals" , axis = 1 ):
129- pose_estimation_series = []
130- for kpt , xyp in df_ .groupby (level = "bodyparts" , axis = 1 , sort = False ):
131- data = xyp .to_numpy ()
132-
133- pes = PoseEstimationSeries (
134- name = f"{ animal } _{ kpt } " ,
135- description = f"Keypoint { kpt } from individual { animal } ." ,
136- data = data [:, :2 ],
137- unit = "pixels" ,
138- reference_frame = "(0,0) corresponds to the bottom left corner of the video." ,
139- timestamps = timestamps ,
140- confidence = data [:, 2 ],
141- confidence_definition = "Softmax output of the deep neural network." ,
142- )
143- pose_estimation_series .append (pes )
144-
145- pe = PoseEstimation (
146- pose_estimation_series = pose_estimation_series ,
147- description = "2D keypoint coordinates estimated using DeepLabCut." ,
148- original_videos = [video [0 ]],
149- dimensions = [list (map (int , video [1 ].split ("," )))[1 ::2 ]],
150- scorer = scorer ,
151- source_software = "DeepLabCut" ,
152- source_software_version = __version__ ,
153- nodes = [pes .name for pes in pose_estimation_series ],
154- edges = paf_graph ,
155- )
156-
157200 nwbfile = NWBFile (
158201 session_description = cfg ["Task" ],
159202 experimenter = cfg ["scorer" ],
@@ -162,10 +205,7 @@ def convert_h5_to_nwb(config, h5file, individual_name="ind1"):
162205 )
163206
164207 # TODO Store the test_pose_config as well?
165- behavior_pm = nwbfile .create_processing_module (
166- name = "behavior" , description = "processed behavioral data"
167- )
168- behavior_pm .add (pe )
208+ nwbfile = _write_pes_to_nwbfile (nwbfile , animal , df_ , scorer , video , paf_graph , timestamps )
169209 output_path = h5file .replace (".h5" , f"_{ animal } .nwb" )
170210 with warnings .catch_warnings (), NWBHDF5IO (output_path , mode = "w" ) as io :
171211 warnings .filterwarnings ("ignore" , category = DtypeConversionWarning )
0 commit comments