Coder Social home page Coder Social logo

sleepy's Introduction

sleepy

sleepy is a Python tool for semi-automatic slow wave detection and labelling. Its user interface is built for data augmentation and helps the user to quickly eliminate false-positives that are usually detected by purely automated approaches.

The project consists of two parts. The sleepy.processing package provides a range of algorithms for slow wave detection, a bandpass filter, an I/O adapter for .mat files exported from fieldtrip and an engine that embedds these algorithms, ulitmately allowing for custom algorithm implementations without the need to load, parse and format the data.

The sleepy.gui package provides a cross-platform GUI-extension for sleepy.processing, making it possible to run algorithms on a graphical user-interface, plot detected events, adding manual events on the data and tagging events as false-positives.

sleepy stores the filtered data, the (either automatically or manually) detected events and the tags associated with these events together with the dataset loaded, such that the work on tagging and/or adding events can be continued upon reloading the resulting file from any machine.

Installation

To start using sleepy, please run the commands below. These will clone the repository, install all dependencies. Note that you need python and pip.

git clone https://github.com/pupuis/sleepy.git
cd sleepy
pip install -r requirements.txt

From here on, starting the sleepy GUI program is as easy as running the off-the-shelf script, which is provided in the top-level-folder of sleepy.

python ots.py

Quickstart GUI

This quick tutorial walks you through the most important features of the sleepy GUI application and demonstrates how you can work with your dataset using this tool. Upon running sleepy, you can open a new dataset by navigating to File -> Open as demonstrated in the following picture or by using the shortcut Ctrl + O. Note that the examples are taken from the Windows version of sleepy. For shortcuts for Mac please refer to the section Keyboard-Shortcuts.

initial-screen

A file-chooser window opens. Once a dataset file is opened, sleepy opens the following preprocessing window which lets you change/verify the path to the dataset, select a filter and an algorithm and their respective parameters, which looks as follows:

preprocessing-dataset-selection preprocessing-choosing-filter preprocessing-choosing-algorithm

As you can see from the pictures it is not mandatory to apply a filter or an algorithm. However, note that if you do not select an algorithm, then you can only tag events that are already stored in the dataset.

Once you are finished setting up the preprocessing parameters, you can choose, by pressing the Compute button, to let sleepy compute the events and inform you of how many events were found, as can be seen in the following picture:

preprocessing-compute-hit

However, to actually plot the detected events and start tagging them, you need to load the events into the UI by pressing the Load button.

If no events can be found, either because no algorithm was applied and the dataset does not contain any previously computed events or because the algorithm was applied but unsuccessful in its search, sleepy shows an error message to inform you of this. Otherwise the algorithm has found at least one event and plots this in the main window, as you can see here:

tagging-at-first-sight

The event in the picture marks an interval were the event occured. At the moment there exist exactly two types of events, said interval events and point events, which mark a specific point in time as the event. Before continuing from here, you should take a look at the settings. You can open the settings as follows or by using the shortcut Ctrl+Q on Windows or the respective shortcut on Mac:

settings-open

Beside general settings for the application, you find options to customize your plots permanently (in contrast to the temporal changes you can make in the integrated matplotlib toolbar) and the possibility to adjust the visible area around the event. As you can see in the following picture the visible is set to 10 seconds before and after the events occurs. Note that you cannot view more than the current epoch allows, i.e. you might increase the visible area without a visible effect which might be because the plot has reached the end of the epoch.

settings-visible-area

To apply the changes, hit the Save button.

We will now turn back to the detected and plotted events. Using the arrow keys of your keyboard or the Previous and Next buttons on the screen you can navigate between event in the same channel, across all epochs. To switch between channels, you can use the D key for navigating backwards and the A key for navigating forwards. The navigation will always select exactly one event by marking the event with a designated color (which can also be customized in the settings). If you select an event that has been detected by the algorithm but you visually recognize that the underlying data does not indicate a slow-wave you can tag the event as a false-positive by pressing the corresponding button, as you can see in the following picture, or by pressing either Ctrl+P or the Up key. To revert the tag simply hit the button again.

tagging-tagging-an-event

Besides navigating with the arrow keys you can also jump to a location in the entire recording by double clicking the timeline below the plotted event. Note that you can also use the toolbar with the timeline, as this it is also drawn on a matplotlib canvas. However, you might encounter the case were the history of toolbar actions is cleared but the timeline remains in, e.g. a zoomed in, state. Then, by right-clicking anywhere on the timeline, you can reset the timeline to again show the entire recording.

If you visually detect an event while navigating through the data that has not been detected by the algorithm, you have the possibility to mark this event manually. Note the following. These events are so-called user events which can only be represented by a point, not by an interval. Further, the events are stored separately in the dataset such that they are identified as user events even after loading the dataset again. Note also that a user event cannot be tagged. You can create an user-event by double-clicking on the respective point on the graph in the main window, which causes the following context menu to appear:

tagging-user-event-create

By selecting the corresponding menu entry, a user event is created and displayed immediately. To revert the action, simply double-click on the user event as follows:

