Skip to content

Commit a1a35c7

Browse files
authored
Merge pull request #55 from int-brain-lab/develop
Develop
2 parents a89d8d3 + 660f5bc commit a1a35c7

File tree

20 files changed

+666
-214
lines changed

20 files changed

+666
-214
lines changed

atlaselectrophysiology/alignment_with_easyqc.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from easyqc.gui import viewseis
22
from ibllib.dsp import voltage
33
from ibllib.ephys import neuropixel
4-
from viewspikes.data import stream, get_ks2
4+
from viewspikes.data import stream
55
from viewspikes.plots import overlay_spikes
66
import scipy
77
from PyQt5 import QtCore, QtGui
@@ -35,6 +35,10 @@ def __init__(self, probe_id=None, one=None, histology=False, spike_collection=No
3535
self.lines_tracks = []
3636
self.points = []
3737

38+
self.plotdata.channels = Bunch()
39+
self.plotdata.channels['localCoordinates'] = self.plotdata.chn_coords_all
40+
self.plotdata.channels['rawInd'] = self.plotdata.chn_ind_all
41+
3842
def on_mouse_double_clicked(self, event):
3943
if not self.offline:
4044
if event.double() and event.modifiers() and QtCore.Qt.ShiftModifier:
@@ -136,12 +140,11 @@ def stream_ap(self, t):
136140
sos = scipy.signal.butter(3, 300 / self.ap.fs / 2, btype='highpass', output='sos')
137141
butt = scipy.signal.sosfiltfilt(sos, raw)
138142
destripe = voltage.destripe(raw, fs=self.ap.fs)
139-
ks2 = get_ks2(raw, dsets, self.loaddata.one)
143+
140144
self.eqc['butterworth'] = viewseis(butt.T, si=1 / self.ap.fs, h=h, t0=t0, title='butt',
141145
taxis=0)
142146
self.eqc['destripe'] = viewseis(destripe.T, si=1 / self.ap.fs, h=h, t0=t0, title='destr',
143147
taxis=0)
144-
self.eqc['ks2'] = viewseis(ks2.T, si=1 / self.ap.fs, h=h, t0=t0, title='ks2', taxis=0)
145148

146149
overlay_spikes(self.eqc['butterworth'], self.plotdata.spikes, self.plotdata.clusters,
147150
self.plotdata.channels)
@@ -227,10 +230,19 @@ def add_clust_scatter(self):
227230
def load_extra_data(probe_id, one=None, spike_collection=None):
228231
one = one or ONE()
229232
eid, probe = one.pid2eid(probe_id)
230-
if spike_collection:
233+
if spike_collection == '':
234+
collection = f'alf/{probe}'
235+
elif spike_collection:
231236
collection = f'alf/{probe}/{spike_collection}'
232237
else:
233-
collection = f'alf/{probe}'
238+
# Pykilosort is default, if not present look for normal kilosort
239+
# Find all collections
240+
all_collections = one.list_collections(eid)
241+
242+
if f'alf/{probe}/pykilosort' in all_collections:
243+
collection = f'alf/{probe}/pykilosort'
244+
else:
245+
collection = f'alf/{probe}'
234246

235247
_ = one.load_object(eid, obj='spikes', collection=collection,
236248
attribute='samples')

atlaselectrophysiology/ephys_atlas_gui.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
import os
2+
import platform
3+
4+
if platform.system() == 'Darwin':
5+
if platform.release().split('.')[0] == '20':
6+
os.environ["QT_MAC_WANTS_LAYER"] = "1"
7+
18
from PyQt5 import QtWidgets, QtCore, QtGui
29
import pyqtgraph as pg
310
import pyqtgraph.exporters
@@ -11,7 +18,6 @@
1118
import atlaselectrophysiology.ephys_gui_setup as ephys_gui
1219
from atlaselectrophysiology.create_overview_plots import make_overview_plot
1320
from pathlib import Path
14-
import os
1521
import qt
1622
import matplotlib.pyplot as mpl # noqa # This is needed to make qt show properly :/
1723

@@ -33,19 +39,20 @@ def _get_or_create(title='Electrophysiology Atlas', **kwargs):
3339
return av
3440

3541
def __init__(self, offline=False, probe_id=None, one=None, histology=True,
36-
spike_collection=None):
42+
spike_collection=None, remote=False):
3743
super(MainWindow, self).__init__()
3844

