""" Explainability utilities for deepfake detection. Provides structured risk explanations with evidence summaries for image, video, audio, and multimodal analysis. """ def explain_risk(score, model_scores=None): """ Generate structured risk explanation for image/video analysis. Args: score: Final risk score (0.0 - 1.0). model_scores: Optional dict of per-model scores for evidence. Returns: str with detailed risk explanation. """ if score >= 0.60: level = "AI-GENERATED" desc = "Strong indicators of AI generation or manipulation" else: level = "AUTHENTIC" desc = "No significant manipulation indicators" explanation = f"{level} — {desc}" if model_scores: evidence = [] if model_scores.get("vit_prob", 0) > 0.6: evidence.append("ViT detected deepfake patterns") if model_scores.get("face_prob", 0) > 0.6: evidence.append("facial manipulation artifacts found") if model_scores.get("forensic_prob", 0) > 0.5: evidence.append("noise/ELA inconsistency detected") if model_scores.get("frequency_prob", 0) > 0.5: evidence.append("frequency-domain anomalies") if model_scores.get("eff_prob", 0) > 0.6: evidence.append("EfficientNet flagged AI generation") if model_scores.get("dino_prob", 0) > 0.6: evidence.append("DINOv2 detected synthetic features") if model_scores.get("clip_prob", 0) > 0.6: evidence.append("CLIP vision-language model detected manipulation signatures") if evidence: explanation += f". Evidence: {'; '.join(evidence)}" return explanation def explain_audio_risk(fake_prob): """Explain audio deepfake risk level.""" if fake_prob >= 0.60: return "AI-GENERATED — AI-generated speech detected (voice cloning / TTS)" else: return "AUTHENTIC — Audio appears authentic" def explain_multimodal(modality_scores, final_score): """ Generate explanation for multimodal fusion result. Args: modality_scores: dict with image/video/audio scores. final_score: fused risk score. Returns: str explanation. """ active = {k: v for k, v in modality_scores.items() if v is not None} if not active: return "No modalities analyzed" parts = [] for mod, score in active.items(): if score >= 60: parts.append(f"{mod}: AI-generated ({score}%)") else: parts.append(f"{mod}: authentic ({score}%)") if final_score >= 0.60: verdict = "Strong evidence of manipulation across modalities" else: verdict = "Content appears authentic across analyzed modalities" return f"{verdict}. Per-modality: {'; '.join(parts)}"