---
title: base
---

<div class="python-reference">
<a id="fastvideo.eval.metrics.base-classes"></a>
<a id="fastvideo.eval.metrics.base.BaseMetric-functions"></a>
<a id="fastvideo.eval.metrics.base"></a>

<h2 id="fastvideo.eval.metrics.base.MetricResult"><code>fastvideo.eval.metrics.base.MetricResult</code></h2>
<pre class="doc-signature"><code>MetricResult(name: str, score: float | None, details: dict&#91;str, Any&#93; = dict())</code></pre>
<p>Standard result container returned by all metrics.</p>
<p><code>score</code> is <code>None</code> when the metric was skipped (e.g. missing
required input).  Check <code>details["skipped"]</code> for the reason.</p>

<h2 id="fastvideo.eval.metrics.base.BaseMetric"><code>fastvideo.eval.metrics.base.BaseMetric</code></h2>
<pre class="doc-signature"><code>BaseMetric()</code></pre>
<p>Abstract base class for all eval metrics.</p>
<p>Two execution shapes:</p>
<ul>
<li>
<p><strong>Per-sample</strong> (<code>is_set_metric=False</code>, default) — implement
  :meth:<code>compute</code>. The Evaluator calls it once per input sample and
  returns one :class:<code>MetricResult</code> per sample.</p>
</li>
<li>
<p><strong>Set-vs-set</strong> (<code>is_set_metric=True</code>) — implement
  :meth:<code>accumulate</code> (called once per sample to buffer features)
  and :meth:<code>finalize</code> (called once after all samples to compute
  the corpus-level result). Use :meth:<code>reset</code> to clear buffers and
  :meth:<code>merge_from</code> to fold multi-GPU per-worker state together.</p>
</li>
</ul>
<p>Optionally override :meth:<code>setup</code> to eagerly load models. Metrics
that chunk along the time dim for memory hardcode their own chunk
size in <code>__init__</code> (see <code>optical_flow</code> for the canonical
example). Eval always processes one video per
:meth:<code>Evaluator.evaluate</code> call; <code>compute</code> / <code>accumulate</code>
receive a single sample, not a batch.</p>

<details><summary>Source</summary><pre><code>class BaseMetric:
    &quot;&quot;&quot;Abstract base class for all eval metrics.

    Two execution shapes:

    * **Per-sample** (``is_set_metric=False``, default) — implement
      :meth:`compute`. The Evaluator calls it once per input sample and
      returns one :class:`MetricResult` per sample.

    * **Set-vs-set** (``is_set_metric=True``) — implement
      :meth:`accumulate` (called once per sample to buffer features)
      and :meth:`finalize` (called once after all samples to compute
      the corpus-level result). Use :meth:`reset` to clear buffers and
      :meth:`merge_from` to fold multi-GPU per-worker state together.

    Optionally override :meth:`setup` to eagerly load models. Metrics
    that chunk along the time dim for memory hardcode their own chunk
    size in ``__init__`` (see ``optical_flow`` for the canonical
    example). Eval always processes one video per
    :meth:`Evaluator.evaluate` call; ``compute`` / ``accumulate``
    receive a single sample, not a batch.
    &quot;&quot;&quot;

    name: str = &quot;&quot;
    requires_reference: bool = True
    higher_is_better: bool = True
    dependencies: list&#91;str&#93; = &#91;&#93;
    needs_gpu: bool = False
    backbone: str | None = None
    is_set_metric: bool = False

    # Default time-dim chunk size for metrics that batch internally over
    # frames or frame-pairs. Override in subclass __init__ if needed
    # (see ``optical_flow``, ``motion_smoothness``, ``dynamic_degree``).
    _chunk_size: int | None = None

    def __init__(self) -&gt; None:
        self._device: torch.device = torch.device(&quot;cpu&quot;)

    @property
    def device(self) -&gt; torch.device:
        return self._device

    def to(self, device: str | torch.device) -&gt; BaseMetric:
        &quot;&quot;&quot;Move metric (and its internal models) to *device*.&quot;&quot;&quot;
        self._device = torch.device(device)
        return self

    def setup(self) -&gt; None:  # noqa: B027 - intentionally optional override
        &quot;&quot;&quot;Eagerly load models. Called once by :class:`EvalWorker`.

        Default is a no-op; metrics with no eager state (pixel math,
        closed-form ops) inherit this. Override only if your metric
        needs to load weights.
        &quot;&quot;&quot;

    def _skip(self, sample: dict, reason: str) -&gt; MetricResult:
        &quot;&quot;&quot;Return a skipped result (``score=None`` + reason in details).&quot;&quot;&quot;
        return MetricResult(name=self.name, score=None, details=&#123;&quot;skipped&quot;: reason&#125;)

    def compute(self, sample: dict) -&gt; MetricResult:
        &quot;&quot;&quot;Per-sample metrics: compute the score for one sample.

        ``sample&#91;&quot;video&quot;&#93;`` is ``(T, C, H, W)`` float in ``&#91;0, 1&#93;``.
        ``sample&#91;&quot;reference&quot;&#93;`` (if used) has the same shape. Return
        ``self._skip(sample, reason)`` for missing inputs.
        &quot;&quot;&quot;
        raise NotImplementedError(f&quot;&#123;type(self).__name__&#125;.compute is not implemented&quot;)

    # --- set-vs-set protocol (only invoked when is_set_metric=True) ---

    def reset(self) -&gt; None:  # noqa: B027 - intentionally optional override
        &quot;&quot;&quot;Clear accumulator state at the start of each evaluate() call.&quot;&quot;&quot;

    def accumulate(self, sample: dict) -&gt; None:
        &quot;&quot;&quot;Buffer per-sample features for a corpus-level metric.&quot;&quot;&quot;
        raise NotImplementedError(f&quot;&#123;type(self).__name__&#125;.accumulate is not implemented&quot;)

    def finalize(self) -&gt; MetricResult:
        &quot;&quot;&quot;Compute the corpus-level result from buffered state.&quot;&quot;&quot;
        raise NotImplementedError(f&quot;&#123;type(self).__name__&#125;.finalize is not implemented&quot;)

    def merge_from(self, other: BaseMetric) -&gt; None:  # noqa: B027 - intentionally optional override
        &quot;&quot;&quot;Multi-GPU: fold another worker&#x27;s accumulator state into this one.&quot;&quot;&quot;</code></pre></details>
<h3 id="fastvideo.eval.metrics.base.BaseMetric.to"><code>fastvideo.eval.metrics.base.BaseMetric.to</code></h3>
<pre class="doc-signature"><code>to(device: str | torch.device) -&gt; BaseMetric</code></pre>
<p>Move metric (and its internal models) to <em>device</em>.</p>
<details><summary>Source</summary><pre><code>def to(self, device: str | torch.device) -&gt; BaseMetric:
    &quot;&quot;&quot;Move metric (and its internal models) to *device*.&quot;&quot;&quot;
    self._device = torch.device(device)
    return self</code></pre></details>
<h3 id="fastvideo.eval.metrics.base.BaseMetric.setup"><code>fastvideo.eval.metrics.base.BaseMetric.setup</code></h3>
<pre class="doc-signature"><code>setup() -&gt; None</code></pre>
<p>Eagerly load models. Called once by :class:<code>EvalWorker</code>.</p>
<p>Default is a no-op; metrics with no eager state (pixel math,
closed-form ops) inherit this. Override only if your metric
needs to load weights.</p>
<details><summary>Source</summary><pre><code>def setup(self) -&gt; None:  # noqa: B027 - intentionally optional override
    &quot;&quot;&quot;Eagerly load models. Called once by :class:`EvalWorker`.

    Default is a no-op; metrics with no eager state (pixel math,
    closed-form ops) inherit this. Override only if your metric
    needs to load weights.
    &quot;&quot;&quot;</code></pre></details>
<h3 id="fastvideo.eval.metrics.base.BaseMetric.compute"><code>fastvideo.eval.metrics.base.BaseMetric.compute</code></h3>
<pre class="doc-signature"><code>compute(sample: dict) -&gt; MetricResult</code></pre>
<p>Per-sample metrics: compute the score for one sample.</p>
<p><code>sample["video"]</code> is <code>(T, C, H, W)</code> float in <code>[0, 1]</code>.
<code>sample["reference"]</code> (if used) has the same shape. Return
<code>self._skip(sample, reason)</code> for missing inputs.</p>
<details><summary>Source</summary><pre><code>def compute(self, sample: dict) -&gt; MetricResult:
    &quot;&quot;&quot;Per-sample metrics: compute the score for one sample.

    ``sample&#91;&quot;video&quot;&#93;`` is ``(T, C, H, W)`` float in ``&#91;0, 1&#93;``.
    ``sample&#91;&quot;reference&quot;&#93;`` (if used) has the same shape. Return
    ``self._skip(sample, reason)`` for missing inputs.
    &quot;&quot;&quot;
    raise NotImplementedError(f&quot;&#123;type(self).__name__&#125;.compute is not implemented&quot;)</code></pre></details>
<h3 id="fastvideo.eval.metrics.base.BaseMetric.reset"><code>fastvideo.eval.metrics.base.BaseMetric.reset</code></h3>
<pre class="doc-signature"><code>reset() -&gt; None</code></pre>
<p>Clear accumulator state at the start of each evaluate() call.</p>
<details><summary>Source</summary><pre><code>def reset(self) -&gt; None:  # noqa: B027 - intentionally optional override
    &quot;&quot;&quot;Clear accumulator state at the start of each evaluate() call.&quot;&quot;&quot;</code></pre></details>
<h3 id="fastvideo.eval.metrics.base.BaseMetric.accumulate"><code>fastvideo.eval.metrics.base.BaseMetric.accumulate</code></h3>
<pre class="doc-signature"><code>accumulate(sample: dict) -&gt; None</code></pre>
<p>Buffer per-sample features for a corpus-level metric.</p>
<details><summary>Source</summary><pre><code>def accumulate(self, sample: dict) -&gt; None:
    &quot;&quot;&quot;Buffer per-sample features for a corpus-level metric.&quot;&quot;&quot;
    raise NotImplementedError(f&quot;&#123;type(self).__name__&#125;.accumulate is not implemented&quot;)</code></pre></details>
<h3 id="fastvideo.eval.metrics.base.BaseMetric.finalize"><code>fastvideo.eval.metrics.base.BaseMetric.finalize</code></h3>
<pre class="doc-signature"><code>finalize() -&gt; MetricResult</code></pre>
<p>Compute the corpus-level result from buffered state.</p>
<details><summary>Source</summary><pre><code>def finalize(self) -&gt; MetricResult:
    &quot;&quot;&quot;Compute the corpus-level result from buffered state.&quot;&quot;&quot;
    raise NotImplementedError(f&quot;&#123;type(self).__name__&#125;.finalize is not implemented&quot;)</code></pre></details>
<h3 id="fastvideo.eval.metrics.base.BaseMetric.merge_from"><code>fastvideo.eval.metrics.base.BaseMetric.merge_from</code></h3>
<pre class="doc-signature"><code>merge_from(other: BaseMetric) -&gt; None</code></pre>
<p>Multi-GPU: fold another worker's accumulator state into this one.</p>
<details><summary>Source</summary><pre><code>def merge_from(self, other: BaseMetric) -&gt; None:  # noqa: B027 - intentionally optional override
    &quot;&quot;&quot;Multi-GPU: fold another worker&#x27;s accumulator state into this one.&quot;&quot;&quot;</code></pre></details>
</div>