3945
self.init_variables()
4046
self.init_layout(self, offline=offline)
4147
self.configure = True
48+
one_mode = 'remote' if remote else 'auto'
4249
if not offline and probe_id is None:
43-
self.loaddata = LoadData()
50+
self.loaddata = LoadData(mode=one_mode)
4451
self.populate_lists(self.loaddata.get_subjects(), self.subj_list, self.subj_combobox)
4552
self.offline = False
4653
elif not offline and probe_id is not None:
4754
self.loaddata = LoadData(probe_id=probe_id, one=one, load_histology=histology,
48-
spike_collection=spike_collection)
55+
spike_collection=spike_collection, mode=one_mode)
4956
self.current_shank_idx = 0
5057
_, self.histology_exists = self.loaddata.get_info(0)
5158
self.feature_prev, self.track_prev = self.loaddata.get_starting_alignment(0)
@@ -1266,6 +1273,11 @@ def data_button_pressed(self):
12661273
self.probe_rfmap, self.rfmap_boundaries = self.plotdata.get_rfmap_data()
12671274
self.img_stim_data = self.plotdata.get_passive_events()
12681275

1276+
if not self.offline:
1277+
self.img_raw_data = self.plotdata.get_raw_data_image(self.loaddata.probe_id, one=self.loaddata.one)
1278+
else:
1279+
self.img_raw_data = {}
1280+
12691281
if self.histology_exists:
12701282
self.slice_data = self.loaddata.get_slice_images(self.ephysalign.xyz_samples)
12711283
else:
@@ -2106,11 +2118,12 @@ def viewer(probe_id, one=None, histology=False, spike_collection=None, title=Non
21062118

21072119
parser = argparse.ArgumentParser(description='Offline vs online mode')
21082120
parser.add_argument('-o', '--offline', default=False, required=False, help='Offline mode')
2121+
parser.add_argument('-r', '--remote', default=False, required=False, action='store_true', help='Remote mode')
21092122
parser.add_argument('-i', '--insertion', default=None, required=False, help='Insertion mode')
21102123
args = parser.parse_args()
21112124

21122125
app = QtWidgets.QApplication([])
2113-
mainapp = MainWindow(offline=args.offline, probe_id=args.insertion)
2126+
mainapp = MainWindow(offline=args.offline, probe_id=args.insertion, remote=args.remote)
21142127
# mainapp = MainWindow(offline=True)
21152128
mainapp.show()
21162129
app.exec_()

atlaselectrophysiology/ephys_gui_setup.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,14 @@ def init_menubar(self):
105105
img_options.addAction(scatter_amp)
106106
self.img_options_group.addAction(scatter_amp)
107107

108+
raw_type = list(self.img_raw_data.keys())
109+
for raw in raw_type:
110+
img = QtWidgets.QAction(raw, self, checkable=True, checked=False)
111+
img.triggered.connect(lambda checked, item=raw: self.plot_image(
112+
self.img_raw_data[item]))
113+
img_options.addAction(img)
114+
self.img_options_group.addAction(img)
115+
108116
stim_type = list(self.img_stim_data.keys())
109117
for stim in stim_type:
110118
img = QtWidgets.QAction(stim, self, checkable=True, checked=False)

atlaselectrophysiology/example_code.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,3 @@
66

77

88
av = viewer(probe_id, one=one)
9-
10-
11-
# To add trials to the window

atlaselectrophysiology/extract_files.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,20 +82,21 @@ def extract_rmsmap(fbin, out_folder=None, spectra=True):
8282
out_folder = Path(fbin).parent
8383
else:
8484
out_folder = Path(out_folder)
85-
alf_object_time = f'_iblqc_ephysTimeRms{sglx.type.upper()}'
86-
alf_object_freq = f'_iblqc_ephysSpectralDensity{sglx.type.upper()}'
85+
alf_object_time = f'ephysTimeRms{sglx.type.upper()}'
86+
alf_object_freq = f'ephysSpectralDensity{sglx.type.upper()}'
8787

8888
# crunch numbers
8989
rms = rmsmap(fbin, spectra=spectra)
9090
# output ALF files, single precision with the optional label as suffix before extension
9191
if not out_folder.exists():
9292
out_folder.mkdir()
9393
tdict = {'rms': rms['TRMS'].astype(np.single), 'timestamps': rms['tscale'].astype(np.single)}
94-
alfio.save_object_npy(out_folder, object=alf_object_time, dico=tdict)
94+
alfio.save_object_npy(out_folder, object=alf_object_time, dico=tdict, namespace='iblqc')
9595
if spectra:
9696
fdict = {'power': rms['spectral_density'].astype(np.single),
9797
'freqs': rms['fscale'].astype(np.single)}
98-
alfio.save_object_npy(out_folder, object=alf_object_freq, dico=fdict)
98+
alfio.save_object_npy(
99+
out_folder, object=alf_object_freq, dico=fdict, namespace='iblqc')
99100

100101

101102
def _sample2v(ap_file):

atlaselectrophysiology/load_data.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919

2020
class LoadData:
2121
def __init__(self, one=None, brain_atlas=None, testing=False, probe_id=None,
22-
load_histology=True, spike_collection=None):
23-
self.one = one or ONE(base_url=ONE_BASE_URL)
22+
load_histology=True, spike_collection=None, mode='auto'):
23+
self.one = one or ONE(base_url=ONE_BASE_URL, mode=mode)
2424
self.brain_atlas = brain_atlas or atlas.AllenAtlas(25)
2525
self.download_hist = load_histology # whether or not to look for the histology files
2626
self.spike_collection = spike_collection
@@ -435,7 +435,7 @@ def upload_data(self, xyz_channels, channels=True):
435435
# Create new trajectory and overwrite previous one
436436
histology.register_aligned_track(self.probe_id, xyz_channels,
437437
chn_coords=self.chn_coords, one=self.one,
438-
overwrite=True, channels=channels)
438+
overwrite=True, channels=channels, brain_atlas=self.brain_atlas)
439439
else:
440440
channel_upload = False
441441

