Spaces:
Running
on
Zero
Running
on
Zero
File size: 6,817 Bytes
0ca05b5 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
"""
Video utilities for visualization.
"""
import os
import cv2
import numpy as np
import subprocess
from PIL import Image
def video_to_image_frames(input_video_path, save_directory=None, frames_per_second=1):
"""
Extracts image frames from a video file at the specified frame rate and saves them as JPEG format.
Supports regular video files, webcam captures, WebM files, and GIF files, including incomplete files.
Args:
input_video_path: Path to the input video file
save_directory: Directory to save extracted frames (default: None)
frames_per_second: Number of frames to extract per second (default: 1)
Returns: List of file paths to extracted frames
"""
extracted_frame_paths = []
# For GIF files, use PIL library for better handling
if input_video_path.lower().endswith('.gif'):
try:
print(f"Processing GIF file using PIL: {input_video_path}")
with Image.open(input_video_path) as gif_img:
# Get GIF properties
frame_duration_ms = gif_img.info.get('duration', 100) # Duration per frame in milliseconds
gif_frame_rate = 1000.0 / frame_duration_ms if frame_duration_ms > 0 else 10.0 # Convert to frame rate
print(f"GIF properties: {gif_img.n_frames} frames, {gif_frame_rate:.2f} FPS, {frame_duration_ms}ms per frame")
# Calculate sampling interval
sampling_interval = max(1, int(gif_frame_rate / frames_per_second)) if frames_per_second < gif_frame_rate else 1
saved_count = 0
for current_frame_index in range(gif_img.n_frames):
gif_img.seek(current_frame_index)
# Sample frames based on desired frame rate
if current_frame_index % sampling_interval == 0:
# Convert to RGB format if necessary
rgb_frame = gif_img.convert('RGB')
# Convert PIL image to numpy array
frame_ndarray = np.array(rgb_frame)
# Save frame as JPEG format
frame_output_path = os.path.join(save_directory, f"frame_{saved_count:06d}.jpg")
pil_image = Image.fromarray(frame_ndarray)
pil_image.save(frame_output_path, 'JPEG', quality=95)
extracted_frame_paths.append(frame_output_path)
saved_count += 1
if extracted_frame_paths:
print(f"Successfully extracted {len(extracted_frame_paths)} frames from GIF using PIL")
return extracted_frame_paths
except Exception as error:
print(f"PIL GIF extraction error: {str(error)}, falling back to OpenCV")
# For WebM files, use FFmpeg directly for more stable processing
if input_video_path.lower().endswith('.webm'):
try:
print(f"Processing WebM file using FFmpeg: {input_video_path}")
# Create a unique output pattern for the frames
output_frame_pattern = os.path.join(save_directory, "frame_%04d.jpg")
# Use FFmpeg to extract frames at specified frame rate
ffmpeg_command = [
"ffmpeg",
"-i", input_video_path,
"-vf", f"fps={frames_per_second}", # Specified frames per second
"-q:v", "2", # High quality
output_frame_pattern
]
# Run FFmpeg process
ffmpeg_process = subprocess.Popen(
ffmpeg_command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
process_stdout, process_stderr = ffmpeg_process.communicate()
# Collect all extracted frames
for filename in sorted(os.listdir(save_directory)):
if filename.startswith("frame_") and filename.endswith(".jpg"):
full_frame_path = os.path.join(save_directory, filename)
extracted_frame_paths.append(full_frame_path)
if extracted_frame_paths:
print(f"Successfully extracted {len(extracted_frame_paths)} frames from WebM using FFmpeg")
return extracted_frame_paths
print("FFmpeg extraction failed, falling back to OpenCV")
except Exception as error:
print(f"FFmpeg extraction error: {str(error)}, falling back to OpenCV")
# Standard OpenCV method for non-WebM files or as fallback
try:
video_capture = cv2.VideoCapture(input_video_path)
# For WebM files, try setting more robust decoder options
if input_video_path.lower().endswith('.webm'):
video_capture.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'VP80'))
source_fps = video_capture.get(cv2.CAP_PROP_FPS)
extraction_interval = max(1, int(source_fps / frames_per_second)) # Extract at specified frame rate
processed_frame_count = 0
# Set error mode to suppress console warnings
cv2.setLogLevel(0)
while True:
read_success, current_frame = video_capture.read()
if not read_success:
break
if processed_frame_count % extraction_interval == 0:
try:
# Additional check for valid frame data
if current_frame is not None and current_frame.size > 0:
rgb_converted_frame = cv2.cvtColor(current_frame, cv2.COLOR_BGR2RGB)
frame_output_path = os.path.join(save_directory, f"frame_{processed_frame_count:06d}.jpg")
cv2.imwrite(frame_output_path, cv2.cvtColor(rgb_converted_frame, cv2.COLOR_RGB2BGR))
extracted_frame_paths.append(frame_output_path)
except Exception as error:
print(f"Warning: Failed to process frame {processed_frame_count}: {str(error)}")
processed_frame_count += 1
# Safety limit to prevent infinite loops
if processed_frame_count > 1000:
break
video_capture.release()
print(f"Extracted {len(extracted_frame_paths)} frames from video using OpenCV")
except Exception as error:
print(f"Error extracting frames: {str(error)}")
return extracted_frame_paths |