Anish commited on
Commit
edd550f
·
1 Parent(s): ee84eed

[Feature Added] > anomaly_gif_maker. This feature detects the frame where the pipeline thinks it is AI, converts it to gif and shows a side by side comparision to the Original Video, while also annotating the 'Peak Glitch' on the AI RADIANCE MAP gif

Browse files
backend/app/ai/video/anomaly_gif_maker.py ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import imageio
3
+ import logging
4
+ import os
5
+ import uuid
6
+ from typing import List
7
+ import numpy as np
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+ def generate_anomaly_gif(frames: List[np.ndarray], center_idx: int) -> str:
12
+ try:
13
+ save_dir = "uploads/video_heatmaps"
14
+ os.makedirs(save_dir, exist_ok=True)
15
+
16
+ safe_filename = f"{uuid.uuid4().hex}_anomaly_clip.gif"
17
+ save_path = os.path.join(save_dir, safe_filename)
18
+
19
+ start_idx = max(0, center_idx - 10)
20
+ end_idx = min(len(frames), center_idx + 10)
21
+
22
+ gif_frames = []
23
+
24
+ for i in range(start_idx, end_idx):
25
+ frame = frames[i]
26
+
27
+ # 1. Take the grayscale X-Ray of the current frame
28
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
29
+ # 2. Extract sharp structural edges
30
+ laplacian = cv2.Laplacian(gray, cv2.CV_64F)
31
+ abs_laplacian = np.absolute(laplacian)
32
+
33
+ # 3. Normalize to force the highest anomaly to glow at maximum brightness (255)
34
+ laplacian_8u = cv2.normalize(abs_laplacian, None, 0, 255, cv2.NORM_MINMAX, dtype=cv2.CV_8U)
35
+ # 4. Blur it to turn sharp noise into soft glowing clouds
36
+ heatmap_blurred = cv2.GaussianBlur(laplacian_8u, (15, 15), 0)
37
+
38
+ # 5. Filter out natural background camera noise (anything under 70 brightness)
39
+ _, heatmap_mask = cv2.threshold(heatmap_blurred, 70, 255, cv2.THRESH_TOZERO)
40
+ # 6. Apply Jet Thermal Colors (Blue = natural, Red = AI Glitch)
41
+ colored_heatmap = cv2.applyColorMap(heatmap_mask, cv2.COLORMAP_JET)
42
+
43
+ # 7. Apply the thermal glow onto a copy of the video frame
44
+ heatmap_overlay = cv2.addWeighted(frame.copy(), 0.5, colored_heatmap, 0.7, 0)
45
+
46
+ # 8. Find the contours (puddles) of the highest glowing areas and draw Red Boxes
47
+ _, thresh = cv2.threshold(laplacian_8u, 100, 255, cv2.THRESH_BINARY)
48
+ contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
49
+ contours = sorted(contours, key=cv2.contourArea, reverse=True)[:3]
50
+
51
+ for contour in contours:
52
+ if cv2.contourArea(contour) > 50:
53
+ x, y, w, h = cv2.boundingRect(contour)
54
+ cv2.rectangle(heatmap_overlay, (x, y), (x+w, y+h), (0, 0, 255), 2)
55
+
56
+ # --- CREATE THE SIDE-BY-SIDE SPLIT SCREEN ---
57
+
58
+ # 9. Resize both halves by 50% so the final GIF file size stays small
59
+ h, w = frame.shape[:2]
60
+ small_original = cv2.resize(frame, (w//2, h//2))
61
+ small_heatmap = cv2.resize(heatmap_overlay, (w//2, h//2))
62
+
63
+ # 10. Add clear text labels to the top of both screens
64
+ cv2.putText(small_original, "ORIGINAL", (10, 25), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
65
+ cv2.putText(small_heatmap, "AI RADIANCE MAP", (10, 25), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2)
66
+
67
+ # 11. Add an extra flashing text warning when it reaches the absolute worst frame
68
+ if i == center_idx:
69
+ cv2.putText(small_heatmap, "PEAK GLITCH", (10, 55), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
70
+
71
+ # 12. Mathematically glue the two images together side-by-side using axis=1 (horizontal)
72
+ combined_frame = np.concatenate((small_original, small_heatmap), axis=1)
73
+
74
+ # 13. Convert from BGR back to RGB before saving so the GIF colors aren't inverted
75
+ rgb_frame = cv2.cvtColor(combined_frame, cv2.COLOR_BGR2RGB)
76
+ gif_frames.append(rgb_frame)
77
+
78
+ # 13b. If this is the absolute peak glitch, append it 8 extra times to create a 1.2-second "Freeze-Frame" pause!
79
+ if i == center_idx:
80
+ for _ in range(8):
81
+ gif_frames.append(rgb_frame)
82
+
83
+ # 14. Synthesize the final GIF animation (Slowed down to ~6.5fps for better readability, and loop=0 for infinite looping)
84
+ imageio.mimsave(save_path, gif_frames, format='GIF', duration=5, loop=0)
85
+
86
+ return save_path
87
+
88
+ except Exception as e:
89
+ logger.error(f"Failed to generate Anomaly GIF Clip: {str(e)}")
90
+ return None