atlaselectrophysiology/load_data_local.py

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
import glob
77
import json
88
from one.api import ONE
9+
from atlaselectrophysiology.load_histology import tif2nrrd
10+
11+
# temporarily add this in for neuropixel course until figured out fix to problem on win32
12+
import ssl
13+
ssl._create_default_https_context = ssl._create_unverified_context
914

1015

1116
class LoadDataLocal:
@@ -133,15 +138,21 @@ def get_slice_images(self, xyz_channels):
133138
# First see if the histology file exists before attempting to connect with FlatIron and
134139
# download
135140

136-
path_to_rd_image = glob.glob(str(self.folder_path) + '/*RD.nrrd')
137-
if path_to_rd_image:
138-
hist_path_rd = Path(path_to_rd_image[0])
141+
path_to_rd_image_nrrd = glob.glob(str(self.folder_path) + '/*RD.nrrd')
142+
path_to_rd_image_tif = glob.glob(str(self.folder_path) + '/*RD.tif')
143+
if path_to_rd_image_nrrd:
144+
hist_path_rd = Path(path_to_rd_image_nrrd[0])
145+
elif path_to_rd_image_tif:
146+
hist_path_rd = tif2nrrd(path_to_rd_image_tif[0])
139147
else:
140148
hist_path_rd = []
141149

142-
path_to_gr_image = glob.glob(str(self.folder_path) + '/*GR.nrrd')
143-
if path_to_gr_image:
144-
hist_path_gr = Path(path_to_gr_image[0])
150+
path_to_gr_image_nrrd = glob.glob(str(self.folder_path) + '/*GR.nrrd')
151+
path_to_gr_image_tif = glob.glob(str(self.folder_path) + '/*GR.tif')
152+
if path_to_gr_image_nrrd:
153+
hist_path_gr = Path(path_to_gr_image_nrrd[0])
154+
elif path_to_gr_image_tif:
155+
hist_path_gr = tif2nrrd(path_to_gr_image_tif[0])
145156
else:
146157
hist_path_gr = []
147158