tagging-user-event-remove

By selecting the corresponding menu entry, the user event will be removed.

You can save your work by either selecting the respective menu entry under File -> Save or by pressing Ctrl+S. If you activated the usage of checkpoints in the settings, then the following message appears before closing the current dataset:

checkpoints-message

Checkpoints are references to your last position in the navigation grid that are stored in the dataset. These references consist of the index of the channel and the index of the event in that channel from where you are trying to close the dataset. This feature might be helpful when wanting to continue from the same position another time without having to remember at which event you closed the dataset. Note that checkpoints might be selecting the wrong event if you have reprocessed the dataset or added or removed user events in the mean time.

This concludes our walkthrough. At last, a humble wish from my side. If you are using sleepy and encounter any issues, e.g. in the form of crashes, unexpected or unwanted behaviour, please report the bug to us. This helps us constantly improve the application and increase the long-term quality of the sleepy software. When reporting a bug, you should add a potential error message you received and always attach a small description of the steps necessary to reproduce the issue. Only then can we reliably spot the errors and correct them.

Thank you.

Keyboard-Shortcuts

The sleepy GUI supports the following keyboard-shortcuts:

  • Ctrl+O / ⌘+O to open a new dataset
  • Left and Right arrow keys to navigate backward and forward between the events.
  • Ctrl+P / ⌘+P or the Up key to tag the selected event in the tagging environment
  • A for navigating backwards through channels and D for navigating forward through channels.
  • Ctrl+Q to open the settings on Windows and ⌘+, on Mac

sleepy's People

Contributors

caglorithm avatar cristianadim avatar interactive-maml avatar luis-mueller avatar

Watchers

 avatar  avatar

sleepy's Issues

Massimini error for some recordings

Traceback (most recent call last):
File "/home/cristiana/sleepy/sleepy/processing/dataset.py", line 97, in tags
return self._tags
AttributeError: 'MatDataset' object has no attribute '_tags'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/home/cristiana/sleepy/sleepy/gui/processing/core.py", line 39, in compute
events, _ = self.__computeEvents()
File "/home/cristiana/sleepy/sleepy/gui/processing/core.py", line 164, in __computeEvents
return Engine.run(self.algorithm, self.filter, dataset, settings), dataset
File "/home/cristiana/sleepy/sleepy/processing/engine.py", line 43, in run
events = Engine.__getEvents(dataset, settings)
File "/home/cristiana/sleepy/sleepy/processing/engine.py", line 194, in __getEvents
return np.array(dataset.forEachChannel(createEvents))
File "/home/cristiana/sleepy/sleepy/processing/dataset.py", line 141, in forEachChannel
return [ self.forEachLabel(channel, converter) for channel in range(numberOfChannels) ]
File "/home/cristiana/sleepy/sleepy/processing/dataset.py", line 141, in
return [ self.forEachLabel(channel, converter) for channel in range(numberOfChannels) ]
File "/home/cristiana/sleepy/sleepy/processing/dataset.py", line 160, in forEachLabel
return [ getObject(idx) for idx in range(numberOfLabels) ]
File "/home/cristiana/sleepy/sleepy/processing/dataset.py", line 160, in
return [ getObject(idx) for idx in range(numberOfLabels) ]
File "/home/cristiana/sleepy/sleepy/processing/dataset.py", line 156, in getObject
tag = self.tags[channel][labelIndex]
File "/home/cristiana/sleepy/sleepy/processing/mat/core.py", line 84, in tags
self.raw['sleepy-tags'] = super().tags
File "/home/cristiana/sleepy/sleepy/processing/dataset.py", line 104, in tags
for channel in range(numberOfChannels)
File "/home/cristiana/sleepy/sleepy/processing/dataset.py", line 104, in
for channel in range(numberOfChannels)
IndexError: tuple index out of range
Aborted (core dumped)

Removing Unnecessary Infrastructure

Some implementations are using much object-oriented approaches to structure the program-flow.
However, this should be avoided. Instead, everything that is not part of the GUI should be callable like
an API. That does not mean no class-instances but why e.g. does the engine exist? Its lifetime does not exceed one API call. The FileProcessor faces the same problem. The only thing that really requires permanent state is the GUI part, i.e. the preprocessing window. This can be called, returning the selected algorithm and filters. This can be done via the FileLoader. This removes a couple of abstraction levels and should lead to a cleaner structure. Basically the whole dataset processing could be one API call: Show preprocessing -> Get selected filter, algorithm -> Load the dataset into memory -> do the processing -> wrap the results into a navigator list -> return result

User-defined labels are merged after save

Can they stay alive?

  • After a user saves the dataset, all user-added labels are merged with the detected labels. Upon reload, all labels are deleted and data is reprocesses. The user should be able to keep their user labels forever without them being deleted by the reprocessing.
  • The user should also be able to delete all user labels (maybe a setting in the processing window?)

Loading screen shows number of events

In the loading screen, when setting the detection parameters, there should be info on how many detected events the selected parameters will produce (maybe with a button to refresh that number when the values are changed?)

