Skip to content

Commit e8e5fec

Browse files
authored
Merge branch 'main' into sorting_extractors_spiking_times
2 parents a69f58f + 0a832dc commit e8e5fec

2 files changed

Lines changed: 71 additions & 4 deletions

File tree

pyproject.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,13 @@ extractors = [
6969
"sonpy;python_version<'3.10'",
7070
"lxml", # lxml for neuroscope
7171
"scipy",
72-
"ibllib==3.3.1", # streaming IBL
72+
"ibllib>=3.4.1;python_version>='3.10'", # streaming IBL
7373
"pymatreader>=0.0.32", # For cell explorer matlab files
7474
"zugbruecke>=0.2; sys_platform!='win32'", # For plexon2
7575
]
7676

7777
streaming_extractors = [
78-
"ibllib==3.3.1", # streaming IBL
78+
"ibllib>=3.4.1;python_version>='3.10'", # streaming IBL
7979
# Following dependencies are for streaming with nwb files
8080
"pynwb>=2.6.0",
8181
"fsspec",
@@ -144,7 +144,7 @@ test_extractors = [
144144
]
145145

146146
test_preprocessing = [
147-
"ibllib==3.3.1", # for IBL
147+
"ibllib>=3.4.1;python_version>='3.10'", # streaming IBL
148148
"torch",
149149
]
150150

@@ -156,7 +156,7 @@ test = [
156156
"psutil",
157157

158158
# preprocessing
159-
"ibllib==3.3.1", # for IBL
159+
"ibllib>=3.4.1;python_version>='3.10'",
160160

161161
# streaming templates
162162
"s3fs",

src/spikeinterface/extractors/neoextractors/neuroscope.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import warnings
44
from pathlib import Path
55
from typing import Union, Optional
6+
from xml.etree import ElementTree as Etree
67

78
import numpy as np
89

@@ -64,6 +65,7 @@ def __init__(
6465
if xml_file_path is not None:
6566
xml_file_path = str(Path(xml_file_path).absolute())
6667
self._kwargs.update(dict(file_path=str(Path(file_path).absolute()), xml_file_path=xml_file_path))
68+
self.xml_file_path = xml_file_path if xml_file_path is not None else Path(file_path).with_suffix(".xml")
6769

6870
@classmethod
6971
def map_to_neo_kwargs(cls, file_path, xml_file_path=None):
@@ -78,6 +80,71 @@ def map_to_neo_kwargs(cls, file_path, xml_file_path=None):
7880

7981
return neo_kwargs
8082

83+
def _parse_xml_file(self, xml_file_path):
84+
"""
85+
Comes from NeuroPhy package by Diba Lab
86+
"""
87+
tree = Etree.parse(xml_file_path)
88+
myroot = tree.getroot()
89+
90+
for sf in myroot.findall("acquisitionSystem"):
91+
n_channels = int(sf.find("nChannels").text)
92+
93+
channel_groups, skipped_channels, anatomycolors = [], [], {}
94+
for x in myroot.findall("anatomicalDescription"):
95+
for y in x.findall("channelGroups"):
96+
for z in y.findall("group"):
97+
chan_group = []
98+
for chan in z.findall("channel"):
99+
if int(chan.attrib["skip"]) == 1:
100+
skipped_channels.append(int(chan.text))
101+
102+
chan_group.append(int(chan.text))
103+
if chan_group:
104+
channel_groups.append(np.array(chan_group))
105+
106+
for x in myroot.findall("neuroscope"):
107+
for y in x.findall("channels"):
108+
for i, z in enumerate(y.findall("channelColors")):
109+
try:
110+
channel_id = str(z.find("channel").text)
111+
color = z.find("color").text
112+
113+
except AttributeError:
114+
channel_id = i
115+
color = "#0080ff"
116+
anatomycolors[channel_id] = color
117+
118+
discarded_channels = [ch for ch in range(n_channels) if all(ch not in group for group in channel_groups)]
119+
kept_channels = [ch for ch in range(n_channels) if ch not in skipped_channels and ch not in discarded_channels]
120+
121+
return channel_groups, kept_channels, discarded_channels, anatomycolors
122+
123+
def _set_neuroscope_groups(self):
124+
"""
125+
Set the group ids and colors based on the xml file.
126+
These group ids are usually different brain/body anatomical areas, or shanks from multi-shank probes.
127+
The group ids are set as a property of the recording extractor.
128+
"""
129+
n = self.get_num_channels()
130+
group_ids = np.full(n, -1, dtype=int) # Initialize all positions to -1
131+
132+
channel_groups, kept_channels, discarded_channels, colors = self._parse_xml_file(self.xml_file_path)
133+
for group_id, numbers in enumerate(channel_groups):
134+
group_ids[numbers] = group_id # Assign group_id to the positions in `numbers`
135+
self.set_property("neuroscope_group", group_ids)
136+
discarded_ppty = np.full(n, False, dtype=bool)
137+
discarded_ppty[discarded_channels] = True
138+
self.set_property("discarded_channels", discarded_ppty)
139+
self.set_property("colors", values=list(colors.values()), ids=list(colors.keys()))
140+
141+
def prepare_neuroscope_for_ephyviewer(self):
142+
"""
143+
Prepare the recording extractor for ephyviewer by setting the group ids and colors.
144+
This function is not called when the extractor is initialized, and the user must call it manually.
145+
"""
146+
self._set_neuroscope_groups()
147+
81148

82149
class NeuroScopeSortingExtractor(BaseSorting):
83150
"""

0 commit comments

Comments
 (0)