@@ -244,11 +255,11 @@ def create_channel_dict(brain_regions):
244255
channel_dict = {}
245256
for i in np.arange(brain_regions.id.size):
246257
channel = {
247-
'x': brain_regions.xyz[i, 0] * 1e6,
248-
'y': brain_regions.xyz[i, 1] * 1e6,
249-
'z': brain_regions.xyz[i, 2] * 1e6,
250-
'axial': brain_regions.axial[i],
251-
'lateral': brain_regions.lateral[i],
258+
'x': np.float64(brain_regions.xyz[i, 0] * 1e6),
259+
'y': np.float64(brain_regions.xyz[i, 1] * 1e6),
260+
'z': np.float64(brain_regions.xyz[i, 2] * 1e6),
261+
'axial': np.float64(brain_regions.axial[i]),
262+
'lateral': np.float64(brain_regions.lateral[i]),
252263
'brain_region_id': int(brain_regions.id[i]),
253264
'brain_region': brain_regions.acronym[i]
254265
}

atlaselectrophysiology/load_histology.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def download_histology_data(subject, lab):
5151
path_to_image = Path(CACHE_DIR, file)
5252
if not path_to_image.exists():
5353
url = (baseurl + '/' + file)
54-
http_download_file(url, cache_dir=CACHE_DIR,
54+
http_download_file(url, target_dir=CACHE_DIR,
5555
username=par.HTTP_DATA_SERVER_LOGIN,
5656
password=par.HTTP_DATA_SERVER_PWD)
5757

@@ -65,7 +65,7 @@ def download_histology_data(subject, lab):
6565

6666

6767
def tif2nrrd(path_to_image):
68-
path_to_nrrd = Path(path_to_image.parent, path_to_image.parts[-1][:-3] + 'nrrd')
68+
path_to_nrrd = Path(path_to_image).with_suffix('.nrrd')
6969
if not path_to_nrrd.exists():
7070
reader = sitk.ImageFileReader()
7171
reader.SetImageIO("TIFFImageIO")

atlaselectrophysiology/plot_data.py

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
import numpy as np
44
import one.alf as alf
55
from brainbox.processing import bincount2D
6+
from brainbox.io.spikeglx import stream
67
from brainbox.population.decode import xcorr
78
from brainbox.task import passive
9+
from ibllib.dsp import voltage
810
import scipy
911
from PyQt5 import QtGui
1012

@@ -23,8 +25,9 @@ def __init__(self, probe_path, ephys_path, alf_path, shank_idx):
2325
self.ephys_path = ephys_path
2426
self.alf_path = alf_path
2527

26-
self.chn_coords_all = np.load(Path(self.probe_path, 'channels.localCoordinates.npy'))
27-
self.chn_ind_all = np.load(Path(self.probe_path, 'channels.rawInd.npy'))
28+
channels = alf.io.load_object(self.probe_path, 'channels')
29+
self.chn_coords_all = channels['localCoordinates']
30+
self.chn_ind_all = channels['rawInd'].astype(int)
2831

2932
self.chn_min = np.min(self.chn_coords_all[:, 1])
3033
self.chn_max = np.max(self.chn_coords_all[:, 1])
@@ -71,7 +74,8 @@ def __init__(self, probe_path, ephys_path, alf_path, shank_idx):
7174
self.cluster_data_status = True
7275
self.compute_timescales()
7376

74-
except Exception:
77+
except Exception as err:
78+
print(err)
7579
print('cluster data was not found, some plots will not display')
7680
self.cluster_data_status = False
7781

@@ -295,9 +299,11 @@ def get_fr_img(self):
295299
else:
296300
T_BIN = 0.05
297301
D_BIN = 5
302+
chn_min = np.min(np.r_[self.chn_min, self.spikes['depths'][self.spike_idx][self.kp_idx]])
303+
chn_max = np.max(np.r_[self.chn_max, self.spikes['depths'][self.spike_idx][self.kp_idx]])
298304
n, times, depths = bincount2D(self.spikes['times'][self.spike_idx][self.kp_idx],
299305
self.spikes['depths'][self.spike_idx][self.kp_idx],
300-
T_BIN, D_BIN, ylim=[self.chn_min, self.chn_max])
306+
T_BIN, D_BIN, ylim=[chn_min, chn_max])
301307
img = n.T / T_BIN
302308
xscale = (times[-1] - times[0]) / img.shape[0]
303309
yscale = (depths[-1] - depths[0]) / img.shape[1]
@@ -323,14 +329,16 @@ def get_fr_amp_data_line(self):
323329
else:
324330
T_BIN = np.max(self.spikes['times'])
325331
D_BIN = 10
332+
chn_min = np.min(np.r_[self.chn_min, self.spikes['depths'][self.spike_idx][self.kp_idx]])
333+
chn_max = np.max(np.r_[self.chn_max, self.spikes['depths'][self.spike_idx][self.kp_idx]])
326334
nspikes, times, depths = bincount2D(self.spikes['times'][self.spike_idx][self.kp_idx],
327335
self.spikes['depths'][self.spike_idx][self.kp_idx],
328336
T_BIN, D_BIN,
329-
ylim=[self.chn_min, self.chn_max])
337+
ylim=[chn_min, chn_max])
330338

