Spaces:
Running
Running
Anish commited on
Commit ·
8bdb51c
1
Parent(s): edd550f
[Feature Updated] > Updated anomaly_gif_maker a lot, to be able to precisely extract frames from the video, where the model and pipeline think that part is AI.
Browse files
backend/app/ai/video/anomaly_gif_maker.py
CHANGED
|
@@ -20,30 +20,21 @@ def generate_anomaly_gif(frames: List[np.ndarray], center_idx: int) -> str:
|
|
| 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]
|
|
@@ -53,35 +44,28 @@ def generate_anomaly_gif(frames: List[np.ndarray], center_idx: int) -> str:
|
|
| 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 |
-
#
|
|
|
|
| 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 |
-
|
| 79 |
-
|
| 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 |
|
|
|
|
| 20 |
end_idx = min(len(frames), center_idx + 10)
|
| 21 |
|
| 22 |
gif_frames = []
|
| 23 |
+
# Create a list to track exactly how many seconds EVERY individual frame stays on screen
|
| 24 |
+
frame_durations = []
|
| 25 |
|
| 26 |
for i in range(start_idx, end_idx):
|
| 27 |
frame = frames[i]
|
| 28 |
|
|
|
|
| 29 |
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
|
|
|
| 30 |
laplacian = cv2.Laplacian(gray, cv2.CV_64F)
|
| 31 |
abs_laplacian = np.absolute(laplacian)
|
|
|
|
|
|
|
| 32 |
laplacian_8u = cv2.normalize(abs_laplacian, None, 0, 255, cv2.NORM_MINMAX, dtype=cv2.CV_8U)
|
|
|
|
| 33 |
heatmap_blurred = cv2.GaussianBlur(laplacian_8u, (15, 15), 0)
|
|
|
|
|
|
|
| 34 |
_, heatmap_mask = cv2.threshold(heatmap_blurred, 70, 255, cv2.THRESH_TOZERO)
|
|
|
|
| 35 |
colored_heatmap = cv2.applyColorMap(heatmap_mask, cv2.COLORMAP_JET)
|
|
|
|
|
|
|
| 36 |
heatmap_overlay = cv2.addWeighted(frame.copy(), 0.5, colored_heatmap, 0.7, 0)
|
| 37 |
|
|
|
|
| 38 |
_, thresh = cv2.threshold(laplacian_8u, 100, 255, cv2.THRESH_BINARY)
|
| 39 |
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
| 40 |
contours = sorted(contours, key=cv2.contourArea, reverse=True)[:3]
|
|
|
|
| 44 |
x, y, w, h = cv2.boundingRect(contour)
|
| 45 |
cv2.rectangle(heatmap_overlay, (x, y), (x+w, y+h), (0, 0, 255), 2)
|
| 46 |
|
|
|
|
|
|
|
|
|
|
| 47 |
h, w = frame.shape[:2]
|
| 48 |
small_original = cv2.resize(frame, (w//2, h//2))
|
| 49 |
small_heatmap = cv2.resize(heatmap_overlay, (w//2, h//2))
|
| 50 |
|
|
|
|
| 51 |
cv2.putText(small_original, "ORIGINAL", (10, 25), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
|
| 52 |
cv2.putText(small_heatmap, "AI RADIANCE MAP", (10, 25), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2)
|
| 53 |
|
| 54 |
+
# --- THE FIX ---
|
| 55 |
+
# If this is the peak glitch, assign it a massive 3-Second duration!
|
| 56 |
if i == center_idx:
|
| 57 |
cv2.putText(small_heatmap, "PEAK GLITCH", (10, 55), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
|
| 58 |
+
frame_durations.append(3.0) # Pause for 3.0 real seconds!
|
| 59 |
+
else:
|
| 60 |
+
# Normal frames zip by at fast normal speed (0.15 seconds per frame)
|
| 61 |
+
frame_durations.append(0.15)
|
| 62 |
|
|
|
|
| 63 |
combined_frame = np.concatenate((small_original, small_heatmap), axis=1)
|
|
|
|
|
|
|
| 64 |
rgb_frame = cv2.cvtColor(combined_frame, cv2.COLOR_BGR2RGB)
|
| 65 |
gif_frames.append(rgb_frame)
|
| 66 |
|
| 67 |
+
# We pass our explicit list of durations to tell the GIF EXACTLY how long to hold each frame
|
| 68 |
+
imageio.mimsave(save_path, gif_frames, format='GIF', duration=frame_durations, loop=0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
|
| 70 |
return save_path
|
| 71 |
|