Skip to main content

Tools for neuroscience experiments

Project description

toon

image image image image image

Description

Additional tools for neuroscience experiments, including:

  • A framework for polling input devices on a separate process.
  • A framework for keyframe-based animation.

Everything should work on Windows/Mac/Linux.

See requirements.txt for dependencies.

Install

Current release:

pip install toon

Development version:

pip install git+https://github.com/aforren1/toon

For full install (including device and demo dependencies):

pip install toon[full]

See setup.py for a list of those dependencies, as well as device-specific subdivisions.

See the demos/ folder for usage examples (note: some require psychopy).

Overview

Input

toon provides a framework for polling from input devices, including common peripherals like mice and keyboards, with the flexibility to handle less-common devices like eyetrackers, motion trackers, and custom devices (see toon/input/ for examples). The goal is to make it easier to use a wide variety of devices, including those with sampling rates >1kHz, with minimal performance impact on the main process.

We use the built-in multiprocessing module to control a separate process that hosts the device, and, in concert with numpy, to move data to the main process via shared memory. It seems that under typical conditions, we can expect single read() operations to take less than 500 microseconds (and more often < 100 us). See demos/bench.py for an example of measuring user-side read performance.

Typical use looks like this:

from toon.input import MpDevice
from toon.input.mouse import Mouse
from timeit import default_timer

device = MpDevice(Mouse())

with device:
    t1 = default_timer() + 10
    while default_timer() < t1:
        data = device.read()
        # alternatively, unpack
        # clicks, pos, scroll = device.read()
        if data.pos is not None:
            # N-D array of data (0th dim is time)
            print(data.pos)
            # time is 1D array of timestamps
            print(data.pos.time)
            print(data.pos[-1].time) # most recent timestamp

Creating a custom device is relatively straightforward, though there are a few boxes to check.

from toon.input import BaseDevice, make_obs
from ctypes import c_double

# Obs is a class that manages observations
class MyDevice(BaseDevice):
    # optional: give a hint for the buffer size (we'll allocate 1s worth of this)
    sampling_frequency = 500

    # required: each data source gets its own Obs
    # can have multiple Obs per device
    # this can either be introduced at the class level, or during __init__
    # ctype can be a python type, numpy dtype, or ctype
    Pos = make_obs('Pos', shape=(3,), ctype=float)
    RotMat = make_obs('RotMat', (3, 3), c_double) # 2D data

    # optional. Do not start device communication here, wait until `enter`
    def __init__(self):
        pass

    ## Use `enter` and `exit`, rather than `__enter__` and `__exit__`
    # optional: configure the device, start communicating
    def enter(self):
        pass

    # optional: clean up resources, close device
    def exit(self, *args):
        pass

    # required
    def read(self):
        # See demos/ for examples of sharing a time source between the processes
        time = self.clock()
        # store new data with a timestamp
        pos = self.Pos(time, (1, 2, 3))
        rotmat = self.RotMat(time, [[1, 2, 3], [4, 5, 6], [7, 8, 9]])
        # can also be explicit, i.e. `self.Returns(pos=pos, rotmat=rotmat)`
        return pos, rotmat

This device can then be passed to a toon.input.MpDevice, which preallocates the shared memory and handles other details.

A few things to be aware of for data returned by MpDevice:

  • If a device only has a single Obs, MpDevice returns a single TsArray (a numpy array with a time attribute). Otherwise, MpDevice returns a named tuple of observations, where the names are alphabetically-sorted, lowercased versions of the pre-defined Obs.
  • If the data returned by a single read is scalar (e.g. a 1D force sensor), MpDevice will drop the 1st dimension.
  • If there's no data for a given observation, None is returned. The named tuple has a method for checking all members at once (data.any()).

Other notes:

  • The returned data is a view of the local copy of the data. toon.input.TsArrays have a copy method, which may be useful if e.g. appending to a list for later concatenation.
  • Re: concatenation, there is a vstack function available in toon/input/tsarray.py, which is like numpy's version but keeps the time attribute intact.
  • If receiving batches of data when reading from the device, you can return a list of Returns (see tests/input/mockdevices.py for an example).
  • You can optionally use device.start()/device.stop() instead of a context manager.
  • You can check for remote errors at any point using device.check_error(), though this automatically happens after entering the context manager and when reading.
  • In addition to python types/dtypes/ctypes, Obs can use ctypes.Structures (see input tests or the cyberglove for examples).

Animation

This is still a work in progress, though I think it has some utility as-is. It's a port of the animation component in the Magnum framework, though lacking some of the features (e.g. Track extrapolation, proper handling of time scaling).

Example:

from time import sleep
from timeit import default_timer
import matplotlib.pyplot as plt
from toon.anim import Track, Player
# see toon/anim/easing.py for all easings available
from toon.anim.easing import linear, elastic_in_out

class Circle(object):
    x = 0
    y = 0

circle = Circle()
# list of (time, value)
keyframes = [(0.2, -0.5), (0.5, 0), (3, 0.5)]
x_track = Track(keyframes, easing=linear)
# we can reuse keyframes
y_track = Track(keyframes, easing=elastic_in_out)

player = Player()

# directly modify an attribute
player.add(x_track, 'x', obj=circle)

def y_cb(val, obj):
    obj.y = val

# modify via callback
player.add(y_track, y_cb, obj=circle)

t0 = default_timer()
player.start(t0)
vals = []
while default_timer() < t0 + 3.2:
    player.advance(default_timer())
    vals.append([circle.x, circle.y])
    sleep(1/60)

plt.plot(vals)
plt.show()

Other notes:

  • Non-numeric attributes, like color strings, can also be modified in this framework (easing is ignored).
  • The Timeline class (in toon.anim) can be used to get the time between frames, or the time since some origin time, taken at timeline.start().
  • The Player can also be used as a mixin, in which case the obj argument can be omitted from player.add() (see the demos/ folder for examples).
  • Multiple objects can be modified simultaneously by feeding a list of objects into player.add().

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

toon-0.12.4.tar.gz (26.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

toon-0.12.4-py2.py3-none-any.whl (34.6 kB view details)

Uploaded Python 2Python 3

File details

Details for the file toon-0.12.4.tar.gz.

File metadata

  • Download URL: toon-0.12.4.tar.gz
  • Upload date:
  • Size: 26.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.19.1 setuptools/40.2.0 requests-toolbelt/0.8.0 tqdm/4.26.0 CPython/3.6.6

File hashes

Hashes for toon-0.12.4.tar.gz
Algorithm Hash digest
SHA256 c8d2cbed825c07f1332f261cf335492c611567c76aadb790081da15c9e27d0cd
MD5 f9d2f3bd6fb5500fae9f72b2169fecc1
BLAKE2b-256 50809d9420d90e603f49dc26a8c8c059658e70b00d80bcfe1d49dead89e0c3d5

See more details on using hashes here.

File details

Details for the file toon-0.12.4-py2.py3-none-any.whl.

File metadata

  • Download URL: toon-0.12.4-py2.py3-none-any.whl
  • Upload date:
  • Size: 34.6 kB
  • Tags: Python 2, Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.19.1 setuptools/40.2.0 requests-toolbelt/0.8.0 tqdm/4.26.0 CPython/3.6.6

File hashes

Hashes for toon-0.12.4-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 7bb5b2867fadb5306443f55c7b0a9b88806f463822deb909a2032d5ba9e56575
MD5 d8cf34b022402bb47b7c520211e49c75
BLAKE2b-256 200d451b0941a05d116db5349a8d3f45b3846460282fbc55d98aaaa153e8469b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page