'''
======================================================================
:mod:`gridcells.analysis.registration` - Positional data registration.
======================================================================
Use the classes here to align (register) positional data of several recordings
with the specified arena coordinates.
Classes
-------
.. autosummary::
ArenaOriginRegistration
OriginRegistrationResult
'''
from __future__ import absolute_import, division, print_function
import numpy as np
from scipy.optimize import minimize
from ..core import Pair2D, Position2D
[docs]class OriginRegistrationResult(object):
'''A holder for registered data.
Contains two attributes: ``positions`` and estimated ``offsets`` in the
arena.
'''
def __init__(self, positions, offsets):
self.positions = positions
self.offsets = offsets
[docs]class ArenaOriginRegistration(object):
'''Register positional data to zero-coordinates of an arena.
The actual positional data recordings are prone to outliers. This
registration engine ensures that the positional data from different
recordings are "aligned" with respect to the arena coordinates. This is
accomplished by optimising the positional offsets with respect to the
number of outliers.
.. todo::
Deal with rotations.
'''
[docs] def __init__(self, arena=None):
'''Initialise with an ``arena`` against which to register the data.
Also use :meth:`set_arena` to change the specific
arena.
'''
self._arena = arena
[docs] def set_arena(self, arena):
'''Set the arena for registration.
All subsequent calls to :meth:`register` will be performed on this
arena.
'''
self._arena = arena
[docs] def register(self, positions):
'''Register the positional data against the current arena.
Parameters
----------
positions : :class:`~gridcells.core.common.Position2D`
Positional data.
Returns
-------
res : :class:`OriginRegistrationResult`
The result object, containing new positional data and the
determined offsets.
'''
arena_sz = self._arena.getSize()
def count_outliers(offsets):
offx = offsets[0]
offy = offsets[1]
out_idx = np.logical_or(positions.x < offx,
np.logical_or(positions.x > arena_sz.x + offx,
np.logical_or(positions.y < offy,
positions.y > arena_sz.y + offy)))
return np.count_nonzero(out_idx)
if self._arena is None:
raise InitialisationError("Arena must be set for the registration "
"process.")
offsets0 = [np.nanmean(positions.x) - arena_sz.x/2.,
np.nanmean(positions.y) - arena_sz.y/2.]
res = minimize(count_outliers, offsets0, method='Nelder-Mead')
offsets = Pair2D(res.x[0], res.x[1])
registered_pos = Position2D(positions.x - offsets.x,
positions.y - offsets.y,
positions.dt)
return OriginRegistrationResult(registered_pos, offsets)