Source code for QVideo.filters.median
'''Batch median-of-medians background estimator (remedian algorithm).'''
from QVideo.filters._MedianBase import _MedianBase
from QVideo.lib.videotypes import Image
import numpy as np
__all__ = ['Median']
[docs]
class Median(_MedianBase):
'''Fast median-of-medians background estimator.
Computes a running pixel-wise median over ``3 ** order`` frames
using a recursive median-of-three algorithm that requires only two
frame buffers per level.
Parameters
----------
order : int
Recursion depth. The median is computed over ``3 ** order``
frames. Default: ``1`` (median of 3 frames).
data : Image or None
Optional seed frame used to pre-allocate internal buffers.
If ``None`` the buffers are allocated on the first call to
:meth:`add`. Default: ``None``.
Notes
-----
Results are only available once :meth:`ready` returns ``True``,
which requires at least ``3 ** order`` frames to have been added.
Before that point :meth:`get` returns whatever seed data was
provided (or ``None``).
The :meth:`reset` method clears all buffers and resets the ready
flag so that the estimator starts fresh without reallocating memory.
References
----------
P.J. Rousseeuw and G.W. Bassett Jr., "The remedian: a robust
averaging method for large data sets", *Journal of the American
Statistical Association*, 85(409):97–104, 1990.
`doi:10.1080/01621459.1990.10475311 <https://doi.org/10.1080/01621459.1990.10475311>`_
'''
[docs]
def add(self, data: Image) -> None:
'''Incorporate a new frame into the median estimate.
Resets the ready flag at the start of each call so that
:meth:`ready` reflects only whether *this* call produced a new
estimate.
Parameters
----------
data : Image
Input frame. If the shape differs from the previously seen
shape, the internal buffers are reallocated.
'''
self._ready = False
if data.shape != self.shape:
self._initialize(data)
if self._next is not None:
self._next.add(data)
if self._next.ready():
data = self._next.get()
else:
return
if self._index == 2:
a = self._buffer[0]
b = self._buffer[1]
self._result = np.maximum(np.minimum(a, b),
np.minimum(np.maximum(a, b), data))
self._index = 0
self._ready = True
self._buffer[self._index] = data
self._index += 1
[docs]
def ready(self) -> bool:
'''Return ``True`` if the most recent :meth:`add` produced a new estimate.
The flag is reset at the start of each :meth:`add` call and set
again only if that call completes a new median computation.
Calling :meth:`get` does not affect this flag.
Returns
-------
bool
``True`` if the last :meth:`add` yielded a fresh estimate.
'''
return self._ready