Update app.py
Browse files
app.py
CHANGED
@@ -57,7 +57,6 @@ def compress_video(video_file, target_width, target_height, progress=gr.Progress
|
|
57 |
ret, frame = cap.read()
|
58 |
if not ret:
|
59 |
break
|
60 |
-
# Resize frame to target resolution
|
61 |
compressed_frame = cv2.resize(frame, (new_width, new_height))
|
62 |
out.write(compressed_frame)
|
63 |
if frame_idx % 10 == 0 or frame_idx == total_frames:
|
@@ -129,12 +128,10 @@ def generate_motion_csv(video_file, output_csv=None, progress=gr.Progress(), pro
|
|
129 |
iterations=3, poly_n=5, poly_sigma=1.2, flags=0)
|
130 |
prev_gray = curr_gray
|
131 |
|
132 |
-
# Compute median magnitude and angle.
|
133 |
mag, ang = cv2.cartToPolar(flow[..., 0], flow[..., 1], angleInDegrees=True)
|
134 |
median_mag = np.median(mag)
|
135 |
median_ang = np.median(ang)
|
136 |
|
137 |
-
# Compute "zoom factor": fraction of pixels moving away from center.
|
138 |
h, w = flow.shape[:2]
|
139 |
center_x, center_y = w / 2, h / 2
|
140 |
x_coords, y_coords = np.meshgrid(np.arange(w), np.arange(h))
|
@@ -223,7 +220,6 @@ def stabilize_video_using_csv(video_file, csv_file, zoom=1.0, vertical_only=Fals
|
|
223 |
if not ret:
|
224 |
break
|
225 |
|
226 |
-
# Optionally apply zoom (resize and center-crop)
|
227 |
if zoom != 1.0:
|
228 |
zoomed_frame = cv2.resize(frame, None, fx=zoom, fy=zoom, interpolation=cv2.INTER_LINEAR)
|
229 |
zoomed_h, zoomed_w = zoomed_frame.shape[:2]
|
@@ -236,7 +232,6 @@ def stabilize_video_using_csv(video_file, csv_file, zoom=1.0, vertical_only=Fals
|
|
236 |
dx = 0 # Only vertical stabilization.
|
237 |
transform = np.array([[1, 0, dx],
|
238 |
[0, 1, dy]], dtype=np.float32)
|
239 |
-
# Use BORDER_REPLICATE to avoid black borders
|
240 |
stabilized_frame = cv2.warpAffine(frame, transform, (width, height), borderMode=cv2.BORDER_REPLICATE)
|
241 |
|
242 |
out.write(stabilized_frame)
|
@@ -252,11 +247,12 @@ def stabilize_video_using_csv(video_file, csv_file, zoom=1.0, vertical_only=Fals
|
|
252 |
print(f"[INFO] Stabilized video saved to: {output_file} in {elapsed:.2f} seconds")
|
253 |
return output_file
|
254 |
|
255 |
-
def process_video_ai(video_file, zoom, vertical_only, compress_mode, target_width, target_height, progress=gr.Progress(track_tqdm=True)):
|
256 |
"""
|
257 |
Gradio interface function:
|
258 |
- Optionally compresses the video if compress_mode is True, resizing it to the chosen resolution.
|
259 |
- Generates motion data from the (possibly compressed) video.
|
|
|
260 |
- Stabilizes the video based on the generated motion data.
|
261 |
- If vertical_only is True, only vertical stabilization is applied.
|
262 |
|
@@ -274,10 +270,8 @@ def process_video_ai(video_file, zoom, vertical_only, compress_mode, target_widt
|
|
274 |
# If compression is enabled, compress the video first.
|
275 |
if compress_mode:
|
276 |
gr.Info("Compressing video before processing...")
|
277 |
-
# Compression phase uses progress 0 to 0.2
|
278 |
video_file = compress_video(video_file, target_width, target_height, progress=progress, progress_offset=0.0, progress_scale=0.2)
|
279 |
gr.Info("Video compression complete.")
|
280 |
-
# Set new progress offsets for subsequent phases.
|
281 |
motion_offset = 0.2
|
282 |
motion_scale = 0.4
|
283 |
stabilization_offset = 0.6
|
@@ -290,6 +284,31 @@ def process_video_ai(video_file, zoom, vertical_only, compress_mode, target_widt
|
|
290 |
|
291 |
csv_file = generate_motion_csv(video_file, progress=progress, progress_offset=motion_offset, progress_scale=motion_scale)
|
292 |
gr.Info("Motion CSV generated successfully.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
293 |
stabilized_path = stabilize_video_using_csv(video_file, csv_file, zoom=zoom, vertical_only=vertical_only,
|
294 |
progress=progress, progress_offset=stabilization_offset, progress_scale=stabilization_scale)
|
295 |
gr.Info("Video stabilization complete.")
|
@@ -301,7 +320,7 @@ def process_video_ai(video_file, zoom, vertical_only, compress_mode, target_widt
|
|
301 |
with gr.Blocks() as demo:
|
302 |
gr.Markdown("# AI-Powered Video Stabilization")
|
303 |
gr.Markdown(
|
304 |
-
"Upload a video, select a zoom factor, choose whether to apply only vertical stabilization, and optionally compress the video before processing. "
|
305 |
"If compressing, specify the target resolution (width and height) for the compressed video. "
|
306 |
"The system will generate motion data using an AI model (RAFT if available) and then stabilize the video with live progress updates and alerts."
|
307 |
)
|
@@ -309,7 +328,8 @@ with gr.Blocks() as demo:
|
|
309 |
with gr.Row():
|
310 |
with gr.Column():
|
311 |
video_input = gr.Video(label="Input Video")
|
312 |
-
zoom_slider = gr.Slider(minimum=1.0, maximum=
|
|
|
313 |
vertical_checkbox = gr.Checkbox(label="Vertical Stabilization Only", value=False)
|
314 |
compress_checkbox = gr.Checkbox(label="Compress Video Before Processing", value=False)
|
315 |
target_width = gr.Number(label="Target Width (px)", value=640)
|
@@ -322,7 +342,7 @@ with gr.Blocks() as demo:
|
|
322 |
|
323 |
process_button.click(
|
324 |
fn=process_video_ai,
|
325 |
-
inputs=[video_input, zoom_slider, vertical_checkbox, compress_checkbox, target_width, target_height],
|
326 |
outputs=[original_video, stabilized_video, logs_output]
|
327 |
)
|
328 |
|
|
|
57 |
ret, frame = cap.read()
|
58 |
if not ret:
|
59 |
break
|
|
|
60 |
compressed_frame = cv2.resize(frame, (new_width, new_height))
|
61 |
out.write(compressed_frame)
|
62 |
if frame_idx % 10 == 0 or frame_idx == total_frames:
|
|
|
128 |
iterations=3, poly_n=5, poly_sigma=1.2, flags=0)
|
129 |
prev_gray = curr_gray
|
130 |
|
|
|
131 |
mag, ang = cv2.cartToPolar(flow[..., 0], flow[..., 1], angleInDegrees=True)
|
132 |
median_mag = np.median(mag)
|
133 |
median_ang = np.median(ang)
|
134 |
|
|
|
135 |
h, w = flow.shape[:2]
|
136 |
center_x, center_y = w / 2, h / 2
|
137 |
x_coords, y_coords = np.meshgrid(np.arange(w), np.arange(h))
|
|
|
220 |
if not ret:
|
221 |
break
|
222 |
|
|
|
223 |
if zoom != 1.0:
|
224 |
zoomed_frame = cv2.resize(frame, None, fx=zoom, fy=zoom, interpolation=cv2.INTER_LINEAR)
|
225 |
zoomed_h, zoomed_w = zoomed_frame.shape[:2]
|
|
|
232 |
dx = 0 # Only vertical stabilization.
|
233 |
transform = np.array([[1, 0, dx],
|
234 |
[0, 1, dy]], dtype=np.float32)
|
|
|
235 |
stabilized_frame = cv2.warpAffine(frame, transform, (width, height), borderMode=cv2.BORDER_REPLICATE)
|
236 |
|
237 |
out.write(stabilized_frame)
|
|
|
247 |
print(f"[INFO] Stabilized video saved to: {output_file} in {elapsed:.2f} seconds")
|
248 |
return output_file
|
249 |
|
250 |
+
def process_video_ai(video_file, zoom, vertical_only, compress_mode, target_width, target_height, auto_zoom, progress=gr.Progress(track_tqdm=True)):
|
251 |
"""
|
252 |
Gradio interface function:
|
253 |
- Optionally compresses the video if compress_mode is True, resizing it to the chosen resolution.
|
254 |
- Generates motion data from the (possibly compressed) video.
|
255 |
+
- If auto_zoom is enabled, computes the optimal zoom level based on the maximum cumulative translation so that no black borders appear.
|
256 |
- Stabilizes the video based on the generated motion data.
|
257 |
- If vertical_only is True, only vertical stabilization is applied.
|
258 |
|
|
|
270 |
# If compression is enabled, compress the video first.
|
271 |
if compress_mode:
|
272 |
gr.Info("Compressing video before processing...")
|
|
|
273 |
video_file = compress_video(video_file, target_width, target_height, progress=progress, progress_offset=0.0, progress_scale=0.2)
|
274 |
gr.Info("Video compression complete.")
|
|
|
275 |
motion_offset = 0.2
|
276 |
motion_scale = 0.4
|
277 |
stabilization_offset = 0.6
|
|
|
284 |
|
285 |
csv_file = generate_motion_csv(video_file, progress=progress, progress_offset=motion_offset, progress_scale=motion_scale)
|
286 |
gr.Info("Motion CSV generated successfully.")
|
287 |
+
|
288 |
+
# If auto zoom is enabled, compute the optimal zoom factor.
|
289 |
+
if auto_zoom:
|
290 |
+
gr.Info("Auto Zoom Mode enabled. Computing optimal zoom factor...")
|
291 |
+
motion_data = read_motion_csv(csv_file)
|
292 |
+
dx_list = [abs(v[0]) for v in motion_data.values()]
|
293 |
+
dy_list = [abs(v[1]) for v in motion_data.values()]
|
294 |
+
max_dx = max(dx_list) if dx_list else 0
|
295 |
+
max_dy = max(dy_list) if dy_list else 0
|
296 |
+
cap = cv2.VideoCapture(video_file)
|
297 |
+
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
|
298 |
+
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
299 |
+
cap.release()
|
300 |
+
if width - 2*max_dx > 0:
|
301 |
+
zoom_x = width / (width - 2*max_dx)
|
302 |
+
else:
|
303 |
+
zoom_x = 1.0
|
304 |
+
if height - 2*max_dy > 0:
|
305 |
+
zoom_y = height / (height - 2*max_dy)
|
306 |
+
else:
|
307 |
+
zoom_y = 1.0
|
308 |
+
auto_zoom_factor = max(1.0, zoom_x, zoom_y)
|
309 |
+
gr.Info(f"Auto zoom factor computed: {auto_zoom_factor:.2f}")
|
310 |
+
zoom = auto_zoom_factor
|
311 |
+
|
312 |
stabilized_path = stabilize_video_using_csv(video_file, csv_file, zoom=zoom, vertical_only=vertical_only,
|
313 |
progress=progress, progress_offset=stabilization_offset, progress_scale=stabilization_scale)
|
314 |
gr.Info("Video stabilization complete.")
|
|
|
320 |
with gr.Blocks() as demo:
|
321 |
gr.Markdown("# AI-Powered Video Stabilization")
|
322 |
gr.Markdown(
|
323 |
+
"Upload a video, select a zoom factor (or use Auto Zoom Mode), choose whether to apply only vertical stabilization, and optionally compress the video before processing. "
|
324 |
"If compressing, specify the target resolution (width and height) for the compressed video. "
|
325 |
"The system will generate motion data using an AI model (RAFT if available) and then stabilize the video with live progress updates and alerts."
|
326 |
)
|
|
|
328 |
with gr.Row():
|
329 |
with gr.Column():
|
330 |
video_input = gr.Video(label="Input Video")
|
331 |
+
zoom_slider = gr.Slider(minimum=1.0, maximum=3.0, step=0.1, value=1.0, label="Zoom Factor (ignored if Auto Zoom enabled)")
|
332 |
+
auto_zoom_checkbox = gr.Checkbox(label="Auto Zoom Mode", value=False)
|
333 |
vertical_checkbox = gr.Checkbox(label="Vertical Stabilization Only", value=False)
|
334 |
compress_checkbox = gr.Checkbox(label="Compress Video Before Processing", value=False)
|
335 |
target_width = gr.Number(label="Target Width (px)", value=640)
|
|
|
342 |
|
343 |
process_button.click(
|
344 |
fn=process_video_ai,
|
345 |
+
inputs=[video_input, zoom_slider, vertical_checkbox, compress_checkbox, target_width, target_height, auto_zoom_checkbox],
|
346 |
outputs=[original_video, stabilized_video, logs_output]
|
347 |
)
|
348 |
|