1
1
"""
2
- The following DataJoint pipeline implements the sequence of steps in the spike-sorting routine featured in the
3
- "spikeinterface" pipeline.
4
- Spikeinterface developed by Alessio Buccino, Samuel Garcia, Cole Hurwitz, Jeremy Magland, and Matthias Hennig (https://github.com/SpikeInterface)
5
-
6
- The DataJoint pipeline currently incorporated Spikeinterfaces approach of running Kilosort using a container
7
-
8
- The follow pipeline features intermediary tables:
9
- 1. PreProcessing - for preprocessing steps (no GPU required)
10
- - create recording extractor and link it to a probe
11
- - bandpass filtering
12
- - common mode referencing
13
- 2. SIClustering - kilosort (MATLAB) - requires GPU and docker/singularity containers
14
- - supports kilosort 2.0, 2.5 or 3.0 (https://github.com/MouseLand/Kilosort.git)
15
- 3. PostProcessing - for postprocessing steps (no GPU required)
16
- - create waveform extractor object
17
- - extract templates, waveforms and snrs
18
- - quality_metrics
2
+ The following DataJoint pipeline implements the sequence of steps in the spike-sorting routine featured in the "spikeinterface" pipeline. Spikeinterface was developed by Alessio Buccino, Samuel Garcia, Cole Hurwitz, Jeremy Magland, and Matthias Hennig (https://github.com/SpikeInterface)
19
3
"""
20
4
21
- import pathlib
22
5
from datetime import datetime
23
6
24
7
import datajoint as dj
25
8
import pandas as pd
26
- import probeinterface as pi
27
9
import spikeinterface as si
28
10
from element_array_ephys import get_logger , probe , readers
29
11
from element_interface .utils import find_full_path
30
12
from spikeinterface import exporters , postprocessing , qualitymetrics , sorters
31
13
14
+ from .. import get_logger , probe , readers
32
15
from . import si_preprocessing
33
16
34
17
log = get_logger (__name__ )
@@ -64,12 +47,6 @@ def activate(
64
47
65
48
SI_SORTERS = [s .replace ("_" , "." ) for s in si .sorters .sorter_dict .keys ()]
66
49
67
- SI_READERS = {
68
- "Open Ephys" : si .extractors .read_openephys ,
69
- "SpikeGLX" : si .extractors .read_spikeglx ,
70
- "Intan" : si .extractors .read_intan ,
71
- }
72
-
73
50
74
51
@schema
75
52
class PreProcessing (dj .Imported ):
@@ -125,9 +102,7 @@ def make(self, key):
125
102
output_dir = find_full_path (ephys .get_ephys_root_data_dir (), output_dir )
126
103
recording_dir = output_dir / sorter_name / "recording"
127
104
recording_dir .mkdir (parents = True , exist_ok = True )
128
- recording_file = (
129
- recording_dir / "si_recording.pkl"
130
- ) # recording cache to be created for each key
105
+ recording_file = recording_dir / "si_recording.pkl"
131
106
132
107
# Create SI recording extractor object
133
108
if acq_software == "SpikeGLX" :
@@ -142,12 +117,15 @@ def make(self, key):
142
117
assert len (oe_probe .recording_info ["recording_files" ]) == 1
143
118
data_dir = oe_probe .recording_info ["recording_files" ][0 ]
144
119
else :
145
- raise NotImplementedError (f"Not implemented for { acq_software } " )
120
+ acq_software = acq_software .replace (" " , "" ).lower ()
121
+ si_extractor : si .extractors .neoextractors = (
122
+ si .extractors .extractorlist .recording_extractor_full_dict [acq_software ]
123
+ ) # data extractor object
146
124
147
125
stream_names , stream_ids = si .extractors .get_neo_streams (
148
- acq_software . strip (). lower () , folder_path = data_dir
126
+ acq_software , folder_path = data_dir
149
127
)
150
- si_recording : si .BaseRecording = SI_READERS [acq_software ](
128
+ si_recording : si .BaseRecording = si_extractor [acq_software ](
151
129
folder_path = data_dir , stream_name = stream_names [0 ]
152
130
)
153
131
@@ -205,23 +183,23 @@ def make(self, key):
205
183
ephys .ClusteringTask * ephys .ClusteringParamSet & key
206
184
).fetch1 ("clustering_method" , "clustering_output_dir" , "params" )
207
185
output_dir = find_full_path (ephys .get_ephys_root_data_dir (), output_dir )
208
-
209
- # Get sorter method and create output directory.
210
186
sorter_name = clustering_method .replace ("." , "_" )
211
187
recording_file = output_dir / sorter_name / "recording" / "si_recording.pkl"
212
188
si_recording : si .BaseRecording = si .load_extractor (recording_file )
213
189
214
190
# Run sorting
191
+ # Sorting performed in a dedicated docker environment if the sorter is not built in the spikeinterface package.
215
192
si_sorting : si .sorters .BaseSorter = si .sorters .run_sorter (
216
193
sorter_name = sorter_name ,
217
194
recording = si_recording ,
218
195
output_folder = output_dir / sorter_name / "spike_sorting" ,
219
196
remove_existing_folder = True ,
220
197
verbose = True ,
221
- docker_image = True ,
198
+ docker_image = sorter_name not in si . sorters . installed_sorters () ,
222
199
** params .get ("SI_SORTING_PARAMS" , {}),
223
200
)
224
201
202
+ # Save sorting object
225
203
sorting_save_path = (
226
204
output_dir / sorter_name / "spike_sorting" / "si_sorting.pkl"
227
205
)
@@ -253,15 +231,14 @@ class PostProcessing(dj.Imported):
253
231
def make (self , key ):
254
232
execution_time = datetime .utcnow ()
255
233
256
- # Load recording object.
234
+ # Load recording & sorting object.
257
235
clustering_method , output_dir , params = (
258
236
ephys .ClusteringTask * ephys .ClusteringParamSet & key
259
237
).fetch1 ("clustering_method" , "clustering_output_dir" , "params" )
260
238
output_dir = find_full_path (ephys .get_ephys_root_data_dir (), output_dir )
261
-
262
- # Get sorter method and create output directory.
263
239
sorter_name = clustering_method .replace ("." , "_" )
264
240
output_dir = find_full_path (ephys .get_ephys_root_data_dir (), output_dir )
241
+
265
242
recording_file = output_dir / sorter_name / "recording" / "si_recording.pkl"
266
243
sorting_file = output_dir / sorter_name / "spike_sorting" / "si_sorting.pkl"
267
244
@@ -301,14 +278,13 @@ def make(self, key):
301
278
_ = si .postprocessing .compute_principal_components (
302
279
waveform_extractor = we , ** params .get ("SI_QUALITY_METRICS_PARAMS" , None )
303
280
)
281
+ metrics = si .qualitymetrics .compute_quality_metrics (waveform_extractor = we )
282
+
304
283
# Save the output (metrics.csv to the output dir)
305
284
metrics_output_dir = output_dir / sorter_name / "metrics"
306
285
metrics_output_dir .mkdir (parents = True , exist_ok = True )
307
-
308
- metrics = si .qualitymetrics .compute_quality_metrics (waveform_extractor = we )
309
286
metrics .to_csv (metrics_output_dir / "metrics.csv" )
310
287
311
- # Save results
312
288
self .insert1 (
313
289
{
314
290
** key ,
0 commit comments