Abstract classes for algorithm, dataset, filter - for extensibility

  • Algorithms, filters, datasets should be extensible. Thus, they need proper abstract base classes predefining some crucial methods etc. Public shared interface

  • Solution for adding Algorithms, filters without changing code. Idea: Inherit FileLoader and FileProcessor. Needed for dataset extensibility anyways. FileLoader should be passable via the SleepyGUI class via FileManager.

=> but then: add supportedLoaders, supportedAlgorithms, supportedFilters to SleepyGUI <=> no inheritance needed!

Variable names with illegal Matlab characters

The output fields from sleepy contain a dash (e.g., "sleepy-labels"), but Matlab only allows underscores in variable names (e.g., "sleepy_labels"), so even though importing in Matlab works, one cannot access/rename these variables.

Crash when selecting new file after selecting one before

I get an error if I want to load a new file from the file selector after having done it once:
I click the button under the red circle:

image

I get

  File "/Users/caglar/Documents/PhD/projects/sleepy/sleepy/io/gui/control.py", line 43, in selectPath
    newPath, _ = QFileDialog.getOpenFileName(self, 'Open File')
TypeError: getOpenFileName(parent: QWidget = None, caption: str = '', directory: str = '', filter: str = '', initialFilter: str = '', options: Union[QFileDialog.Options, QFileDialog.Option] = 0): argument 1 has unexpected type 'FileLoaderControl'
[1]    4001 abort      python ots.py```

Suggestions from Docs

Hey,
I'm just compiling here my notes that I've taken when I met with the medical Docs who work on the sleep data - so I won't forget them. Maybe you can comment on them to tell me what you think :)

  • option to turn on and off using plt.grid() and to change the grid's size / spacing
  • in the main view, mark peaks that are detected but not selected yet (other events in the same epoch)
  • in the loading screen, when setting the detection parameters, there should be info on how many detected events the selected parameters will produce (maybe with a button to refresh that number when the values are changed?)
  • mouse should be able to add events manually by clicking in the main screen
  • we had some crashes during the demonstration under normal use, I'll check later on how to reproduce them 🔥

Crash when loading data

I'm getting this error when loading a new time series . I select a new file, select Bandpass and select Massimi.

I select the

Traceback (most recent call last):
  File "/Users/caglar/Documents/PhD/projects/sleepy/sleepy/gui/window.py", line 90, in onOpenFile
    self.stack.switchToTagging(self.fileLoader)
  File "/Users/caglar/Documents/PhD/projects/sleepy/sleepy/gui/stack.py", line 22, in switchToTagging
    self.tagging.activate(fileLoader)
  File "/Users/caglar/Documents/PhD/projects/sleepy/sleepy/tagging/environments/core.py", line 57, in activate
    self.control.open(fileLoader)
  File "/Users/caglar/Documents/PhD/projects/sleepy/sleepy/tagging/control/core.py", line 219, in open
    self.navigator = navigator
  File "/Users/caglar/Documents/PhD/projects/sleepy/sleepy/tagging/control/core.py", line 62, in navigator
    [self.onChangesMade]
  File "/Users/caglar/Documents/PhD/projects/sleepy/sleepy/tagging/core.py", line 47, in initialize
    self.trigger()
  File "/Users/caglar/Documents/PhD/projects/sleepy/sleepy/tagging/core.py", line 38, in trigger
    list( map( lambda f: f(self.value), self._onTrigger ) )
  File "/Users/caglar/Documents/PhD/projects/sleepy/sleepy/tagging/core.py", line 38, in <lambda>
    list( map( lambda f: f(self.value), self._onTrigger ) )
  File "/Users/caglar/Documents/PhD/projects/sleepy/sleepy/tagging/control/core.py", line 315, in onChangesMade
    self.updateWindowTitle()
  File "/Users/caglar/Documents/PhD/projects/sleepy/sleepy/tagging/control/core.py", line 336, in updateWindowTitle
    counterString = self.getCounterString()
  File "/Users/caglar/Documents/PhD/projects/sleepy/sleepy/tagging/control/core.py", line 350, in getCounterString
    if self.settings.showIndex:
AttributeError: 'Settings' object has no attribute 'showIndex'
[1]    1156 abort      python ots.py```

Removing branch "relative"

Hi @CristianaDim,

in an attempt to clean up the repository I removed all deprecated/unused remote branches, except for master and your branch, "relative". With you permission I would also like to remove that branch as I am pretty sure all changes were merged into master.

Can you confirm this? If yes, please notify me really quick, so I can remove the branch too.

Thank you.

Checkpoints in the data-set

  1. Prompt on using data-set for the first time(with dont show this again)
  2. Save in data-set with key "sleepy-metadata" (this is what user should be warned about)
  3. "Remove checkpoint from data-set" (if it already exists)

Algorithms should be in separate folders

Sine the users will be able to add new algorithms themselves, it would be a cleaner folder structure and avoids possible conflicts of same filenames. For instance: sleepy/processing/algorithms/options.py is only for massimi.py

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.