intrusionx-backend / utils /explainer.py
ayucancode's picture
Upload 18 files
56c08f3 verified
Raw
History Blame Contribute Delete
23.6 kB
"""
Tattva.AI β€” Explanation Generator & AI Insights Engine
Generates human-readable explanations and structured AI insights
for detection results using a rule-based analysis engine.
"""
from __future__ import annotations
# ══════════════════════════════════════════════════════════════
# AI INSIGHTS ENGINE (Rule-Based)
# ══════════════════════════════════════════════════════════════
def generate_ai_insights(result: dict, media_type: str = "image") -> dict:
"""
Generate structured, human-readable AI insights from a detection result.
Parameters
----------
result : dict
The detection result from any detector (image/video/audio).
media_type : str
One of "image", "video", "audio".
Returns
-------
dict with:
ai_insights : list of {category, description, severity}
anomaly_score : float (0-1)
risk_level : str ("Low", "Medium", "High", "Critical")
summary : str
"""
insights = []
anomaly_score = 0.0
if media_type == "image":
insights, anomaly_score = _analyze_image_insights(result)
elif media_type == "video":
insights, anomaly_score = _analyze_video_insights(result)
elif media_type == "audio":
insights, anomaly_score = _analyze_audio_insights(result)
# Determine risk level
if anomaly_score >= 0.8:
risk_level = "Critical"
elif anomaly_score >= 0.6:
risk_level = "High"
elif anomaly_score >= 0.35:
risk_level = "Medium"
else:
risk_level = "Low"
# Generate summary
verdict = result.get("verdict", "UNKNOWN")
confidence = result.get("confidence", 0)
n_insights = len([i for i in insights if i["severity"] in ("high", "critical")])
if verdict == "DEEPFAKE":
summary = (
f"Analysis detected {n_insights} high-severity anomalies with "
f"{confidence:.1f}% confidence. Strong indicators of AI manipulation "
f"or synthetic generation were found."
)
elif verdict == "SUSPICIOUS":
summary = (
f"Analysis found {len(insights)} potential anomalies. "
f"Some indicators of manipulation are present but not definitive. "
f"Manual review is recommended."
)
elif verdict == "AUTHENTIC":
summary = (
f"No significant manipulation indicators detected. "
f"The media appears authentic with {confidence:.1f}% confidence."
)
else:
summary = "Analysis could not be completed. Please try again."
return {
"ai_insights": insights,
"anomaly_score": round(anomaly_score, 2),
"risk_level": risk_level,
"summary": summary,
}
def _analyze_image_insights(result: dict) -> tuple[list, float]:
"""Generate insights specific to image detection."""
insights = []
scores = []
verdict = result.get("verdict", "UNKNOWN")
confidence = result.get("confidence", 0)
ela_score = result.get("ela_score", 0)
face_detected = result.get("face_detected", False)
models_used = result.get("models_used", [])
probs = result.get("probs", {})
# ── Rule: Face detection + deepfake verdict ──
if face_detected and verdict == "DEEPFAKE":
insights.append({
"category": "Facial Inconsistency",
"description": (
"Face region analysis reveals texture irregularities consistent "
"with GAN-generated or face-swapped imagery. Subtle artifacts "
"detected around facial landmarks (eyes, mouth, jawline)."
),
"severity": "high",
})
scores.append(0.85)
elif face_detected and verdict == "SUSPICIOUS":
insights.append({
"category": "Facial Anomaly",
"description": (
"Minor facial texture inconsistencies detected. The face region "
"shows some statistical deviations from natural imagery patterns."
),
"severity": "medium",
})
scores.append(0.5)
# ── Rule: No face but deepfake β†’ full-image AI generation ──
if not face_detected and verdict in ("DEEPFAKE", "SUSPICIOUS"):
insights.append({
"category": "AI-Generated Content",
"description": (
"No human face detected, but the full image exhibits patterns "
"consistent with AI image generation (Stable Diffusion, DALL-E, "
"Midjourney). Uniform noise distribution suggests synthetic origin."
),
"severity": "high" if verdict == "DEEPFAKE" else "medium",
})
scores.append(0.75 if verdict == "DEEPFAKE" else 0.45)
# ── Rule: ELA-based insights ──
if ela_score > 30:
insights.append({
"category": "Compression Artifacts",
"description": (
f"Error Level Analysis shows elevated error levels ({ela_score:.1f}). "
"This indicates the image has undergone non-uniform compression, "
"suggesting regions may have been edited or spliced after initial save."
),
"severity": "high",
})
scores.append(0.7)
elif ela_score > 15:
insights.append({
"category": "Compression Anomaly",
"description": (
f"Moderate ELA score ({ela_score:.1f}) detected. Some regions "
"show different error levels, which could indicate light editing "
"or multiple save operations."
),
"severity": "medium",
})
scores.append(0.4)
elif ela_score < 5 and verdict in ("DEEPFAKE", "SUSPICIOUS"):
insights.append({
"category": "Unnaturally Clean Image",
"description": (
f"Very low ELA score ({ela_score:.1f}) combined with deepfake "
"indicators. AI-generated images often have uniform error levels "
"because they are never captured by a physical camera sensor."
),
"severity": "medium",
})
scores.append(0.5)
# ── Rule: Model agreement/disagreement ──
if len(models_used) >= 2:
# Check if models agree
fake_probs = []
for key, val in probs.items():
if "Fake" in key or "artificial" in key:
fake_probs.append(val)
if len(fake_probs) >= 2:
agree = all(p >= 50 for p in fake_probs) or all(p < 50 for p in fake_probs)
if agree and all(p >= 50 for p in fake_probs):
insights.append({
"category": "Cross-Model Consensus",
"description": (
"Both ViT and Swin Transformer models independently "
"flagged this image as manipulated. Cross-model agreement "
"significantly increases detection reliability."
),
"severity": "high",
})
scores.append(0.9)
elif not agree:
insights.append({
"category": "Model Disagreement",
"description": (
"Detection models produced conflicting results. One model "
"flags manipulation while the other does not. This can "
"occur with sophisticated deepfakes or borderline cases."
),
"severity": "medium",
})
scores.append(0.45)
# ── Rule: High confidence authentic ──
if verdict == "AUTHENTIC" and confidence > 90:
insights.append({
"category": "High Authenticity",
"description": (
"Multiple detection layers confirm this image appears genuine. "
"Natural sensor noise, consistent compression, and no face-swap "
"artifacts detected."
),
"severity": "low",
})
scores.append(0.1)
# Calculate aggregate anomaly score
anomaly_score = max(scores) if scores else 0.0
return insights, anomaly_score
def _analyze_video_insights(result: dict) -> tuple[list, float]:
"""Generate insights specific to video detection."""
insights = []
scores = []
verdict = result.get("verdict", "UNKNOWN")
confidence = result.get("confidence", 0)
frame_results = result.get("frame_results", [])
flagged_frames = result.get("flagged_frames", [])
frame_count = result.get("frame_count", 0)
duration = result.get("duration", 0)
# ── Rule: Temporal instability ──
if len(frame_results) >= 3:
confidences = []
for fr in frame_results:
v = fr.get("verdict", "AUTHENTIC")
c = fr.get("confidence", 50)
confidences.append(c if v != "AUTHENTIC" else 100 - c)
variance = float(np.std(confidences)) if len(confidences) > 1 else 0
mean_conf = float(np.mean(confidences))
if variance > 20:
insights.append({
"category": "Temporal Instability",
"description": (
f"High frame-to-frame confidence variance ({variance:.1f}%). "
"Deepfake generation often produces inconsistent quality across "
"frames, especially during rapid head movements or expressions."
),
"severity": "high",
})
scores.append(0.75)
elif variance > 10:
insights.append({
"category": "Temporal Fluctuation",
"description": (
f"Moderate confidence variance ({variance:.1f}%) detected across frames. "
"Some frames show more manipulation artifacts than others."
),
"severity": "medium",
})
scores.append(0.5)
# ── Rule: Flagged frame ratio ──
if frame_count > 0:
flag_ratio = len(flagged_frames) / frame_count
if flag_ratio >= 0.5:
insights.append({
"category": "Widespread Manipulation",
"description": (
f"{len(flagged_frames)} out of {frame_count} analyzed frames "
f"({flag_ratio*100:.0f}%) flagged as deepfake. Manipulation "
"appears to span the majority of the video."
),
"severity": "critical",
})
scores.append(0.95)
elif flag_ratio >= 0.2:
insights.append({
"category": "Partial Manipulation",
"description": (
f"{len(flagged_frames)} out of {frame_count} frames flagged. "
"Manipulation may be limited to specific segments of the video."
),
"severity": "high",
})
scores.append(0.7)
# ── Rule: Face consistency ──
face_counts = sum(1 for fr in frame_results if fr.get("face_detected", False))
if frame_count > 0 and face_counts > 0:
face_ratio = face_counts / frame_count
if face_ratio < 0.5 and verdict in ("DEEPFAKE", "SUSPICIOUS"):
insights.append({
"category": "Face Detection Inconsistency",
"description": (
f"Faces detected in only {face_counts}/{frame_count} frames. "
"Inconsistent face detection can indicate face-swap artifacts "
"that confuse the detector in certain angles or lighting."
),
"severity": "medium",
})
scores.append(0.55)
# ── Rule: Authentic video ──
if verdict == "AUTHENTIC":
insights.append({
"category": "Temporal Consistency",
"description": (
f"All {frame_count} analyzed frames show consistent authenticity. "
"No significant manipulation artifacts detected across the timeline."
),
"severity": "low",
})
scores.append(0.1)
# ── Rule: Peak frame anomaly ──
if frame_results:
peak_frame = max(frame_results, key=lambda x: x.get("confidence", 0) if x.get("verdict") != "AUTHENTIC" else 0)
if peak_frame.get("verdict") == "DEEPFAKE" and peak_frame.get("confidence", 0) > 85:
insights.append({
"category": "Peak Anomaly Frame",
"description": (
f"Frame #{peak_frame.get('frame_index', 0)} at "
f"{peak_frame.get('timestamp', 0):.1f}s shows extremely high "
f"manipulation confidence ({peak_frame.get('confidence', 0):.1f}%). "
"This frame likely contains the most visible deepfake artifacts."
),
"severity": "high",
})
scores.append(0.8)
anomaly_score = max(scores) if scores else 0.0
return insights, anomaly_score
def _analyze_audio_insights(result: dict) -> tuple[list, float]:
"""Generate insights specific to audio detection."""
insights = []
scores = []
verdict = result.get("verdict", "UNKNOWN")
confidence = result.get("confidence", 0)
method = result.get("method", "unknown")
features = result.get("features", {})
# ── Rule: Spectral flatness anomaly ──
flatness = features.get("spectral_flatness_mean", 0)
if flatness > 0.15:
insights.append({
"category": "Spectral Flatness Anomaly",
"description": (
f"High spectral flatness ({flatness:.4f}) indicates the audio "
"has an unusually smooth frequency distribution. Natural human "
"speech has more tonal variation. This pattern is common in "
"TTS-generated audio."
),
"severity": "high",
})
scores.append(0.7)
elif flatness < 0.02 and verdict in ("DEEPFAKE", "SUSPICIOUS"):
insights.append({
"category": "Spectral Profile Anomaly",
"description": (
f"Very low spectral flatness ({flatness:.4f}) combined with "
"deepfake indicators. Some voice cloning systems produce audio "
"with concentrated tonal energy that differs from natural speech."
),
"severity": "medium",
})
scores.append(0.5)
# ── Rule: RMS energy consistency ──
rms_std = features.get("rms_std", 0)
if rms_std < 0.02 and verdict in ("DEEPFAKE", "SUSPICIOUS"):
insights.append({
"category": "Unnatural Energy Consistency",
"description": (
f"RMS energy standard deviation is very low ({rms_std:.4f}). "
"Natural human speech has significant volume variation (breathing, "
"emphasis, pauses). AI-generated audio often maintains unnaturally "
"consistent energy levels throughout."
),
"severity": "high",
})
scores.append(0.75)
# ── Rule: Zero-crossing rate ──
zcr_std = features.get("zcr_std", 0)
if zcr_std < 0.01 and verdict in ("DEEPFAKE", "SUSPICIOUS"):
insights.append({
"category": "Zero-Crossing Uniformity",
"description": (
f"Zero-crossing rate variance is abnormally low ({zcr_std:.4f}). "
"This suggests the audio lacks the micro-variations present in "
"natural vocal cord vibration patterns."
),
"severity": "medium",
})
scores.append(0.5)
# ── Rule: Wav2Vec2 model detection ──
if method == "wav2vec2_xlsr":
if verdict == "DEEPFAKE":
insights.append({
"category": "Neural Network Detection",
"description": (
"The Wav2Vec2-XLSR model (97.9% accuracy) classified this "
"audio as AI-generated with high confidence. This model is "
"trained on ElevenLabs, Amazon Polly, Kokoro, and Hume AI samples."
),
"severity": "high",
})
scores.append(0.85)
elif verdict == "AUTHENTIC":
insights.append({
"category": "Neural Verification",
"description": (
"The Wav2Vec2-XLSR model confirms this audio exhibits natural "
"human speech patterns. No voice cloning or TTS artifacts detected."
),
"severity": "low",
})
scores.append(0.1)
# ── Rule: Spectral centroid ──
centroid_std = features.get("spectral_centroid_std", 0)
if centroid_std < 200 and verdict in ("DEEPFAKE", "SUSPICIOUS"):
insights.append({
"category": "Frequency Monotony",
"description": (
f"Low spectral centroid variation ({centroid_std:.0f} Hz). "
"Natural speech shifts frequency content significantly during "
"different phonemes. Low variation suggests synthetic origin."
),
"severity": "medium",
})
scores.append(0.45)
anomaly_score = max(scores) if scores else 0.0
return insights, anomaly_score
# ══════════════════════════════════════════════════════════════
# ORIGINAL EXPLANATION FORMATTERS (preserved)
# ══════════════════════════════════════════════════════════════
def explain_image_result(result: dict) -> str:
"""Format an image detection result into a rich markdown explanation."""
verdict = result.get("verdict", "UNKNOWN")
confidence = result.get("confidence", 0)
details = result.get("details", [])
icon = _verdict_icon(verdict)
md = f"## {icon} Verdict: **{verdict}**\n\n"
md += f"### Confidence: {confidence:.1f}%\n\n"
md += _confidence_bar(confidence, verdict) + "\n\n"
md += "### Analysis Details\n\n"
for d in details:
md += f"- {d}\n"
# Add probability breakdown if available
probs = result.get("probs", {})
if probs:
md += "\n### Model Probabilities\n\n"
for label, prob in probs.items():
md += f"- **{label}**: {prob}%\n"
return md
def explain_video_result(result: dict) -> str:
"""Format a video detection result into markdown."""
verdict = result.get("verdict", "UNKNOWN")
confidence = result.get("confidence", 0)
details = result.get("details", [])
frame_count = result.get("frame_count", 0)
flagged = result.get("flagged_frames", [])
duration = result.get("duration", 0)
icon = _verdict_icon(verdict)
md = f"## {icon} Verdict: **{verdict}**\n\n"
md += f"### Confidence: {confidence:.1f}%\n\n"
md += _confidence_bar(confidence, verdict) + "\n\n"
md += f"**Video Duration:** {duration:.1f}s | "
md += f"**Frames Analysed:** {frame_count} | "
md += f"**Frames Flagged:** {len(flagged)}\n\n"
md += "### Analysis Details\n\n"
for d in details:
md += f"- {d}\n"
# Frame breakdown
frame_results = result.get("frame_results", [])
if frame_results:
md += "\n### Frame-by-Frame Results\n\n"
md += "| Frame | Time | Verdict | Confidence |\n"
md += "|-------|------|---------|------------|\n"
for fr in frame_results:
t = fr.get("timestamp", 0)
v = fr.get("verdict", "?")
c = fr.get("confidence", 0)
fi = fr.get("frame_index", 0)
flag = " ⚠️" if v == "DEEPFAKE" else ""
md += f"| #{fi} | {t:.1f}s | {v}{flag} | {c:.1f}% |\n"
return md
def explain_audio_result(result: dict) -> str:
"""Format an audio detection result into markdown."""
verdict = result.get("verdict", "UNKNOWN")
confidence = result.get("confidence", 0)
details = result.get("details", [])
method = result.get("method", "unknown")
icon = _verdict_icon(verdict)
md = f"## {icon} Verdict: **{verdict}**\n\n"
md += f"### Confidence: {confidence:.1f}%\n\n"
md += _confidence_bar(confidence, verdict) + "\n\n"
md += f"**Detection Method:** {method.replace('_', ' ').title()}\n\n"
md += "### Analysis Details\n\n"
for d in details:
md += f"- {d}\n"
# Feature breakdown
features = result.get("features", {})
if features:
md += "\n### Audio Features\n\n"
md += "| Feature | Value |\n"
md += "|---------|-------|\n"
for k, v in features.items():
name = k.replace("_", " ").title()
if isinstance(v, float):
md += f"| {name} | {v:.4f} |\n"
else:
md += f"| {name} | {v} |\n"
return md
def explain_metadata_result(meta: dict) -> str:
"""Format metadata analysis into markdown."""
risk = meta.get("risk_score", 0)
has_exif = meta.get("has_exif", False)
indicators = meta.get("ai_indicators", [])
details = meta.get("details", [])
if risk >= 50:
icon = "πŸ”΄"
label = "HIGH RISK"
elif risk >= 25:
icon = "🟑"
label = "MODERATE RISK"
else:
icon = "🟒"
label = "LOW RISK"
md = f"## {icon} Metadata Risk: **{label}** ({risk:.0f}%)\n\n"
if indicators:
md += "### AI Indicators Found\n\n"
for ind in indicators:
md += f"- ⚠️ {ind}\n"
md += "\n"
md += "### Metadata Details\n\n"
for d in details:
md += f"- {d}\n"
# Raw EXIF table
exif = meta.get("exif_data", {})
if exif:
md += "\n### Raw Metadata Fields\n\n"
md += "| Field | Value |\n"
md += "|-------|-------|\n"
for k, v in list(exif.items())[:20]:
val = str(v)[:80]
md += f"| {k} | {val} |\n"
if len(exif) > 20:
md += f"\n*...and {len(exif) - 20} more fields*\n"
return md
# ── Helpers ───────────────────────────────────────────────────
def _verdict_icon(verdict: str) -> str:
return {
"DEEPFAKE": "πŸ”΄",
"SUSPICIOUS": "🟑",
"AUTHENTIC": "🟒",
"ERROR": "βšͺ",
}.get(verdict, "βšͺ")
def _verdict_color(verdict: str) -> str:
return {
"DEEPFAKE": "#ff5064",
"SUSPICIOUS": "#ffd23c",
"AUTHENTIC": "#00e6a0",
"ERROR": "#888",
}.get(verdict, "#888")
def _confidence_bar(confidence: float, verdict: str) -> str:
"""Generate a text-based confidence bar."""
filled = int(confidence / 5)
empty = 20 - filled
bar = "β–ˆ" * filled + "β–‘" * empty
return f"`{bar}` **{confidence:.1f}%**"
# Need numpy for video insight variance calculations
import numpy as np