Upload 2 files
Browse files- tabs/tab_fft.py +101 -0
- tabs/tab_video_analysis.py +154 -0
tabs/tab_fft.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
# -*- coding: utf-8 -*-
|
| 3 |
+
"""
|
| 4 |
+
Help Tab
|
| 5 |
+
|
| 6 |
+
Created on Sat Nov 8 11:58:29 2025
|
| 7 |
+
@author: standarduser
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
import gradio as gr
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def create_tab_image_fft(tab_label):
|
| 14 |
+
"""Creates a tab for help text"""
|
| 15 |
+
with gr.TabItem(tab_label):
|
| 16 |
+
gr.Markdown("""
|
| 17 |
+
# SVAT - Synthetic Video Analyze Tool
|
| 18 |
+
|
| 19 |
+
## Quick Start
|
| 20 |
+
|
| 21 |
+
1. **Load Video:** Go to "Video-Frames" tab and upload a video
|
| 22 |
+
2. **Navigate Frames:** Use slider or buttons to move through frames
|
| 23 |
+
3. **Apply Transformations:** Click transformation buttons to analyze frames
|
| 24 |
+
4. **Annotate:** Draw on frames in "Annotations" tab
|
| 25 |
+
5. **Analyze Video:** Use "Video Analysis" tab for global analysis
|
| 26 |
+
|
| 27 |
+
## Frame Transformations
|
| 28 |
+
|
| 29 |
+
### Laplacian High-Pass
|
| 30 |
+
Emphasizes high-frequency details and edges. Useful for detecting sharpness artifacts.
|
| 31 |
+
|
| 32 |
+
### FFT Spectrum
|
| 33 |
+
Shows frequency domain representation with viridis colormap (blue-green-yellow).
|
| 34 |
+
Reveals periodic patterns and compression artifacts.
|
| 35 |
+
|
| 36 |
+
### Error Level Analysis (ELA)
|
| 37 |
+
Detects JPEG compression artifacts by re-compressing the image.
|
| 38 |
+
Lower quality = more visible differences in manipulated areas.
|
| 39 |
+
|
| 40 |
+
### Wavelet Decomposition
|
| 41 |
+
Multi-scale frequency analysis showing LL, LH, HL, HH subbands.
|
| 42 |
+
Reveals different frequency components.
|
| 43 |
+
|
| 44 |
+
### Noise Extraction
|
| 45 |
+
Isolates high-frequency noise via high-pass filtering.
|
| 46 |
+
Shows noise patterns that might indicate generation artifacts.
|
| 47 |
+
|
| 48 |
+
### YCbCr Channels
|
| 49 |
+
Separates luminance (Y) and chrominance (Cb, Cr) channels.
|
| 50 |
+
Useful for detecting color space artifacts.
|
| 51 |
+
|
| 52 |
+
### Gradient Magnitude
|
| 53 |
+
Visualizes edge strength using Sobel operator.
|
| 54 |
+
Shows edge consistency.
|
| 55 |
+
|
| 56 |
+
### Histogram Stretching (CLAHE)
|
| 57 |
+
Adaptive contrast enhancement that preserves local details.
|
| 58 |
+
|
| 59 |
+
## Video Analysis
|
| 60 |
+
|
| 61 |
+
### Mean FFT
|
| 62 |
+
Calculates average FFT across all frames to detect:
|
| 63 |
+
- Consistent frequency patterns in AI-generated videos
|
| 64 |
+
- Generator-specific fingerprints
|
| 65 |
+
- Temporal artifacts
|
| 66 |
+
|
| 67 |
+
## Annotation Modes
|
| 68 |
+
|
| 69 |
+
**Per Frame (A):** Separate drawings for each frame
|
| 70 |
+
**Global (B):** One drawing overlaid on all frames
|
| 71 |
+
|
| 72 |
+
## Tips for AI Detection
|
| 73 |
+
|
| 74 |
+
- Look for **repeating patterns** in FFT spectrum
|
| 75 |
+
- Check **ELA** for inconsistent compression levels
|
| 76 |
+
- Use **Mean FFT** to find generator fingerprints
|
| 77 |
+
- Compare **noise patterns** between frames
|
| 78 |
+
- Watch for **unnatural frequency distributions**
|
| 79 |
+
|
| 80 |
+
## Keyboard Shortcuts
|
| 81 |
+
|
| 82 |
+
*Navigation:*
|
| 83 |
+
- Use frame slider for quick navigation
|
| 84 |
+
- Click ◀/▶ buttons for precise frame control
|
| 85 |
+
|
| 86 |
+
## System Requirements
|
| 87 |
+
|
| 88 |
+
- Python 3.8+
|
| 89 |
+
- Gradio 6.x
|
| 90 |
+
- OpenCV
|
| 91 |
+
- NumPy 2.x
|
| 92 |
+
- Pillow
|
| 93 |
+
- Matplotlib
|
| 94 |
+
|
| 95 |
+
## About
|
| 96 |
+
|
| 97 |
+
SVAT is designed to help identify synthetic/AI-generated video content through various image analysis techniques.
|
| 98 |
+
|
| 99 |
+
Version: 1.0
|
| 100 |
+
Updated: 2025
|
| 101 |
+
""")
|
tabs/tab_video_analysis.py
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
# -*- coding: utf-8 -*-
|
| 3 |
+
"""
|
| 4 |
+
Video Analysis Tab - Global analysis functions for entire videos
|
| 5 |
+
|
| 6 |
+
@author: standarduser
|
| 7 |
+
"""
|
| 8 |
+
import gradio as gr
|
| 9 |
+
import cv2
|
| 10 |
+
import numpy as np
|
| 11 |
+
from PIL import Image
|
| 12 |
+
import matplotlib.cm as cm
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def compute_mean_fft(frames, progress=gr.Progress()):
|
| 16 |
+
"""Computes mean FFT across all video frames"""
|
| 17 |
+
if not frames or len(frames) == 0:
|
| 18 |
+
return None, "No video loaded"
|
| 19 |
+
|
| 20 |
+
progress(0, desc="Starting Mean FFT calculation...")
|
| 21 |
+
|
| 22 |
+
# Initialize accumulator for FFT magnitudes
|
| 23 |
+
first_frame = frames[0]
|
| 24 |
+
if len(first_frame.shape) == 3:
|
| 25 |
+
gray = cv2.cvtColor(first_frame, cv2.COLOR_RGB2GRAY)
|
| 26 |
+
else:
|
| 27 |
+
gray = first_frame
|
| 28 |
+
|
| 29 |
+
h, w = gray.shape
|
| 30 |
+
fft_accumulator = np.zeros((h, w), dtype=np.float64)
|
| 31 |
+
|
| 32 |
+
# Process each frame
|
| 33 |
+
total_frames = len(frames)
|
| 34 |
+
for i, frame in enumerate(frames):
|
| 35 |
+
# Convert to grayscale
|
| 36 |
+
if len(frame.shape) == 3:
|
| 37 |
+
gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
|
| 38 |
+
else:
|
| 39 |
+
gray = frame
|
| 40 |
+
|
| 41 |
+
# Compute FFT
|
| 42 |
+
f_transform = np.fft.fft2(gray)
|
| 43 |
+
f_shift = np.fft.fftshift(f_transform)
|
| 44 |
+
|
| 45 |
+
# Magnitude spectrum (log scale)
|
| 46 |
+
magnitude_spectrum = np.abs(f_shift)
|
| 47 |
+
magnitude_spectrum = np.log1p(magnitude_spectrum)
|
| 48 |
+
|
| 49 |
+
# Accumulate
|
| 50 |
+
fft_accumulator += magnitude_spectrum
|
| 51 |
+
|
| 52 |
+
# Update progress
|
| 53 |
+
if i % 10 == 0: # Update every 10 frames
|
| 54 |
+
progress((i + 1) / total_frames, desc=f"Processing frame {i+1}/{total_frames}")
|
| 55 |
+
|
| 56 |
+
# Compute mean
|
| 57 |
+
mean_fft = fft_accumulator / total_frames
|
| 58 |
+
|
| 59 |
+
progress(1.0, desc="Applying colormap...")
|
| 60 |
+
|
| 61 |
+
# Normalize to 0-1
|
| 62 |
+
if np.max(mean_fft) > 0:
|
| 63 |
+
mean_fft_norm = mean_fft / np.max(mean_fft)
|
| 64 |
+
else:
|
| 65 |
+
mean_fft_norm = np.zeros_like(mean_fft)
|
| 66 |
+
|
| 67 |
+
# Apply viridis colormap
|
| 68 |
+
viridis = cm.get_cmap('viridis')
|
| 69 |
+
colored = viridis(mean_fft_norm)
|
| 70 |
+
|
| 71 |
+
# Convert to RGB and scale to 0-255
|
| 72 |
+
rgb = (colored[:, :, :3] * 255).astype(np.uint8)
|
| 73 |
+
result_image = Image.fromarray(rgb)
|
| 74 |
+
|
| 75 |
+
status_text = f"✓ Mean FFT calculated from {total_frames} frames"
|
| 76 |
+
|
| 77 |
+
return result_image, status_text
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def create_tab_video_analysis(tab_label):
|
| 81 |
+
"""Creates a tab for video-level analysis"""
|
| 82 |
+
with gr.TabItem(tab_label):
|
| 83 |
+
# State for video frames (will be shared from main tab)
|
| 84 |
+
video_frames = gr.State([])
|
| 85 |
+
mean_fft_result = gr.State(None)
|
| 86 |
+
|
| 87 |
+
gr.Markdown("# Video Analysis")
|
| 88 |
+
gr.Markdown("Global analysis functions that process the entire video")
|
| 89 |
+
|
| 90 |
+
with gr.Accordion("Mean FFT Analysis", open=True):
|
| 91 |
+
gr.Markdown("""
|
| 92 |
+
**Purpose:** Calculate the mean FFT (Fast Fourier Transform) across all video frames.
|
| 93 |
+
|
| 94 |
+
This analysis helps detect:
|
| 95 |
+
- Consistent frequency patterns in AI-generated videos
|
| 96 |
+
- Generator-specific artifacts
|
| 97 |
+
- Periodic noise or compression artifacts
|
| 98 |
+
|
| 99 |
+
*Note: This operation processes all frames and may take some time for long videos.*
|
| 100 |
+
""")
|
| 101 |
+
|
| 102 |
+
with gr.Row():
|
| 103 |
+
btn_calculate_mean_fft = gr.Button(
|
| 104 |
+
"🔬 Calculate Mean FFT",
|
| 105 |
+
variant="primary",
|
| 106 |
+
size="lg",
|
| 107 |
+
scale=1
|
| 108 |
+
)
|
| 109 |
+
status_text = gr.Textbox(
|
| 110 |
+
label="Status",
|
| 111 |
+
value="Ready - Load a video first",
|
| 112 |
+
interactive=False,
|
| 113 |
+
scale=2
|
| 114 |
+
)
|
| 115 |
+
|
| 116 |
+
with gr.Row():
|
| 117 |
+
mean_fft_image = gr.Image(
|
| 118 |
+
label="Mean FFT Spectrum",
|
| 119 |
+
type="pil",
|
| 120 |
+
height=500,
|
| 121 |
+
interactive=False, # Read-only
|
| 122 |
+
sources=[] # No upload sources
|
| 123 |
+
)
|
| 124 |
+
|
| 125 |
+
with gr.Row():
|
| 126 |
+
btn_download = gr.Button("💾 Download Result", size="sm")
|
| 127 |
+
|
| 128 |
+
# Placeholder for future analysis tools
|
| 129 |
+
with gr.Accordion("Future Analysis Tools", open=False):
|
| 130 |
+
gr.Markdown("""
|
| 131 |
+
**Coming soon:**
|
| 132 |
+
- Temporal Consistency Analysis
|
| 133 |
+
- Motion Pattern Detection
|
| 134 |
+
- Frame-to-Frame Similarity Analysis
|
| 135 |
+
- Audio-Video Synchronization Check
|
| 136 |
+
""")
|
| 137 |
+
|
| 138 |
+
# Event handlers
|
| 139 |
+
btn_calculate_mean_fft.click(
|
| 140 |
+
fn=compute_mean_fft,
|
| 141 |
+
inputs=[video_frames],
|
| 142 |
+
outputs=[mean_fft_image, status_text]
|
| 143 |
+
)
|
| 144 |
+
|
| 145 |
+
# Download functionality (triggers browser download)
|
| 146 |
+
# Note: In Gradio, the image component already has download capability
|
| 147 |
+
btn_download.click(
|
| 148 |
+
fn=lambda img: img,
|
| 149 |
+
inputs=[mean_fft_image],
|
| 150 |
+
outputs=[mean_fft_image]
|
| 151 |
+
)
|
| 152 |
+
|
| 153 |
+
# Return the video_frames state so it can be connected from the main tab
|
| 154 |
+
return video_frames
|