Skip to content

Commit 2c84598

Browse files
committed
add raw data viewer from raster
1 parent 3489763 commit 2c84598

File tree

2 files changed

+155
-7
lines changed

2 files changed

+155
-7
lines changed

viewspikes/gui.py

Lines changed: 100 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,106 @@
33
import numpy as np
44
from PyQt5 import QtWidgets, QtCore, QtGui, uic
55
import pyqtgraph as pg
6-
7-
from iblapps import qt
6+
import scipy.signal
7+
from brainbox.processing import bincount2D
8+
import qt
89
from iblutil.numerical import ismember
910
from easyqc.gui import viewseis
10-
11-
12-
def viewephys(data, fs, channels=None, br=None, title='ephys'):
11+
from ibllib.io import spikeglx
12+
from ibllib.dsp import voltage
13+
14+
15+
class RasterView(QtWidgets.QMainWindow):
16+
def __init__(self, bin_file, spikes, clusters, channels=None, trials=None, *args, **kwargs):
17+
self.sr = spikeglx.Reader(bin_file)
18+
self.spikes = spikes
19+
self.clusters = clusters
20+
self.channels = channels
21+
self.eqcs = []
22+
super(RasterView, self).__init__(*args, **kwargs)
23+
# wave by Diana Militano from the Noun Projectp
24+
uic.loadUi(Path(__file__).parent.joinpath('raster.ui'), self)
25+
background_color = self.palette().color(self.backgroundRole())
26+
self.plotItem_raster.setAspectLocked(False)
27+
self.imageItem_raster = pg.ImageItem()
28+
self.plotItem_raster.setBackground(background_color)
29+
self.plotItem_raster.addItem(self.imageItem_raster)
30+
self.viewBox_raster = self.plotItem_raster.getPlotItem().getViewBox()
31+
s = self.viewBox_raster.scene()
32+
# vb.scene().sigMouseMoved.connect(self.mouseMoveEvent)
33+
s.sigMouseClicked.connect(self.mouseClick)
34+
35+
# set image
36+
t_bin = .007
37+
d_bin = 10
38+
self.raster, self.rtimes, self.depths = bincount2D(spikes.times, spikes.depths, t_bin, d_bin)
39+
self.imageItem_raster.setImage(np.flip(self.raster.T))
40+
transform = [t_bin, 0., 0., 0., d_bin, 0., - .5, - .5, 1.]
41+
self.transform = np.array(transform).reshape((3, 3)).T
42+
self.imageItem_raster.setTransform(QtGui.QTransform(*transform))
43+
self.plotItem_raster.setLimits(xMin=0, xMax=self.rtimes[-1], yMin=0, yMax=self.depths[-1])
44+
45+
# set colormap
46+
cm = pg.colormap.get('Greys', source='matplotlib') # prepare a linear color map
47+
bar = pg.ColorBarItem(values=(0, .5), colorMap=cm) # prepare interactive color bar
48+
# Have ColorBarItem control colors of img and appear in 'plot':
49+
bar.setImageItem(self.imageItem_raster)
50+
self.show()
51+
52+
53+
54+
def mouseClick(self, event):
55+
if not event.double():
56+
return
57+
qxy = self.imageItem_raster.mapFromScene(event.scenePos())
58+
self.show_ephys(t0=self.rtimes[int(qxy.x())])
59+
60+
def keyPressEvent(self, e):
61+
"""
62+
page-up / ctrl + a : gain up
63+
page-down / ctrl + z : gain down
64+
:param e:
65+
"""
66+
k, m = (e.key(), e.modifiers())
67+
# page up / ctrl + a
68+
if k == QtCore.Qt.Key_PageUp or (
69+
m == QtCore.Qt.ControlModifier and k == QtCore.Qt.Key_A):
70+
self.imageItem_raster.setLevels([0, self.imageItem_raster.levels[1] / 1.4])
71+
# page down / ctrl + z
72+
elif k == QtCore.Qt.Key_PageDown or (
73+
m == QtCore.Qt.ControlModifier and k == QtCore.Qt.Key_Z):
74+
self.imageItem_raster.setLevels([0, self.imageItem_raster.levels[1] * 1.4])
75+
76+
def show_ephys(self, t0, len=1):
77+
78+
first = int(t0 * self.sr.fs)
79+
last = first + int(self.sr.fs * len)
80+
81+
raw = self.sr[first:last, : - self.sr.nsync].T
82+
83+
butter_kwargs = {'N': 3, 'Wn': 300 / self.sr.fs * 2, 'btype': 'highpass'}
84+
sos = scipy.signal.butter(**butter_kwargs, output='sos')
85+
butt = scipy.signal.sosfiltfilt(sos, raw)
86+
destripe = voltage.destripe(raw, fs=self.sr.fs)
87+
88+
# overlay spikes
89+
tprobe = self.spikes.samples / self.sr.fs
90+
slice_spikes = slice(np.searchsorted(tprobe, t0), np.searchsorted(tprobe, t0 + len))
91+
t = tprobe[slice_spikes]
92+
c = self.clusters.channels[self.spikes.clusters[slice_spikes]]
93+
94+
self.eqc_raw = viewephys(butt, self.sr.fs, channels=None, br=None, title='butt', t0=t0, t_scalar=1)
95+
self.eqc_des = viewephys(destripe, self.sr.fs, channels=None, br=None, title='destripe', t0=t0, t_scalar=1)
96+
# eqc2 = viewephys(butt - destripe, self.sr.fs, channels=None, br=None, title='diff')
97+
98+
self.eqc_raw.ctrl.add_scatter(t, c)
99+
self.eqc_des.ctrl.add_scatter(t, c)
100+
# eqc2.ctrl.add_scatter(t, c)
101+
102+
103+
104+
105+
def viewephys(data, fs, channels=None, br=None, title='ephys', t0=0, t_scalar=1e3):
13106
"""
14107
:param data: [nc, ns]
15108
:param fs:
@@ -28,15 +121,15 @@ def viewephys(data, fs, channels=None, br=None, title='ephys'):
28121
from ibllib.ephys.neuropixel import trace_header
29122
if channels is None or br is None:
30123
channels = trace_header(version = 1)
31-
eqc = viewseis(data.T, si=1 / fs * 1e3, h=channels, title=title, taxis=0)
124+
eqc = viewseis(data.T, si=1 / fs * t_scalar, h=channels, title=title, taxis=0, t0=t0)
32125
return eqc
33126
else:
34127
_, ir = ismember(channels['atlas_id'], br.id)
35128
image = br.rgb[ir].astype(np.uint8)
36129
image = image[np.newaxis, :, :]
37130

38131

39-
eqc = viewseis(data.T, si=1 / fs * 1e3, h=channels, title=title, taxis=0)
132+
eqc = viewseis(data.T, si=1 / fs * t_scalar, h=channels, title=title, taxis=0)
40133
imitem = pg.ImageItem(image)
41134
eqc.plotItem_header_v.addItem(imitem)
42135
transform = [1, 0, 0, 0, 1, 0, -0.5, 0, 1.]

viewspikes/raster.ui

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ui version="4.0">
3+
<class>MainWindow</class>
4+
<widget class="QMainWindow" name="MainWindow">
5+
<property name="geometry">
6+
<rect>
7+
<x>0</x>
8+
<y>0</y>
9+
<width>810</width>
10+
<height>606</height>
11+
</rect>
12+
</property>
13+
<property name="windowTitle">
14+
<string>MainWindow</string>
15+
</property>
16+
<widget class="QWidget" name="centralwidget">
17+
<layout class="QVBoxLayout" name="verticalLayout">
18+
<item>
19+
<layout class="QGridLayout" name="gridLayout">
20+
<item row="0" column="0">
21+
<widget class="PlotWidget" name="plotItem_raster"/>
22+
</item>
23+
</layout>
24+
</item>
25+
<item>
26+
<widget class="QSlider" name="horizontalSlider">
27+
<property name="orientation">
28+
<enum>Qt::Horizontal</enum>
29+
</property>
30+
</widget>
31+
</item>
32+
</layout>
33+
</widget>
34+
<widget class="QMenuBar" name="menubar">
35+
<property name="geometry">
36+
<rect>
37+
<x>0</x>
38+
<y>0</y>
39+
<width>810</width>
40+
<height>22</height>
41+
</rect>
42+
</property>
43+
</widget>
44+
<widget class="QStatusBar" name="statusbar"/>
45+
</widget>
46+
<customwidgets>
47+
<customwidget>
48+
<class>PlotWidget</class>
49+
<extends>QGraphicsView</extends>
50+
<header>pyqtgraph</header>
51+
</customwidget>
52+
</customwidgets>
53+
<resources/>
54+
<connections/>
55+
</ui>

0 commit comments

Comments
 (0)