Spaces:
Running
Running
| """ | |
| 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 | |