Spaces:
Running
Running
File size: 1,723 Bytes
2e175db | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | """
Detector interface.
Every detector is a plug-in that takes a PIL image and returns a
DetectorResult. The ensemble layer aggregates results from all enabled
detectors into a single Probabilities vector.
Adding a new detector = subclassing Detector + registering it in ensemble.py.
The /v1 API response shape never changes.
"""
from __future__ import annotations
from abc import ABC, abstractmethod
from dataclasses import dataclass
from PIL import Image
@dataclass
class DetectorResult:
"""Output of a single detector run.
Attributes
----------
name :
Stable identifier for this detector (used in API `signals`).
score :
The detector's own "fakeness" estimate in [0, 1].
1.0 means "definitely synthetic"; 0.0 means "definitely authentic".
contributions :
Optional per-class hints in [0, 1]. Keys must be a subset of the
4-class taxonomy: "authentic", "ai_generated", "deepfake", "edited".
Detectors that only know "real vs. fake" leave this empty and let
the ensemble splat their score across the relevant classes.
notes :
Free-form human-readable note (surfaced in API for debugging).
"""
name: str
score: float
contributions: dict[str, float]
notes: str | None = None
class Detector(ABC):
"""Base class for all forensic detectors.
Detectors must be safe to instantiate once at process start and reused
across requests — load model weights in __init__, not in run().
"""
name: str = "abstract"
@abstractmethod
def run(self, image: Image.Image) -> DetectorResult:
"""Score a single PIL image. Must not mutate the image."""
raise NotImplementedError
|