Spaces:
Sleeping
Sleeping
| """Abstract base class every detector must implement.""" | |
| from __future__ import annotations | |
| import base64 | |
| from abc import ABC, abstractmethod | |
| from pathlib import Path | |
| from backend.core.schema import DetectionResult, Verdict | |
| class BaseDetector(ABC): | |
| """ | |
| Contract: | |
| - Each detector receives an image path. | |
| - Returns a DetectionResult with p_fake in [0, 1]. | |
| - Must never raise β catch all exceptions and return | |
| DetectionResult with error set. | |
| """ | |
| name: str = "base" | |
| async def detect(self, image_path: str) -> DetectionResult: | |
| """Run detection. Must not raise.""" | |
| # ββ Helpers ββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def _to_base64(image_path: str) -> str: | |
| data = Path(image_path).read_bytes() | |
| return base64.b64encode(data).decode("utf-8") | |
| def _mime_type(image_path: str) -> str: | |
| ext = Path(image_path).suffix.lower() | |
| return { | |
| ".jpg": "image/jpeg", | |
| ".jpeg": "image/jpeg", | |
| ".png": "image/png", | |
| ".webp": "image/webp", | |
| ".gif": "image/gif", | |
| ".bmp": "image/bmp", | |
| }.get(ext, "image/jpeg") | |
| def _verdict_from_p_fake( | |
| p_fake: float, | |
| ai_threshold: float = 0.65, | |
| real_threshold: float = 0.35, | |
| ) -> Verdict: | |
| if p_fake >= ai_threshold: | |
| return Verdict.AI_GENERATED | |
| if p_fake <= real_threshold: | |
| return Verdict.REAL | |
| return Verdict.UNCERTAIN | |
| def _error_result(self, error: str) -> DetectionResult: | |
| return DetectionResult( | |
| detector=self.name, | |
| p_fake=0.5, | |
| verdict=Verdict.UNCERTAIN, | |
| confidence=0.0, | |
| error=error, | |
| ) | |