Compare commits
3 Commits
1dd318f23e
...
e3b5d2d6cc
Author | SHA1 | Date | |
---|---|---|---|
e3b5d2d6cc | |||
94fa5e3d14 | |||
cbd0541a54 |
74
build_docs.sh
Executable file
74
build_docs.sh
Executable file
@ -0,0 +1,74 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
die() { echo "ERROR: $*"; exit 2; }
|
||||||
|
warn() { echo "WARNING: $*"; }
|
||||||
|
|
||||||
|
for cmd in mkdocs pdoc3 genbadge; do
|
||||||
|
command -v "$cmd" >/dev/null ||
|
||||||
|
warn "missing $cmd: run \`pip install $cmd\`"
|
||||||
|
done
|
||||||
|
|
||||||
|
PACKAGE="etrack"
|
||||||
|
PACKAGESRC="src/$PACKAGE"
|
||||||
|
PACKAGEROOT="$(dirname "$(realpath "$0")")"
|
||||||
|
BUILDROOT="$PACKAGEROOT/site"
|
||||||
|
|
||||||
|
# check for code coverage report:
|
||||||
|
# need to call nosetest with --with-coverage --cover-html --cover-xml
|
||||||
|
HAS_COVER=false
|
||||||
|
test -d cover && HAS_COVER=true
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Clean up documentation of $PACKAGE"
|
||||||
|
echo
|
||||||
|
|
||||||
|
rm -rf "$BUILDROOT" 2> /dev/null || true
|
||||||
|
mkdir -p "$BUILDROOT"
|
||||||
|
|
||||||
|
if command -v mkdocs >/dev/null; then
|
||||||
|
echo
|
||||||
|
echo "Building general documentation for $PACKAGE"
|
||||||
|
echo
|
||||||
|
|
||||||
|
cd "$PACKAGEROOT"
|
||||||
|
cp .mkdocs.yml mkdocs-tmp.yml
|
||||||
|
if $HAS_COVER; then
|
||||||
|
echo " - Coverage: 'cover/index.html'" >> mkdocs-tmp.yml
|
||||||
|
fi
|
||||||
|
mkdir -p docs
|
||||||
|
sed -e 's|docs/||; /\[Documentation\]/d; /\[API Reference\]/d' README.md > docs/index.md
|
||||||
|
mkdocs build --config-file mkdocs.yml --site-dir "$BUILDROOT"
|
||||||
|
rm mkdocs-tmp.yml docs/index.md
|
||||||
|
cd - > /dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
if $HAS_COVER; then
|
||||||
|
echo
|
||||||
|
echo "Copy code coverage report and generate badge for $PACKAGE"
|
||||||
|
echo
|
||||||
|
|
||||||
|
cd "$PACKAGEROOT"
|
||||||
|
cp -r cover "$BUILDROOT/"
|
||||||
|
genbadge coverage -i coverage.xml
|
||||||
|
# https://smarie.github.io/python-genbadge/
|
||||||
|
mv coverage-badge.svg site/coverage.svg
|
||||||
|
cd - > /dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v pdoc3 >/dev/null; then
|
||||||
|
echo
|
||||||
|
echo "Building API reference docs for $PACKAGE"
|
||||||
|
echo
|
||||||
|
|
||||||
|
cd "$PACKAGEROOT"
|
||||||
|
pdoc3 --html --config latex_math=True --config sort_identifiers=False --output-dir "$BUILDROOT/api-tmp" $PACKAGESRC
|
||||||
|
mv "$BUILDROOT/api-tmp/$PACKAGE" "$BUILDROOT/api"
|
||||||
|
rmdir "$BUILDROOT/api-tmp"
|
||||||
|
cd - > /dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Done. Docs in:"
|
||||||
|
echo
|
||||||
|
echo " file://$BUILDROOT/index.html"
|
||||||
|
echo
|
35
docs/etrack.md
Normal file
35
docs/etrack.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# E-Fish tracking
|
||||||
|
|
||||||
|
Tool for easier handling of tracking results.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### 1. Clone git repository
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git clone https://whale.am28.uni-tuebingen.de/git/jgrewe/efish_tracking.git
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Change into directory
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cd efish_tracking
|
||||||
|
````
|
||||||
|
|
||||||
|
### 3. Install with pip
|
||||||
|
|
||||||
|
```shell
|
||||||
|
pip3 install -e . --user
|
||||||
|
```
|
||||||
|
|
||||||
|
The ```-e``` installs the package in an *editable* model that you do not need to reinstall whenever you pull upstream changes.
|
||||||
|
|
||||||
|
If you leave away the ```--user``` the package will be installed system-wide.
|
||||||
|
|
||||||
|
## TrackingResults
|
||||||
|
|
||||||
|
Is a class that wraps around the *.h5 files written by DeepLabCut
|
||||||
|
|
||||||
|
## ImageMarker
|
||||||
|
|
||||||
|
Class that allows for creating MarkerTasks to get specific positions in a video.
|
4
docs/trackingdata.md
Normal file
4
docs/trackingdata.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# TrackingData
|
||||||
|
|
||||||
|
Class that represents the position data associated with one noe/bodypart.
|
||||||
|
|
17
mkdocs.yml
Normal file
17
mkdocs.yml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
site_name: etrack
|
||||||
|
|
||||||
|
repo_url: https://github.com/bendalab/etrack/
|
||||||
|
|
||||||
|
edit_uri: ""
|
||||||
|
|
||||||
|
site_author: Jan Grewe jan.grewe@g-node.org
|
||||||
|
|
||||||
|
theme: readthedocs
|
||||||
|
|
||||||
|
nav:
|
||||||
|
- Home: 'index.md'
|
||||||
|
- 'User guide':
|
||||||
|
- 'etrack': 'etrack.md'
|
||||||
|
- 'TrackingData' : 'trackingdata.md'
|
||||||
|
- 'Code':
|
||||||
|
- API reference: 'api/index.html'
|
@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
name = "etrack"
|
name = "etrack"
|
||||||
dynamic = ["version"]
|
dynamic = ["version"]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nixio>=1.5",
|
"hdf5",
|
||||||
"nixtrack",
|
"nixtrack",
|
||||||
"numpy",
|
"numpy",
|
||||||
"matplotlib",
|
"matplotlib",
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
""" etrack package for easier reading and handling of efish tracking data.
|
||||||
|
|
||||||
|
Copyright © 2024, Jan Grewe
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted under the terms of the BSD License. See LICENSE file in the root of the Project.
|
||||||
|
"""
|
||||||
from .image_marker import ImageMarker, MarkerTask
|
from .image_marker import ImageMarker, MarkerTask
|
||||||
from .tracking_result import TrackingResult, coordinate_transformation
|
from .tracking_result import TrackingResult, coordinate_transformation
|
||||||
from .arena import Arena, Region
|
from .arena import Arena, Region
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
|
"""
|
||||||
|
Classes to construct the arena in which the animals were tracked.
|
||||||
|
"""
|
||||||
import logging
|
import logging
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
import matplotlib.patches as patches
|
import matplotlib.patches as patches
|
||||||
|
|
||||||
from skimage.draw import disk
|
from skimage.draw import disk
|
||||||
|
|
||||||
from .util import RegionShape, AnalysisType, Illumination
|
from .util import RegionShape, AnalysisType, Illumination
|
||||||
from IPython import embed
|
|
||||||
|
|
||||||
|
|
||||||
class Region(object):
|
class Region(object):
|
||||||
@ -119,7 +120,14 @@ class Region(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def position(self):
|
def position(self):
|
||||||
"""Returns the position and extent of the region as 4-tuple, (x, y, width, height)"""
|
"""
|
||||||
|
Get the position of the arena.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
tuple
|
||||||
|
A tuple containing the x-coordinate, y-coordinate, width, and height of the arena.
|
||||||
|
"""
|
||||||
x = self._min_extent[0]
|
x = self._min_extent[0]
|
||||||
y = self._min_extent[1]
|
y = self._min_extent[1]
|
||||||
width = self._max_extent[0] - self._min_extent[0]
|
width = self._max_extent[0] - self._min_extent[0]
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import matplotlib.pyplot as plt
|
"""
|
||||||
import cv2
|
Module that defines the ImageMarker and MarkerTask classes to manually mark things in individual images.
|
||||||
|
"""
|
||||||
import os
|
import os
|
||||||
|
import cv2
|
||||||
import sys
|
import sys
|
||||||
from IPython import embed
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
|
||||||
class ImageMarker:
|
class ImageMarker:
|
||||||
|
|
||||||
|
@ -6,6 +6,9 @@
|
|||||||
# Redistribution and use in source and binary forms, with or without
|
# Redistribution and use in source and binary forms, with or without
|
||||||
# modification, are permitted under the terms of the BSD License. See
|
# modification, are permitted under the terms of the BSD License. See
|
||||||
# LICENSE file in the root of the Project.
|
# LICENSE file in the root of the Project.
|
||||||
|
"""
|
||||||
|
Package info.
|
||||||
|
"""
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
"""
|
||||||
|
Reader classes for DeepLabCut, or SLEAP written data files.
|
||||||
|
"""
|
@ -7,7 +7,7 @@ from ..tracking_data import TrackingData
|
|||||||
|
|
||||||
|
|
||||||
class DLCReader(object):
|
class DLCReader(object):
|
||||||
|
"""Class that represents the tracking data stored in a DeepLabCut hdf5 file."""
|
||||||
def __init__(self, results_file, crop=(0, 0)) -> None:
|
def __init__(self, results_file, crop=(0, 0)) -> None:
|
||||||
"""
|
"""
|
||||||
If the video data was cropped before tracking and the tracked positions are with respect to the cropped images, we may want to correct for this to convert the data back to absolute positions in the video frame.
|
If the video data was cropped before tracking and the tracked positions are with respect to the cropped images, we may want to correct for this to convert the data back to absolute positions in the video frame.
|
||||||
|
@ -9,7 +9,8 @@ from IPython import embed
|
|||||||
|
|
||||||
|
|
||||||
class NixtrackData(object):
|
class NixtrackData(object):
|
||||||
|
"""Wrapper around a nix data file that has been written accorind to the nixtrack model (https://github.com/bendalab/nixtrack)
|
||||||
|
"""
|
||||||
def __init__(self, filename, crop=(0, 0)) -> None:
|
def __init__(self, filename, crop=(0, 0)) -> None:
|
||||||
"""
|
"""
|
||||||
If the video data was cropped before tracking and the tracked positions are with respect to the cropped images, we may want to correct for this to convert the data back to absolute positions in the video frame.
|
If the video data was cropped before tracking and the tracked positions are with respect to the cropped images, we may want to correct for this to convert the data back to absolute positions in the video frame.
|
||||||
@ -37,28 +38,93 @@ class NixtrackData(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def filename(self):
|
def filename(self):
|
||||||
|
"""
|
||||||
|
Returns the name of the file associated with the NixtrackData object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The name of the file.
|
||||||
|
"""
|
||||||
return self._file_name
|
return self._file_name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bodyparts(self):
|
def bodyparts(self):
|
||||||
|
"""
|
||||||
|
Returns the bodyparts of the dataset.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: A list of bodyparts.
|
||||||
|
"""
|
||||||
return self._dataset.nodes
|
return self._dataset.nodes
|
||||||
|
|
||||||
def _correct_cropping(self, orgx, orgy):
|
def _correct_cropping(self, orgx, orgy):
|
||||||
|
"""
|
||||||
|
Corrects the coordinates based on the cropping values, If it cropping was done during tracking.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
orgx (int): The original x-coordinate.
|
||||||
|
orgy (int): The original y-coordinate.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple: A tuple containing the corrected x and y coordinates.
|
||||||
|
"""
|
||||||
x = orgx + self._crop[0]
|
x = orgx + self._crop[0]
|
||||||
y = orgy + self._crop[1]
|
y = orgy + self._crop[1]
|
||||||
return x, y
|
return x, y
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fps(self):
|
||||||
|
"""Property that holds frames per second of the original video.
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
int : the frames of second
|
||||||
|
"""
|
||||||
|
return self._dataset.fps
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tracks(self):
|
def tracks(self):
|
||||||
return self._dataset.tracks
|
"""
|
||||||
|
Returns a list of tracks from the dataset.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: A list of tracks.
|
||||||
|
"""
|
||||||
|
return [t[0] for t in self._dataset.tracks]
|
||||||
|
|
||||||
|
def track_data(self, bodypart=0, track=-1, fps=None):
|
||||||
|
"""
|
||||||
|
Retrieve tracking data for a specific body part and track.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
bodypart : int or str
|
||||||
|
Index or name of the body part to retrieve tracking data for.
|
||||||
|
track : int or str
|
||||||
|
Index of the track to retrieve tracking data for.
|
||||||
|
fps : float
|
||||||
|
Frames per second of the tracking data. If not provided, it will be retrieved from the dataset.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
TrackingData: An object containing the x and y positions, time, score, body part name, and frames per second.
|
||||||
|
|
||||||
def track(self, bodypart=0, fps=None):
|
Raises
|
||||||
|
------
|
||||||
|
ValueError: If the body part or track is not valid.
|
||||||
|
"""
|
||||||
if isinstance(bodypart, nb.Number):
|
if isinstance(bodypart, nb.Number):
|
||||||
bp = self.bodyparts[bodypart]
|
bp = self.bodyparts[bodypart]
|
||||||
elif isinstance(bodypart, (str)) and bodypart in self.bodyparts:
|
elif isinstance(bodypart, (str)) and bodypart in self.bodyparts:
|
||||||
bp = bodypart
|
bp = bodypart
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Body part {bodypart} is not a tracked node!")
|
raise ValueError(f"Body part {bodypart} is not a tracked node!")
|
||||||
|
|
||||||
|
if track not in self.tracks:
|
||||||
|
raise ValueError(f"Track {track} is not a valid track name!")
|
||||||
|
if not isinstance(track, (list, tuple)):
|
||||||
|
track = [track]
|
||||||
|
elif isinstance(track, int):
|
||||||
|
track = [self.tracks[track]]
|
||||||
|
|
||||||
if fps is None:
|
if fps is None:
|
||||||
fps = self._dataset.fps
|
fps = self._dataset.fps
|
||||||
|
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
"""
|
||||||
|
Module that defines the TrackingData class that wraps the position data for a given node/bodypart that has been tracked.
|
||||||
|
"""
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
@ -6,23 +10,37 @@ class TrackingData(object):
|
|||||||
These data are the x, and y-positions, the time at which the agent was detected, and the quality associated with the position estimation.
|
These data are the x, and y-positions, the time at which the agent was detected, and the quality associated with the position estimation.
|
||||||
TrackingData contains these data and offers a few functions to work with it.
|
TrackingData contains these data and offers a few functions to work with it.
|
||||||
Using the 'quality_threshold', 'temporal_limits', or the 'position_limits' data can be filtered (see filter_tracks function).
|
Using the 'quality_threshold', 'temporal_limits', or the 'position_limits' data can be filtered (see filter_tracks function).
|
||||||
The 'interpolate' function allows to fill up the gaps that be result from filtering with linearly interpolated data points.
|
The 'interpolate' function allows to fill up the gaps that may result from filtering with linearly interpolated data points.
|
||||||
|
|
||||||
More may follow...
|
More may follow...
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, x, y, time, quality, node="", fps=None,
|
||||||
self,
|
quality_threshold=None, temporal_limits=None, position_limits=None) -> None:
|
||||||
x,
|
"""
|
||||||
y,
|
Initialize a TrackingData object.
|
||||||
time,
|
|
||||||
quality,
|
Parameters
|
||||||
node="",
|
----------
|
||||||
fps=None,
|
x : float
|
||||||
quality_threshold=None,
|
The x-coordinates of the tracking data.
|
||||||
temporal_limits=None,
|
y : float
|
||||||
position_limits=None,
|
The y-coordinates of the tracking data.
|
||||||
) -> None:
|
time : float
|
||||||
|
The time vector associated with the x-, and y-coordinates.
|
||||||
|
quality : float
|
||||||
|
The quality score associated with the position estimates.
|
||||||
|
node : str, optional
|
||||||
|
The node name associated with the data. Default is an empty string.
|
||||||
|
fps : float, optional
|
||||||
|
The frames per second of the tracking data. Default is None.
|
||||||
|
quality_threshold : float, optional
|
||||||
|
The quality threshold for the tracking data. Default is None.
|
||||||
|
temporal_limits : tuple, optional
|
||||||
|
The temporal limits for the tracking data. Default is None.
|
||||||
|
position_limits : tuple, optional
|
||||||
|
The position limits for the tracking data. Default is None.
|
||||||
|
"""
|
||||||
self._orgx = x
|
self._orgx = x
|
||||||
self._orgy = y
|
self._orgy = y
|
||||||
self._orgtime = time
|
self._orgtime = time
|
||||||
@ -61,11 +79,19 @@ class TrackingData(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def quality_threshold(self):
|
def quality_threshold(self):
|
||||||
|
"""Property that holds the quality filter setting.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
float : the quality threshold
|
||||||
|
"""
|
||||||
return self._threshold
|
return self._threshold
|
||||||
|
|
||||||
@quality_threshold.setter
|
@quality_threshold.setter
|
||||||
def quality_threshold(self, new_threshold):
|
def quality_threshold(self, new_threshold):
|
||||||
"""Setter of the quality threshold that should be applied when filterin the data. Setting this to None removes the quality filter.
|
"""Setter of the quality threshold that should be applied when filtering the data. Setting this to None removes the quality filter.
|
||||||
|
|
||||||
|
Data points that have a quality score below the given threshold are discarded.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
@ -76,11 +102,18 @@ class TrackingData(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def position_limits(self):
|
def position_limits(self):
|
||||||
|
"""
|
||||||
|
Get the position limits of the tracking data.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple: A 4-tuple containing the start x, and y positions, width and height limits.
|
||||||
|
"""
|
||||||
return self._position_limits
|
return self._position_limits
|
||||||
|
|
||||||
@position_limits.setter
|
@position_limits.setter
|
||||||
def position_limits(self, new_limits):
|
def position_limits(self, new_limits):
|
||||||
"""Sets the limits for the position filter. 'new_limits' must be a 4-tuple of the form (x0, y0, width, height). If None, the limits will be removed.
|
"""Sets the limits for the position filter. 'new_limits' must be a 4-tuple of the form (x0, y0, width, height). If None, the limits will be removed.
|
||||||
|
Data points outside the position limits are discarded.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
@ -101,16 +134,30 @@ class TrackingData(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def temporal_limits(self):
|
def temporal_limits(self):
|
||||||
|
"""
|
||||||
|
Get the temporal limits of the tracking data.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple: A tuple containing the start and end time of the tracking data.
|
||||||
|
"""
|
||||||
return self._time_limits
|
return self._time_limits
|
||||||
|
|
||||||
@temporal_limits.setter
|
@temporal_limits.setter
|
||||||
def temporal_limits(self, new_limits):
|
def temporal_limits(self, new_limits):
|
||||||
"""Limits for temporal filter. The limits must be a 2-tuple of start and end time. Setting this to None removes the filter.
|
"""Limits for temporal filter. The limits must be a 2-tuple of start and end time. Setting this to None removes the filter.
|
||||||
|
Data points the are associated with times outside the limits are discarded.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
new_limits : 2-tuple
|
new_limits : 2-tuple
|
||||||
The new limits in the form (start, end) given in seconds.
|
The new limits in the form (start, end) given in seconds.
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
None
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
ValueError if the limits are not valid.
|
||||||
"""
|
"""
|
||||||
if new_limits is not None and not (
|
if new_limits is not None and not (
|
||||||
isinstance(new_limits, (tuple, list)) and len(new_limits) == 2
|
isinstance(new_limits, (tuple, list)) and len(new_limits) == 2
|
||||||
@ -166,7 +213,7 @@ class TrackingData(object):
|
|||||||
self._quality = self._quality[indices]
|
self._quality = self._quality[indices]
|
||||||
|
|
||||||
def positions(self):
|
def positions(self):
|
||||||
"""Returns the filtered data (if filters have been applied).
|
"""Returns the filtered data (if filters have been applied, otherwise the original data).
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
@ -184,7 +231,7 @@ class TrackingData(object):
|
|||||||
def speed(self, x=None, y=None, t=None):
|
def speed(self, x=None, y=None, t=None):
|
||||||
""" Returns the agent's speed as a function of time and position. The speed estimation is associated to the time/position between two sample points. If any of the arguments is not provided, the function will use the x,y coordinates that are stored within the object, otherwise, if all are provided, the user-provided values will be used.
|
""" Returns the agent's speed as a function of time and position. The speed estimation is associated to the time/position between two sample points. If any of the arguments is not provided, the function will use the x,y coordinates that are stored within the object, otherwise, if all are provided, the user-provided values will be used.
|
||||||
|
|
||||||
Since the velocities are estimated from the difference between two sample points the returned velocities are assigned to positions and times between the respective sampled positions/times.
|
Since the velocities are estimated from the difference between two sample points the returned velocities and positions are assigned to positions and times between the respective sampled positions/times.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
@ -204,11 +251,12 @@ class TrackingData(object):
|
|||||||
The position
|
The position
|
||||||
"""
|
"""
|
||||||
if x is None or y is None or t is None:
|
if x is None or y is None or t is None:
|
||||||
x = self._x
|
x = self._x.copy()
|
||||||
y = self._y
|
y = self._y.copy()
|
||||||
t = self._time
|
t = self._time.copy()
|
||||||
speed = np.sqrt(np.diff(x)**2 + np.diff(y)**2) / np.diff(t)
|
dt = np.diff(t)
|
||||||
t = t[:-1] + np.diff(t) / 2
|
speed = np.sqrt(np.diff(x)**2 + np.diff(y)**2) / dt
|
||||||
|
t = t[:-1] + dt / 2
|
||||||
x = x[:-1] + np.diff(x) / 2
|
x = x[:-1] + np.diff(x) / 2
|
||||||
y = y[:-1] + np.diff(y) / 2
|
y = y[:-1] + np.diff(y) / 2
|
||||||
|
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
"""
|
||||||
|
Module containing utility functions and enum classes.
|
||||||
|
"""
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
class Illumination(Enum):
|
class Illumination(Enum):
|
||||||
@ -22,11 +25,26 @@ class RegionShape(Enum):
|
|||||||
|
|
||||||
|
|
||||||
class AnalysisType(Enum):
|
class AnalysisType(Enum):
|
||||||
|
"""
|
||||||
|
Enumeration representing different types of analysis used when analyzing whether
|
||||||
|
positions fall into a given region.
|
||||||
|
|
||||||
|
Possible types:
|
||||||
|
AnalysisType.Full: considers both, the x- and the y-coordinates
|
||||||
|
AnalysisType.CollapseX: consider only the x-coordinates
|
||||||
|
AnalysisType.CollapseY: consider only the y-coordinates
|
||||||
|
"""
|
||||||
Full = 0
|
Full = 0
|
||||||
CollapseX = 1
|
CollapseX = 1
|
||||||
CollapseY = 2
|
CollapseY = 2
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
|
"""
|
||||||
|
Returns the string representation of the analysis type.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The name of the analysis type.
|
||||||
|
"""
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
class PositionType(Enum):
|
class PositionType(Enum):
|
||||||
|
BIN
test/2022lepto01_converted_2024.03.27_0.mp4.nix
Normal file
BIN
test/2022lepto01_converted_2024.03.27_0.mp4.nix
Normal file
Binary file not shown.
32
test/test_nixtrackio.py
Normal file
32
test/test_nixtrackio.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import pytest
|
||||||
|
import etrack as et
|
||||||
|
|
||||||
|
from IPython import embed
|
||||||
|
|
||||||
|
dataset = "test/2022lepto01_converted_2024.03.27_0.mp4.nix"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def nixtrack_data():
|
||||||
|
# Create a NixTrackData object with some test data
|
||||||
|
return et.NixtrackData(dataset)
|
||||||
|
|
||||||
|
|
||||||
|
def test_basics(nixtrack_data):
|
||||||
|
assert nixtrack_data.filename == dataset
|
||||||
|
assert len(nixtrack_data.bodyparts) == 5
|
||||||
|
assert len(nixtrack_data.tracks) == 2
|
||||||
|
assert nixtrack_data.fps == 25
|
||||||
|
|
||||||
|
|
||||||
|
def test_trackingdata(nixtrack_data):
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
nixtrack_data.track_data(bodypart="test")
|
||||||
|
nixtrack_data.track_data(track="fish")
|
||||||
|
|
||||||
|
assert nixtrack_data.track_data("center") is not None
|
||||||
|
assert nixtrack_data.track_data("center", "none") is not None
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
pytest.main()
|
Loading…
Reference in New Issue
Block a user