SigVisualizer is a PyQt5 GUI application that visualizes electroencephalogram signals streamed from lab streaming layer (LSL) in real time.
- Python 3.x
- PyQt5
PyQt5 application for real time signal visualization
Home Page: https://github.com/labstreaminglayer/App-SigVisualizer
License: GNU General Public License v3.0
SigVisualizer is a PyQt5 GUI application that visualizes electroencephalogram signals streamed from lab streaming layer (LSL) in real time.
Python packages are commonly all lowercase. Would it be possible to rename this repository to "sigvisualizer"? If you are OK with this change, the files SigVisualizer.png
, SigVisualizer.py
, Ui_SigVisualizer.py
, and Ui_SigVisualizer.ui
should also be renamed accordingly.
I'd like to test the app by receiving some artificial LSL streams - do you know if there is a simple LSL outlet I could use? Ideally, this outlet would produce sinusoidal, rectangular, and sawtooth signals instead of random noise.
Just a heads up that MNE now supports LSL streaming with its realtime client API: mne-tools/mne-python#6141
I am trying to put the filtering or PSD calculation in the middle of SigVisualizer.
In the paintwidget.py, where is the best place to put the some code lines using scipy.signal and mne.filter?
from scipy.signal import lfilter, lfilter_zi
from mne.filter import create_filter
window = 10
n_samples = int(self.sfreq * window)
#self.sfreq=params['metadata']['srate']
#n_chans=params['metadata']['ch_count']
self.data_f = np.zeros((n_samples, self.n_chans))
self.data = np.zeros((n_samples, self.n_chans))
self.af = [1.0]
self.bf = create_filter(self.data_f.T, self.sfreq, 3, 40.,
method='fir')
LSL SigVisualizer repeatedly crashes when It receive streamed EEG Inlet from 256ch EGI Amplifier. The program starts properly and visualize very short part of initial eeg signals from 256chs, than crash.
Traceback (most recent call last):
File "/Users/User/Downloads/App-SigVisualizer-64fe663e88246c14fbd60b14eff84c228f5a85a6/paintwidget.py", line 216, in paintEvent
-self.dataBuffer[0][ch_idx] + chan_offset)
OverflowError: argument 4 overflowed: value must be in the range -2147483648 to 2147483647
Abort trap: 6
As soon as i click on Update Stream, i get an error in paintwidget.py:
`
for ch_idx in range(n_chans):
chan_offset = (ch_idx + 0.5) * self.channelHeight
if self.lastY:
if not math.isnan(self.lastY[ch_idx]) and not math.isnan(self.dataBuffer[0][ch_idx]):
painter.drawLine(x0 - self.px_per_samp,
-self.lastY[ch_idx] + chan_offset,
x0,
-self.dataBuffer[0][ch_idx] + chan_offset)
`
in line in painter.drawLine(...)
OverflowError: argument 4 overflowed: value must be in the range -2147483648 to 2147483647
When debuging, values in dataBuffer are too large, but i cannot figure out why is that. Im not sending those kind of values on the stream.
And this happens more if I add filters like below:
x = np.array(self.dataBuffer).T
`
if self.chunk_idx == 0 or self.mean.size == 0:
self.mean = np.mean(x, axis=1).reshape(-1, 1)
x2 = copy.deepcopy(x) - self.mean
data_range = (np.max(x2, axis=1) - np.min(x2, axis=1) + 0.0000000000001).reshape(-1, 1)
self.scaling = self.channelHeight * CHANNEL_Y_FILL / data_range
self.scaling = np.clip(self.scaling, 0.05, 1e8)
`
x = x - self.mean x = x * self.scaling
nyq = 0.5 * 1024
low = 1 / nyq
high = 50 / nyq
order = 5
b, a = signal.butter(order, [low,high], btype='band')
x = signal.filtfilt(b, a, x, axis=1)`
`self.dataBuffer = list(x.T)
This is the signal after filtering:
If there is any question regarding the issue, please feel free to ask me.
Currently the control panel on the left with checkboxes are not functional, yet. Ideally it should control which stream/channels are being streamed, and when checkbox is toggled, the changes should be reflected immediately.
Code here
for m in range(round(self.chunkSize/self.downSamplingFactor)):
if m != round(self.chunkSize/self.downSamplingFactor):
endIdx = (m+1) * self.downSamplingFactor
buf = [chunk[n][k] for n in range(m * self.downSamplingFactor, endIdx)]
else:
buf = [chunk[n][k] for n in range(m * self.downSamplingFactor, len(chunk))]
When round
rounds up, this goes out of range. e.g., I have a self.chunkSize of 655, self.downSamplingFactor of 16. 655 / 14 = 40.937. On the 41st iteration, m is 40 which != round(<>) so the first path is used with endIdx = 656; too big!
A simple solution would be to make endIdx = min((m+1) * self.downSamplingFactor, len(chunk))
and then the if-else block isn't needed.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.