Feature#
- class deeptrack.features.Feature(_input: Any | None = None, **kwargs: Any)#
Bases:
DeepTrackNodeBase feature class.
Features define the data generation and transformation process.
All features operate on lists of data, often lists of images. Most features, such as noise, apply a tranformation to all data in the list. The transformation can be additive, such as adding some Gaussian noise or a background illumination to images, or non-additive, such as introducing Poisson noise or performing a low-pass filter. The transformation is defined by the .get(data, **kwargs) method, which all implementations of the Feature class need to define. This method operates on a single data at a time.
Whenever a feature is initialized, it wraps all keyword arguments passed to the constructor as Property objects, and stores them in the .properties attribute as a PropertyDict.
When a feature is resolved, the current value of each property is sent as input to the .get() method.
Computational Backends and Data Types
The Feature class also provides mechanisms for managing numerical types and computational backends.
Supported backends include NumPy and PyTorch. The active backend is determined at initialization and stored in the ._backend attribute, which is used internally to control how computations are executed. The backend can be switched using the .numpy() and .torch() methods.
Numerical types used in computation (float, int, complex, and bool) can be configured using the .dtype() method. The chosen types are retrieved via the properties .float_dtype, .int_dtype, .complex_dtype, and .bool_dtype. These are resolved dynamically using the backend’s internal type resolution system and are used in downstream computations.
The computational device (e.g., “cpu” or a specific GPU) is managed through the .to() method and accessed via the .device property. This is especially relevant for PyTorch backends, which support GPU acceleration.
Parameters#
- data: Any, optional
The input data for the feature. If left empty, no initial input is set. It is most commonly a NumPy array, a PyTorch tensor, or a list of NumPy arrays or PyTorch tensors; however, it can be anything.
- **kwargs: Any
Keyword arguments to configure the feature. Each keyword argument is wrapped as a Property and added to the properties attribute, allowing dynamic sampling and parameterization during the feature’s execution. These properties are passed to the get() method when a feature is resolved.
Attributes#
- properties: PropertyDict
A dictionary containing all keyword arguments passed to the constructor, wrapped as instances of Property. The properties can dynamically sampled values during pipeline execution. A sampled copy of this dictionary is passed to the .get() function and appended to the properties of the output.
- _input: DeepTrackNode
A node representing the input data for the feature. It is most commonly a NumPy array, PyTorch tensor, or a list of NumPy arrays or PyTorch tensors; however, it can be anything. It supports lazy evaluation and graph traversal.
- _random_seed: DeepTrackNode
A node representing the feature’s random seed. This allows for deterministic behavior when generating random elements, and ensures reproducibility during evaluation.
- arguments: Feature or None
An optional feature whose properties are bound to this feature. This allows dynamic property sharing and centralized parameter management in complex pipelines.
- __list_merge_strategy__: int
Specifies how the output of .get(data, **kwargs) is merged with the current _input. Options include: - MERGE_STRATEGY_OVERRIDE (0, default): _input is replaced by the
new output.
MERGE_STRATEGY_APPEND (1): The output is appended to the end of _input.
- __distributed__: bool
Determines whether .get(image, **kwargs) is applied to each element of the input list independently (__distributed__ = True) or to the list as a whole (__distributed__ = False).
- __conversion_table__: ConversionTable
Defines the unit conversions used by the feature to convert its properties into the desired units.
- float_dtype: np.dtype
The data type of the float numbers.
- int_dtype: np.dtype
The data type of the integer numbers.
- complex_dtype: np.dtype
The data type of the complex numbers.
- bool_dtype: np.dtype
The data type of the boolean numbers.
- device: str or torch.device
The device on which the feature is executed.
- _backend: “numpy” or “torch”
The computational backend.
Methods#
- get(data, **kwargs) -> Any
Abstract method that defines how the feature transforms the input data. The input is most commonly a NumPy array or a PyTorch tensor, but it can be anything.
- __call__(data_list, _ID, **kwargs) -> Any
Executes the feature or pipeline on the input and applies property overrides from kwargs.
- resolve(data_list, _ID, **kwargs) -> Any
Alias of __call__().
- to_sequential(**kwargs) -> Feature
Converts a feature to be resolved as a sequence.
- torch(device, recursive) -> Feature
Sets the backend to PyTorch.
- numpy(recursice) -> Feature
Sets the backend to NumPy.
- get_backend() -> “numpy” or “torch”
Returns the current backend of the feature.
- dtype(float, int, complex, bool) -> Feature
Sets the dtype to be used during evaluation.
- to(device) -> Feature
Sets the device to be used during evaluation.
- batch(batch_size) -> tuple
Batches the feature for repeated execution.
- action(_ID) -> Any or list[Any]
Implements the core logic to create or transform the input(s).
- update() -> Feature
Refreshes the feature to create a new output.
- new(data_list, _ID, **kwargs) -> Any
Resets and recomputes the feature output.
- add_feature(feature) -> Feature
Adds a feature to the dependency graph of this one.
- seed(updated_seed, _ID) -> int
Sets the random seed for the feature, ensuring deterministic behavior.
- bind_arguments(arguments) -> Feature
Binds another feature’s properties as arguments to this feature.
- plot(input_image, resolve_kwargs, interval, **kwargs) -> Any
Visualizes the output of the feature when it is an image.
Private and internal methods. _normalize(**properties) -> dict[str, Any]
Normalizes the properties of the feature.
- _process_properties(propertydict) -> dict[str, Any]
Preprocesses the input properties before calling the get method.
- _format_input(data_list, **kwargs) -> list[Any]
Formats the input data for the feature.
- _process_and_get(data_list, **kwargs) -> list[Any]
Calls the .get() method according to the __distributed__ attribute.
- _activate_sources(x) -> None
Activates sources in the input data.
- __getattr__(key) -> Any
Provides custom attribute access for the Feature class.
- __iter__() -> Feature
Returns an iterator for the feature.
- __next__() -> Any
Return the next element iterating over the feature.
- __rshift__(other) -> Feature
Allows chaining of features.
- __rrshift__(other) -> Feature
Allows right chaining of features.
- __add__(other) -> Feature
Overrides add operator.
- __radd__(other) -> Feature
Overrides right add operator.
- __sub__(other) -> Feature
Overrides subtraction operator.
- __rsub__(other) -> Feature
Overrides right subtraction operator.
- __mul__(other) -> Feature
Overrides multiplication operator.
- __rmul__(other) -> Feature
Overrides right multiplication operator.
- __truediv__(other) -> Feature
Overrides division operator.
- __rtruediv__(other) -> Feature
Overrides right division operator.
- __floordiv__(other) -> Feature
Overrides floor division operator.
- __rfloordiv__(other) -> Feature
Overrides right floor division operator.
- __pow__(other) -> Feature
Overrides power operator.
- __rpow__(other) -> Feature
Overrides right power operator.
- __gt__(other) -> Feature
Overrides greater than operator.
- __rgt__(other) -> Feature
Overrides right greater than operator.
- __lt__(other) -> Feature
Overrides less than operator.
- __rlt__(other) -> Feature
Overrides right less than operator.
- __le__(other) -> Feature
Overrides less than or equal to operator.
- __rle__(other) -> Feature
Overrides right less than or equal to operator.
- __ge__(other) -> Feature
Overrides greater than or equal to operator.
- __rge__(other) -> Feature
Overrides right greater than or equal to operator.
- __xor__(other) -> Feature
Overrides XOR operator.
- __and__(other) -> Feature
Overrides and operator.
- __rand__(other) -> Feature
Overrides right and operator.
- __getitem__(key) -> Feature
Allows direct slicing of the data.
Examples#
>>> import deeptrack as dt
Define and evaluate a simple feature
>>> import numpy as np >>> >>> feature = dt.Value(np.array([1, 2, 3])) >>> result = feature() >>> result array([1, 2, 3])
Chain features using ‘>>’
>>> pipeline = dt.Value(np.array([1, 2, 3])) >> dt.Add(2) >>> pipeline() array([3, 4, 5])
Use arithmetic operators
>>> feature = dt.Value(np.array([1, 2, 3])) >>> result = (feature + 1) * 2 - 1 >>> result() array([3, 5, 7])
This is equivalent to chaining with Add, Multiply, and Subtract.
Evaluate a dynamic feature using `.update()` or `.new()`
>>> feature = dt.Value(lambda: np.random.rand()) >>> output1 = feature() >>> output1 0.9938966963707441
>>> output2 = feature() # Cached result >>> output2 0.9938966963707441
>>> feature.update() >>> output3 = feature() # New sample >>> output3 0.3874078815170007
>>> output4 = feature.new() # Combine update and resolve >>> output4 0.28477040978587476
Generate a batch of outputs
>>> feature = dt.Value(lambda: np.random.rand()) + 1 >>> batch = feature.batch(batch_size=3) >>> batch (array([1.6888222 , 1.88422131, 1.90027316]),)
Switch computational backend to torch
>>> import torch >>> >>> feature = dt.Add(b=5).torch() >>> input_tensor = torch.tensor([1.0, 2.0]) >>> feature(input_tensor) tensor([6., 7.])
Use `.seed()` for reproducibility
>>> feature = dt.Value(lambda: np.random.randint(0, 100)) >>> seed = feature.seed() >>> v1 = feature.new() >>> v1 76
>>> feature.seed(seed) >>> v2 = feature.new() >>> v2 76
Sequential feature with evolving property
>>> def rotate(sequence_length, previous_value): ... return previous_value + 2 * np.pi / sequence_length
>>> rotating = dt.Ellipse( ... position=(16, 16), ... radius=(1.5e-6, 1e-6), ... rotation=0, ... ).to_sequential(rotation=rotate)
>>> frames = dt.Sequence(rotating, sequence_length=5).update() >>> images = frames() >>> len(images) 5
Bind dynamic arguments across multiple features
>>> arguments = dt.Arguments(frequency=1, amplitude=2) >>> wave = ( ... dt.Value( ... value=lambda freq: np.linspace(0, 2 * np.pi * freq, 100), ... freq=arguments.frequency, ... ) ... >> np.sin ... >> dt.Multiply( ... b=lambda amp: amp, ... amp=arguments.amplitude, ... ) ... ) >>> wave.bind_arguments(arguments)
>>> from matplotlib import pyplot as plt >>> >>> plt.plot(wave()) >>> plt.show()
>>> plt.plot(wave(frequency=2, amplitude=1)) >>> plt.show()
Attributes Summary
The dtype of the boolean numbers.
The dtype of the complex numbers.
The device to be used during evaluation.
The dtype of the float numbers.
The dtype of the integer numbers.
Methods Summary
__call__([data_list, _ID])Execute the feature or pipeline.
add_feature(feature)Add a feature to the dependecy graph of this one.
batch([batch_size])Batch the feature.
bind_arguments(arguments)Bind another feature’s properties as arguments to this feature.
dtype([float, int, complex, bool])Set the dtypes to be used during evaluation.
get(data[, _ID])Transform input data (abstract method).
Get the current backend of the feature.
new([data_list, _ID])Reset and recompute the feature output for the given _ID.
numpy([recursive])Set the backend to numpy.
plot([input_image, resolve_kwargs, interval])Visualize the output of the feature.
resolve([data_list, _ID])Execute the feature or pipeline.
seed([updated_seed, _ID])Seed all random number generators for reproducibility.
to(device)Set the device to be used during evaluation.
to_sequential(**kwargs)Convert a feature to be resolved as a sequence.
torch([device, recursive])Set the backend to torch.
update(**global_arguments)Refresh the feature to generate a new output.
Attributes Documentation
- bool_dtype#
The dtype of the boolean numbers.
- complex_dtype#
The dtype of the complex numbers.
- device#
The device to be used during evaluation.
- float_dtype#
The dtype of the float numbers.
- int_dtype#
The dtype of the integer numbers.
Methods Documentation
- __call__(data_list: Any | None = None, _ID: tuple[int, ...] = (), **kwargs: Any) Any#
Execute the feature or pipeline.
The .__call__() method executes the feature or pipeline on the provided input data and updates the computation graph if necessary. It overrides properties using the keyword arguments.
The actual computation is performed by calling the parent .__call__() method in the DeepTrackNode class, which manages lazy evaluation and caching.
Parameters#
- data_list: Any, optional
The input data to the feature or pipeline. It is most commonly a list of NumPy arrays or PyTorch tensors, but it can be anything. Defaults to None, in which case the feature uses the previous set of input values or propagates properties.
- **kwargs: Any
Additional parameters passed to the pipeline. These override properties with matching names. For example, calling feature(x, value=4) executes feature on the input x while setting the property value to 4. All features in a pipeline are affected by these overrides.
Returns#
- Any
The output of the feature or pipeline after execution. This is typically a list of NumPy arrays or PyTorch tensors, but it can be anything.
Examples#
>>> import deeptrack as dt
Define a feature:
>>> feature = dt.Add(b=2)
Call this feature with an input:
>>> import numpy as np >>> >>> feature(np.array([1, 2, 3])) array([3, 4, 5])
Execute the feature with previously set input:
>>> feature() # Uses stored input array([3, 4, 5])
Execute the feature with new input:
>>> feature(np.array([10, 20, 30])) # Uses new input array([12, 22, 32])
Override a property:
>>> feature(np.array([10, 20, 30]), b=1) array([11, 21, 31])
- add_feature(feature: Feature) Feature#
Add a feature to the dependecy graph of this one.
This method establishes a dependency relationship by registering the provided feature as a dependency of the current feature. This ensures that its evaluation and property resolution are included in the current feature’s computation graph.
Internally, it calls feature.add_child(self), which automatically handles graph integration and triggers recomputation if necessary.
This is often used to define explicit data dependencies or to ensure side-effect features are computed when this feature is resolved.
Parameters#
- feature: Feature
The feature to add as a dependency.
Returns#
- Feature
The newly added feature (for chaining).
Examples#
>>> import deeptrack as dt
Define the main feature that adds a constant to the input:
>>> feature = dt.Add(b=2)
Define a side-effect feature:
>>> dependency = dt.Value(value=42)
Register the dependency so its state becomes part of the graph:
>>> feature.add_feature(dependency)
Execute the main feature on an input array:
>>> import numpy as np >>> >>> result = feature(np.array([1, 2, 3])) >>> result array([3, 4, 5])
Note that the dependency does not affect the result directly, but it will be tracked and updated as part of the pipeline’s evaluation graph. This can be useful if the dependency affects any parameters of the main feature.
- batch(batch_size: int = 32) tuple#
Batch the feature.
This method produces a batch of outputs by repeatedly calling .new().
Parameters#
- batch_size: int, optional
The number of times to sample or generate data. Defaults to 32.
Returns#
- tuple
A tuple where each element corresponds to one component of the output. If the outputs are NumPy arrays or PyTorch tensors, each element is a stacked array.
Examples#
>>> import deeptrack as dt
Define a feature that adds a random value to a fixed array:
>>> import numpy as np >>> >>> feature = ( ... dt.Value(value=np.array([[-1, 1]])) ... >> dt.Add(b=lambda: np.random.rand()) ... )
Evaluate the feature once:
>>> output = feature() >>> output array([[-0.77378939, 1.22621061]])
Generate a batch of outputs:
>>> batch = feature.batch(batch_size=3) >>> batch (array([[-0.2375814 , 1.7624186 ], [-0.65764878, 1.34235122], [-0.87449525, 1.12550475]]),)
- bind_arguments(arguments: Arguments | Feature) Feature#
Bind another feature’s properties as arguments to this feature.
This method allows properties of arguments to be dynamically linked to this feature, enabling shared configurations across multiple features. It is commonly used in advanced feature pipelines.
This method is often used in combination with the Arguments feature, which provides a utility that helps manage and propagate feature arguments efficiently.
The values from arguments override the corresponding properties during feature evaluation (call-time), without permanently modifying the feature’s own properties.
Parameters#
- arguments: Arguments or Feature
A feature whose properties will be used as call-time arguments for this feature. Typically an Arguments feature.
Returns#
- Feature
The current feature instance with bound arguments for chaining.
Examples#
>>> import deeptrack as dt
Create an Arguments feature:
>>> arguments = dt.Arguments(scale=2.0)
Bind it with a pipeline:
>>> pipeline = dt.Value(value=3) >> dt.Add(b=1 * arguments.scale) >>> pipeline.bind_arguments(arguments) >>> result = pipeline() >>> result 5.0
Override the argument dynamically:
>>> result = pipeline(scale=1.0) >>> result 4.0
Without binding, overriding scale at call-time would have no effect, and the result would remain 5.0.
- dtype(float: Literal['float32', 'float64', 'default'] | None = None, int: Literal['int16', 'int32', 'int64', 'default'] | None = None, complex: Literal['complex64', 'complex128', 'default'] | None = None, bool: Literal['bool', 'default'] | None = None) Feature#
Set the dtypes to be used during evaluation.
It alters the dtypes used for array creation, but does not automatically cast the type.
Parameters#
- float: str, optional
The float dtype to set. Can be “float32”, “float64”, “default”, or None. Defaults to None.
- int: str, optional
The int dtype to set. Can be “int16”, “int32”, “int64”, “default”, or None. Defaults to None.
- complex: str, optional
The complex dtype to set. Can be “complex64”, “complex128”, “default”, or None. Defaults to None.
- bool: str, optional
The bool dtype to set. Can be “bool”, “default”, or None. Defaults to None.
Returns#
- Feature
self
Examples#
>>> import deeptrack as dt
Set float and int data types for a feature:
>>> feature = dt.Multiply(b=2) >>> feature.dtype(float="float32", int="int16") >>> feature.float_dtype dtype('float32')
>>> feature.int_dtype dtype('int16')
Use complex numbers in the feature:
>>> feature.dtype(complex="complex128") >>> feature.complex_dtype dtype('complex128')
Reset float dtype to default:
>>> feature.dtype(float="default") >>> feature.float_dtype # resolved from config dtype('float64') # Depends on backend config
- get(data: Any, _ID: tuple[int, ...] = (), **kwargs: Any) Any#
Transform input data (abstract method).
Abstract method that defines how the feature transforms the input data. The current values of all properties are passed as keyword arguments.
Parameters#
- data: Any
The input data to be transformed, most commonly a NumPy array or a PyTorch tensor, but it can be anything.
- _ID: tuple[int, …], optional
The unique identifier for the current execution. Defaults to ().
- **kwargs: Any
The current value of all properties in the properties attribute, as well as any global arguments passed to the feature.
Returns#
- Any
The transformed data.
Raises#
- NotImplementedError
Raised if this method is not overridden by subclasses.
- get_backend() Literal['numpy', 'torch']#
Get the current backend of the feature.
Returns#
- “numpy” or “torch”
The backend of this feature.
Examples#
>>> import deeptrack as dt
Create a feature:
>>> feature = dt.Add(b=5)
Set the feature’s backend to NumPy and check it:
>>> feature.numpy() >>> feature.get_backend() 'numpy'
Set the feature’s backend to PyTorch and check it:
>>> feature.torch() >>> feature.get_backend() 'torch'
- new(data_list: Any | None = None, _ID: tuple[int, ...] = (), **kwargs: Any) Any#
Reset and recompute the feature output for the given _ID.
This method invalidates the cached data (via .update()), then immediately evaluates the feature using the same input and keyword override semantics as .__call__().
Parameters#
- data_list: Any, optional
The input data passed to .__call__(). Defaults to None.
- _ID: tuple[int, …], optional
The identifier for which the value should be recomputed. Defaults to an empty tuple.
- **kwargs: Any
Keyword arguments forwarded to .__call__(), overriding properties.
Returns#
- Any
The newly computed output.
- numpy(recursive: bool = True) Feature#
Set the backend to numpy.
The NumPy backend does not support non-CPU devices. Calling .numpy() resets the feature’s device to “cpu”.
Parameters#
- recursive: bool, optional
If True (default), also converts all dependent features.
Returns#
- Feature
self
Examples#
>>> import deeptrack as dt >>> import numpy as np
Create a feature and ensure it uses the NumPy backend:
>>> feature = dt.Add(b=5) >>> feature.numpy()
Evaluate the feature on a NumPy array:
>>> output = feature(np.array([1, 2, 3])) >>> output array([6, 7, 8])
Apply recursively in a pipeline:
>>> f1 = dt.Multiply(b=2) >>> f2 = dt.Subtract(b=1) >>> pipeline = f1 >> f2 >>> pipeline.numpy() >>> output = pipeline(np.array([1, 2, 3])) >>> output array([1, 3, 5])
- plot(input_image: ndarray | list[ndarray] | Tensor | list[Tensor] | None = None, resolve_kwargs: dict[str, Any] | None = None, interval: float | None = None, **kwargs: Any) Any#
Visualize the output of the feature.
The .plot() method resolves the feature and visualizes the result: - If the output is a single image (NumPy array or PyTorch tensor), it
is displayed using pyplot.imshow(). Any parameters in kwargs are passed to pyplot.imshow().
If the output is a list or a tuple, an animation is created. In Jupyter notebooks, the animation is played inline using .to_jshtml(). In scripts, the animation is displayed using the matplotlib backend.
Parameters#
- input_image: array or tensor, or list[array] or list[tensor], optional
The input image or list of images passed as an argument to the .resolve() call. If None, uses previously set input values or propagates properties.
- resolve_kwargs: dict[str, Any], optional
Additional keyword arguments passed to the .resolve() call.
- interval: float, optional
The time between frames in the animation, in milliseconds. Defaults to ~33 ms (30 fps).
- **kwargs: Any
Additional keyword arguments passed to pyplot.imshow().
Returns#
- matplotlib.axes.Axes or matplotlib.animation.ArtistAnimation or Any
For single images, returns the current axes. For videos, returns the animation. In notebook fallback mode, may return an interactive widget.
Examples#
>>> import deeptrack as dt
Create an instance of a dummy feature that returns the input:
>>> feature = dt.DummyFeature()
Generate and plot a grayscale image:
>>> import numpy as np >>> >>> img = np.random.randint(0, 256, (64, 64)) >>> feature.plot(img, cmap="gray");
Generate and plot a grayscale video:
>>> video = [np.random.randint(0, 256, (64, 64)) for _ in range(10)] >>> feature.plot(video, interval=100, cmap="gray");
Generate a grayscale image using torch and plot it:
>>> import torch >>> >>> img = torch.randint(0, 256, size=(64, 64)) >>> feature.plot(img, cmap="gray");
Generate a simulated image of a point particle visualized using brightfield microscopy and plot it:
>>> particle = dt.PointParticle(intensity=100) >>> optics = dt.Fluorescence() >>> imaged_particle = optics(particle) >>> imaged_particle.plot(cmap="gray");
- resolve(data_list: Any | None = None, _ID: tuple[int, ...] = (), **kwargs: Any) Any#
Execute the feature or pipeline.
The .__call__() method executes the feature or pipeline on the provided input data and updates the computation graph if necessary. It overrides properties using the keyword arguments.
The actual computation is performed by calling the parent .__call__() method in the DeepTrackNode class, which manages lazy evaluation and caching.
Parameters#
- data_list: Any, optional
The input data to the feature or pipeline. It is most commonly a list of NumPy arrays or PyTorch tensors, but it can be anything. Defaults to None, in which case the feature uses the previous set of input values or propagates properties.
- **kwargs: Any
Additional parameters passed to the pipeline. These override properties with matching names. For example, calling feature(x, value=4) executes feature on the input x while setting the property value to 4. All features in a pipeline are affected by these overrides.
Returns#
- Any
The output of the feature or pipeline after execution. This is typically a list of NumPy arrays or PyTorch tensors, but it can be anything.
Examples#
>>> import deeptrack as dt
Define a feature:
>>> feature = dt.Add(b=2)
Call this feature with an input:
>>> import numpy as np >>> >>> feature(np.array([1, 2, 3])) array([3, 4, 5])
Execute the feature with previously set input:
>>> feature() # Uses stored input array([3, 4, 5])
Execute the feature with new input:
>>> feature(np.array([10, 20, 30])) # Uses new input array([12, 22, 32])
Override a property:
>>> feature(np.array([10, 20, 30]), b=1) array([11, 21, 31])
- seed(updated_seed: int | None = None, _ID: tuple[int, ...] = ()) int#
Seed all random number generators for reproducibility.
This method sets the global random seed for Python’s random module, NumPy, and (if available) PyTorch. If updated_seed is provided, it replaces the value of the internal _random_seed node before resolution.
This method sets the following: - random.seed(seed) for Python’s RNG - np.random.seed(seed) for NumPy - torch.manual_seed(seed) and torch.cuda.manual_seed_all(seed)
The same seed will lead to deterministic behavior within each backend (e.g., random, NumPy or PyTorch), but not across them. NumPy and PyTorch use different RNG algorithms, so identical seeds will not generate the same random numbers across backends.
Parameters#
- updated_seed: int or None, optional
If provided, sets a fixed value for the internal _random_seed. Defaults to None.
- _ID: tuple[int, …], optional
Unique identifier used to resolve the seed value. Defaults to ().
Returns#
- int
The resolved seed value used for all RNGs.
Examples#
>>> import deeptrack as dt
Using `random`
Define a feature that samples a random integer from 0 to 10 using the Python standard library’s random module:
>>> import random >>> >>> feature = dt.Value(lambda: random.randint(0, 10)) >>> >>> for _ in range(3): ... print(f"output={feature.new()} seed={feature.seed()}") output=3 seed=355549663 output=5 seed=119234165 output=9 seed=1956541335
Each time .update() is called, the internal _random_seed is re-sampled and used to reseed the Python random module. This produces a new deterministic seed, but different output values.
Fix the seed to reuse it later for reproducibility:
>>> seed = feature.seed() >>> seed 1956541335
Now reseed the feature with the same value before each update, to make the output deterministic and repeatable.
>>> for _ in range(3): ... feature.seed(seed) ... print(f"output={feature.new()} seed={feature.seed()}") output=5 seed=1933964715 output=5 seed=1933964715 output=5 seed=1933964715
Since the random seed is fixed before each sample, the output is the same every time. Note: the seed reported after sampling may differ if it’s re-sampled internally, but the output remains stable.
Using NumPy
Similar observations can be made with NumPy:
>>> import numpy as np >>> >>> feature = dt.Value(lambda: np.random.randint(0, 10))
Using PyTorch
Similar observations can be made with PyTorch:
>>> import torch >>> >>> feature = dt.Value(lambda: torch.randint(0, 10, (1,)).item())
- to(device: str | device) Feature#
Set the device to be used during evaluation.
Parameters#
- device: str or torch.device
The device to use. If the backend is numpy, this can only be “cpu”.
Returns#
- Feature
self
Examples#
>>> import deeptrack as dt >>> import torch
Create a feature and assign a device (for torch backend):
>>> feature = dt.Add(b=1) >>> feature.torch() >>> feature.to(torch.device("cpu")) >>> feature.device device(type='cpu')
Move the feature to GPU (if available):
>>> if torch.cuda.is_available(): ... feature.to(torch.device("cuda")) ... feature.device device(type='cuda')
Use Apple MPS device on Apple Silicon (if supported):
>>> if (torch.backends.mps.is_available() ... and torch.backends.mps.is_built()): ... feature.to(torch.device("mps")) ... feature.device device(type='mps')
- to_sequential(**kwargs: Any) Feature#
Convert a feature to be resolved as a sequence.
Should be called on individual features, not combinations of features. All keyword arguments will be treated as sequential properties and will be passed to the parent feature.
If a property from the keyword argument already exists in the feature, the existing property will be used to initialize the passed property (that is, it will be used for the first timestep).
Parameters#
- self: Feature
Feature to make sequential.
- kwargs: Any
Keyword arguments mapping property names to sequential sampling rules.
Returns#
- Feature
The feature itself (returned for chaining), now configured to resolve sequentially.
Examples#
>>> import deeptrack as dt
Sequentially evaluate a feature.
This example shows how to_sequential() can be used together with __distributed__ = False to create a feature that generates values over time, rather than transforming input data.
Define a feature that returns a position value and does not depend on any inputs:
>>> class PositionFeature(dt.Feature): ... __distributed__ = False ... ... def __init__(self, position, **kwargs): ... super().__init__(position=position, **kwargs) ... ... def get(self, input_list, position, **kwargs): ... return position
Convert the position property into a sequential property that increments at each time step:
>>> feature = PositionFeature(position=0) >>> feature.to_sequential( ... position=lambda previous_value: 0 ... if previous_value is None ... else previous_value + 1 ... )
Wrap the feature in a Sequence and evaluate it:
>>> sequence = dt.Sequence(feature, sequence_length=5) >>> sequence() [0, 1, 2, 3, 4]
Sequentially evaluate a rotating ellipse.
Create the optics:
>>> optics = dt.Fluorescence( ... NA=0.6, ... magnification=10, ... resolution=1e-6, ... wavelength=633e-9, ... output_region=(0, 0, 32, 32), ... )
Create the scatterer:
>>> ellipse = dt.Ellipse( ... position_unit="pixel", ... position=(16, 16), ... intensity=1, ... radius=(1.5e-6, 1e-6), ... rotation=0, # Initial rotation at time step 0 ... )
Implement a function to increment the rotation:
>>> from numpy import pi >>> >>> def get_rotation(sequence_length, previous_value): ... delta = 2 * pi / sequence_length ... return previous_value + delta
Call to_sequential() to resolve the feature sequentially:
>>> rotating_ellipse = ellipse.to_sequential(rotation=get_rotation)
Image the scatterer with the optics:
>>> imaged_rotating_ellipse = optics(rotating_ellipse)
Encapsulate as a Sequence object and specify the sequence length:
>>> imaged_rotating_ellipse_sequence = dt.Sequence( ... imaged_rotating_ellipse, ... sequence_length=10, ... )
Finally observe the scatterer rotate:
>>> imaged_rotating_ellipse_sequence.update().plot();
- torch(device: device | None = None, recursive: bool = True) Feature#
Set the backend to torch.
Parameters#
- device: torch.device, optional
The device to use during evaluation (e.g. CPU, CUDA, or MPS). If provided, the feature’s device is updated via .to(device). Defaults to None.
- recursive: bool, optional
If True (default), it also converts all dependent features. If False, it does not.
Returns#
- Feature
self
Examples#
>>> import deeptrack as dt >>> import torch
Create a feature and switch to the PyTorch backend:
>>> feature = dt.Multiply(b=2) >>> feature.torch()
Call the feature on a torch tensor:
>>> input_tensor = torch.tensor([1.0, 2.0, 3.0]) >>> output = feature(input_tensor) >>> output tensor([2., 4., 6.])
Switch to GPU if available (CUDA):
>>> if torch.cuda.is_available(): ... device = torch.device("cuda") ... feature.torch(device=device) ... output = feature(torch.tensor([1.0, 2.0, 3.0], device=device)) ... output.device.type 'cuda'
Switch to GPU if available (MPS):
>>> if (torch.backends.mps.is_available() ... and torch.backends.mps.is_built()): ... device = torch.device("mps") ... feature.torch(device=device) ... output = feature(torch.tensor([1.0, 2.0, 3.0], device=device)) ... output.device.type 'mps'
Apply recursively in a pipeline:
>>> f1 = dt.Add(b=1) >>> f2 = dt.Multiply(b=2) >>> pipeline = f1 >> f2 >>> pipeline.torch() >>> output = pipeline(torch.tensor([1.0, 2.0])) >>> output tensor([4., 6.])
- update(**global_arguments: Any) Feature#
Refresh the feature to generate a new output.
By default, when a feature is called multiple times, it returns the same value, which is cached.
Calling .update() forces the feature to recompute and return a new value the next time it is evaluated.
Calling .new() is equivalent to calling .update() plus evaluation.
Parameters#
- **global_arguments: Any
DEPRECATED. Has no effect. Previously used to inject values during update. Use Arguments or call-time overrides instead.
Returns#
- Feature
The updated feature instance, ensuring the next evaluation produces a fresh result.
Examples#
>>> import deeptrack as dt
Create and resolve a feature:
>>> import numpy as np >>> >>> feature = dt.Value(lambda: np.random.rand()) >>> output1 = feature() >>> output1 0.9173610765203623
When resolving it again, it returns the same value:
>>> output2 = feature() >>> output2 # Same as before 0.9173610765203623
Using .update() forces re-evaluation when resolved:
>>> feature.update() # Feature updated >>> output3 = feature() >>> output3 0.13917950359184617
Using .new() both updates and resolves the feature:
>>> output4 = feature.new() >>> output4 0.006278518685428169