331339
amp, times, depths = bincount2D(self.spikes['amps'][self.spike_idx][self.kp_idx],
332340
self.spikes['depths'][self.spike_idx][self.kp_idx],
333-
T_BIN, D_BIN, ylim=[self.chn_min, self.chn_max],
341+
T_BIN, D_BIN, ylim=[chn_min, chn_max],
334342
weights=self.spikes['amps'][self.spike_idx]
335343
[self.kp_idx])
336344
mean_fr = nspikes[:, 0] / T_BIN
@@ -363,9 +371,11 @@ def get_correlation_data_img(self):
363371
else:
364372
T_BIN = 0.05
365373
D_BIN = 40
374+
chn_min = np.min(np.r_[self.chn_min, self.spikes['depths'][self.spike_idx][self.kp_idx]])
375+
chn_max = np.max(np.r_[self.chn_max, self.spikes['depths'][self.spike_idx][self.kp_idx]])
366376
R, times, depths = bincount2D(self.spikes['times'][self.spike_idx][self.kp_idx],
367377
self.spikes['depths'][self.spike_idx][self.kp_idx],
368-
T_BIN, D_BIN, ylim=[self.chn_min, self.chn_max])
378+
T_BIN, D_BIN, ylim=[chn_min, chn_max])
369379
corr = np.corrcoef(R)
370380
corr[np.isnan(corr)] = 0
371381
scale = (np.max(depths) - np.min(depths)) / corr.shape[0]
@@ -464,6 +474,38 @@ def median_subtract(a):
464474

465475
return data_img, data_probe
466476

477+
# only for IBL sorry
478+
def get_raw_data_image(self, pid, t0=(1000, 2000, 3000), one=None):
479+
480+
def gain2level(gain):
481+
return 10 ** (gain / 20) * 4 * np.array([-1, 1])
482+
data_img = dict()
483+
for t in t0:
484+
485+
sr, t = stream(pid, t, one=one)
486+
raw = sr[:, :-sr.nsync].T
487+
channel_labels, channel_features = voltage.detect_bad_channels(raw, sr.fs)
488+
raw = voltage.destripe(raw, fs=sr.fs, channel_labels=channel_labels)
489+
raw_image = raw[:, int((450 / 1e3) * sr.fs):int((500 / 1e3) * sr.fs)].T
490+
x_range = np.array([0, raw_image.shape[0] - 1]) / sr.fs * 1e3
491+
levels = gain2level(-90)
492+
xscale = (x_range[1] - x_range[0]) / raw_image.shape[0]
493+
yscale = (self.chn_max - self.chn_min) / raw_image.shape[1]
494+
495+
data_raw = {
496+
'img': raw_image,
497+
'scale': np.array([xscale, yscale]),
498+
'levels': levels,
499+
'offset': np.array([0, 0]),
500+
'cmap': 'bone',
501+
'xrange': x_range,
502+
'xaxis': 'Time (ms)',
503+
'title': 'Power (uV)'
504+
}
505+
data_img[f'Raw data t={t}'] = data_raw
506+
507+
return data_img
508+
467509
def get_lfp_spectrum_data(self):
468510
freq_bands = np.vstack(([0, 4], [4, 10], [10, 30], [30, 80], [80, 200]))
469511
data_probe = {}

0 commit comments

Comments
 (0)