Skip to content

Commit 73bd245

Browse files
authored
Update read_openephys and add OneBox tests (#4456)
1 parent 1f0339f commit 73bd245

File tree

4 files changed

+34
-35
lines changed

4 files changed

+34
-35
lines changed

src/spikeinterface/extractors/neoextractors/openephys.py

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -315,54 +315,36 @@ def __init__(
315315

316316
# find settings file
317317
if "#" in stream_name:
318-
record_node, oe_stream = stream_name.split("#")
318+
record_node, oe_stream_name = stream_name.split("#")
319319
else:
320320
record_node = ""
321-
oe_stream = stream_name
322-
exp_ids = sorted(list(self.neo_reader.folder_structure[record_node]["experiments"].keys()))
321+
oe_stream_name = stream_name
322+
node_structure = self.neo_reader.folder_structure[record_node]
323+
exp_ids = sorted(list(node_structure["experiments"].keys()))
323324
if block_index is None:
324325
exp_id = exp_ids[0]
325326
else:
326327
exp_id = exp_ids[block_index]
327-
rec_ids = sorted(
328-
list(self.neo_reader.folder_structure[record_node]["experiments"][exp_id]["recordings"].keys())
329-
)
328+
rec_ids = sorted(list(node_structure["experiments"][exp_id]["recordings"].keys()))
330329

331330
# do not load probe for NIDQ stream or if load_sync_channel is True
332331
if "NI-DAQmx" not in stream_name and not load_sync_channel:
333-
settings_file = self.neo_reader.folder_structure[record_node]["experiments"][exp_id]["settings_file"]
332+
settings_file = node_structure["experiments"][exp_id]["settings_file"]
334333

335334
if Path(settings_file).is_file():
336335
probe = probeinterface.read_openephys(
337-
settings_file=settings_file, stream_name=stream_name, raise_error=False
336+
settings_file=settings_file, stream_name=oe_stream_name, raise_error=False
338337
)
339338
else:
340339
probe = None
341340

342341
if probe is not None:
343-
# Ensure device channel index corresponds to channel_ids
344-
probe_channel_names = probe.contact_annotations.get("channel_name", None)
345-
if probe_channel_names is not None and not np.array_equal(probe_channel_names, self.channel_ids):
346-
if set(probe_channel_names) == set(self.channel_ids):
347-
device_channel_indices = []
348-
probe_channel_names = list(probe_channel_names)
349-
device_channel_indices = np.zeros(len(self.channel_ids), dtype=int)
350-
for i, ch in enumerate(self.channel_ids):
351-
index_in_probe = probe_channel_names.index(ch)
352-
device_channel_indices[index_in_probe] = i
353-
probe.set_device_channel_indices(device_channel_indices)
354-
else:
355-
warnings.warn(
356-
"Channel names in the probe do not match the channel ids from Neo. "
357-
"Cannot set device channel indices, but this might lead to incorrect probe geometries"
358-
)
359-
360342
if probe.shank_ids is not None:
361343
self.set_probe(probe, in_place=True, group_mode="by_shank")
362344
else:
363345
self.set_probe(probe, in_place=True)
364346
# get inter-sample shifts based on the probe information and mux channels
365-
sample_shifts = get_neuropixels_sample_shifts_from_probe(probe, stream_name=self.stream_name)
347+
sample_shifts = get_neuropixels_sample_shifts_from_probe(probe)
366348
if sample_shifts is not None:
367349
self.set_property("inter_sample_shift", sample_shifts)
368350

@@ -371,7 +353,7 @@ def __init__(
371353
stream_folders = []
372354
for segment_index, rec_id in enumerate(rec_ids):
373355
stream_folder = (
374-
recording_folder / f"experiment{exp_id}" / f"recording{rec_id}" / "continuous" / oe_stream
356+
recording_folder / f"experiment{exp_id}" / f"recording{rec_id}" / "continuous" / oe_stream_name
375357
)
376358
stream_folders.append(stream_folder)
377359
if load_sync_timestamps:

src/spikeinterface/extractors/neoextractors/spikeglx.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ def __init__(
9494
self.set_probe(probe, in_place=True)
9595

9696
# get inter-sample shifts based on the probe information and mux channels
97-
sample_shifts = get_neuropixels_sample_shifts_from_probe(probe, stream_name=self.stream_name)
97+
sample_shifts = get_neuropixels_sample_shifts_from_probe(probe)
9898
if sample_shifts is not None:
9999
self.set_property("inter_sample_shift", sample_shifts)
100100
else:

src/spikeinterface/extractors/neuropixels_utils.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,14 @@
44
from probeinterface import Probe
55

66

7-
def get_neuropixels_sample_shifts_from_probe(probe: Probe, stream_name: str = "ap") -> np.ndarray:
7+
def get_neuropixels_sample_shifts_from_probe(probe: Probe) -> np.ndarray:
88
"""
99
Get the inter-sample shifts for Neuropixels probes based on the probe information.
1010
1111
Parameters
1212
----------
1313
probe : Probe
1414
The probe object containing channel and ADC information.
15-
stream_name : str, default: "ap"
16-
The name of the stream for which to calculate the sample shifts.
17-
This is used for Neuropixels 1.0 technology to correctly set the number of cycles.
1815
1916
Returns
2017
-------

src/spikeinterface/extractors/tests/test_neoextractors.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import importlib.util
77

88
import pytest
9+
import numpy as np
910

1011
from spikeinterface import get_global_dataset_folder
1112
from spikeinterface.extractors.extractor_classes import (
@@ -121,9 +122,6 @@ class OpenEphysBinaryRecordingTest(RecordingCommonTestSuite, unittest.TestCase):
121122
("openephysbinary/v0.5.x_two_nodes", {"stream_id": "0"}),
122123
("openephysbinary/v0.5.x_two_nodes", {"stream_id": "1"}),
123124
("openephysbinary/v0.6.x_neuropixels_multiexp_multistream", {"stream_id": "0", "block_index": 0}),
124-
# TODO: block_indices 1/2 of v0.6.x_neuropixels_multiexp_multistream have a mismatch in the channel names between
125-
# the settings files (starting with CH0) and structure.oebin (starting at CH1).
126-
# Currently, the extractor will skip remapping to match order in oebin and settings file, raising a warning
127125
("openephysbinary/v0.6.x_neuropixels_multiexp_multistream", {"stream_id": "1", "block_index": 1}),
128126
(
129127
"openephysbinary/v0.6.x_neuropixels_multiexp_multistream",
@@ -134,8 +132,30 @@ class OpenEphysBinaryRecordingTest(RecordingCommonTestSuite, unittest.TestCase):
134132
"openephysbinary/v0.6.x_neuropixels_multiexp_multistream",
135133
{"stream_id": "2", "block_index": 2, "load_sync_timestamps": True},
136134
),
135+
(
136+
"openephysbinary/v0.6.x_onebox_neuropixels",
137+
{"stream_name": "Record Node 101#OneBox-100.ProbeA-AP", "block_index": 0},
138+
),
139+
(
140+
"openephysbinary/v0.6.x_onebox_neuropixels_nontrivial_wiring",
141+
{"stream_name": "Record Node 101#OneBox-111.ProbeA", "block_index": 0},
142+
),
137143
]
138144

145+
def test_non_trivial_wiring(self):
146+
"""
147+
Test that we can load the probe information and sample shifts for a one box neuropixels recording with
148+
non trivial wiring.
149+
"""
150+
folder_path = local_folder / "openephysbinary/v0.6.x_onebox_neuropixels_nontrivial_wiring"
151+
stream_name = "Record Node 101#OneBox-111.ProbeA"
152+
block_index = 0
153+
154+
recording = self.ExtractorClass(folder_path, stream_name=stream_name, block_index=block_index)
155+
# check that channel_ids and settings_channel_key contact annotations are correctly loaded
156+
probe = recording.get_probe()
157+
np.testing.assert_array_equal(recording.channel_ids, probe.contact_annotations["settings_channel_key"])
158+
139159

140160
class OpenEphysBinaryEventTest(EventCommonTestSuite, unittest.TestCase):
141161
ExtractorClass = OpenEphysBinaryEventExtractor

0 commit comments

Comments
 (0)