Source code for morphocut.stat
"""
.. note::
This module is deprecated.
"""
import itertools
from typing import Any, Optional
import numpy as np
from morphocut.core import (
Node,
Output,
ReturnOutputs,
Stream,
Variable,
_pipeline_stack,
closing_if_closable,
)
from morphocut.parallel import ParallelPipeline
from morphocut.filters import ExponentialSmoothingFilter as ExponentialSmoothing
[docs]@ReturnOutputs
@Output("agg_value")
class RunningMedian(Node):
"""
Calculate the running median of a value over a stream of objects.
Parameters:
value (Variable of numpy.ndarray): Values to calculate the running mean for.
n_init (int): Number of objects to initialize the median estimate.
Returns:
Variable[numpy.ndarray]: Running median approximation of ``value``.
This node uses the efficient approximation of the median from:
Mcfarlane, N. J. B., & Schofield, C. P. (1995).
Segmentation and tracking of piglets in images.
In Machine Vision and Applications (Vol. 8).
"""
def __init__(self, value: Variable[np.ndarray], n_init: int = 10):
super().__init__()
self.value = value
self.n_init = n_init
self.median: Optional[Any] = None
assert not any(
isinstance(p, ParallelPipeline) for p in _pipeline_stack
), "RunningMedian can not be used in a ParallelPipeline context"
def transform_stream(self, stream: Stream) -> Stream:
"""Transform a stream."""
with closing_if_closable(stream):
# Initial approximation
# TODO: This does not work correctly in situations when transform_stream
# is called repeatedly, e.g. ParallelPipeline.
if self.median is None:
yield from self._initialize_median(stream)
# Process
for obj in stream:
value = self.prepare_input(obj, "value")
# Update according to Mcfarlane & Schofield
mask = value > self.median
if np.isscalar(mask):
self.median += mask
else:
self.median[mask] += 1
mask = value < self.median
if np.isscalar(mask):
self.median -= mask
else:
self.median[mask] -= 1
yield self.prepare_output(obj, self.median)
self.after_stream()
def _initialize_median(self, stream):
objects = []
values = []
for obj in itertools.islice(stream, self.n_init):
value = self.prepare_input(obj, "value")
objects.append(obj)
values.append(value)
self.median = np.median(values, axis=0)
for obj in objects:
yield self.prepare_output(obj, self.median)