Werli commited on
Commit
8912092
·
verified ·
1 Parent(s): 35399c3
Files changed (2) hide show
  1. app.py +3 -8
  2. modules/video_processor.py +201 -206
app.py CHANGED
@@ -14,17 +14,12 @@ from modules.booru import create_booru_interface
14
  from modules.multi_comfy import create_multi_comfy
15
  from modules.media_handler import handle_single_media_upload, handle_multiple_media_uploads
16
 
17
- import logging
18
-
19
- logging.basicConfig(level=logging.WARNING)
20
- logging.getLogger("httpcore").setLevel(logging.WARNING)
21
-
22
  """ For GPU install all the requirements.txt and the following:
23
- pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 --index-url https://download.pytorch.org/whl/cu126
24
  pip install onnxruntime-gpu
25
  """
26
 
27
- """ It's recommended to create a venv:
28
  python -m venv venv
29
  venv\Scripts\activate
30
  pip install ...
@@ -850,4 +845,4 @@ with gr.Blocks(title=TITLE, css=css, theme="Werli/Purple-Crimson-Gradio-Theme",
850
  )
851
  gr.Markdown(NEXT_RESTART)
852
 
853
- demo.queue(max_size=5).launch(show_error=True, quiet=True, show_api=False)
 
14
  from modules.multi_comfy import create_multi_comfy
15
  from modules.media_handler import handle_single_media_upload, handle_multiple_media_uploads
16
 
 
 
 
 
 
17
  """ For GPU install all the requirements.txt and the following:
18
+ pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 --index-url https://download.pytorch.org/whl/cu126 or any other Torch version
19
  pip install onnxruntime-gpu
20
  """
21
 
22
+ """ It's recommended to create a venv if you want to use it offline:
23
  python -m venv venv
24
  venv\Scripts\activate
25
  pip install ...
 
845
  )
846
  gr.Markdown(NEXT_RESTART)
847
 
848
+ demo.queue(max_size=5).launch(show_error=True, show_api=False)
modules/video_processor.py CHANGED
@@ -1,206 +1,201 @@
1
- import os
2
- import cv2
3
- import tempfile
4
- from typing import List, Tuple, Optional
5
- from PIL import Image
6
- import logging
7
-
8
- # Configure logging
9
- logging.basicConfig(level=logging.INFO)
10
- logger = logging.getLogger(__name__)
11
-
12
- # Supported video formats
13
- SUPPORTED_VIDEO_FORMATS = ['.mp4', '.avi', '.mov', '.mkv', '.webm', '.flv', '.wmv', '.m4v']
14
-
15
- def is_video_file(file_path: str) -> bool:
16
- """Check if the file is a supported video format."""
17
- if not file_path:
18
- return False
19
- _, ext = os.path.splitext(file_path.lower())
20
- return ext in SUPPORTED_VIDEO_FORMATS
21
-
22
- def get_video_duration(video_path: str) -> float:
23
- """Get the duration of a video in seconds."""
24
- try:
25
- cap = cv2.VideoCapture(video_path)
26
- if not cap.isOpened():
27
- logger.error(f"Could not open video: {video_path}")
28
- return 0.0
29
-
30
- fps = cap.get(cv2.CAP_PROP_FPS)
31
- frame_count = cap.get(cv2.CAP_PROP_FRAME_COUNT)
32
-
33
- if fps <= 0:
34
- logger.warning(f"Invalid FPS for video {video_path}, using fallback method")
35
- # Fallback method: get duration directly
36
- duration = cap.get(cv2.CAP_PROP_POS_MSEC) / 1000.0
37
- # Seek to end to get duration
38
- cap.set(cv2.CAP_PROP_POS_AVI_RATIO, 1.0)
39
- duration = cap.get(cv2.CAP_PROP_POS_MSEC) / 1000.0
40
- else:
41
- duration = frame_count / fps
42
-
43
- cap.release()
44
- return max(0.0, duration) # Ensure non-negative duration
45
-
46
- except Exception as e:
47
- logger.error(f"Error getting video duration for {video_path}: {str(e)}")
48
- return 0.0
49
-
50
- def extract_frames_from_video(
51
- video_path: str,
52
- max_duration: int = 30,
53
- frame_interval: int = 1,
54
- output_dir: Optional[str] = None
55
- ) -> List[str]:
56
- """
57
- Extract frames from a video at specified intervals.
58
-
59
- Args:
60
- video_path: Path to the video file
61
- max_duration: Maximum duration to process (seconds)
62
- frame_interval: Interval between frames (seconds)
63
- output_dir: Directory to save frames (creates temp if None)
64
-
65
- Returns:
66
- List of paths to extracted frame images
67
- """
68
- if not os.path.exists(video_path):
69
- logger.error(f"Video file does not exist: {video_path}")
70
- return []
71
-
72
- if not is_video_file(video_path):
73
- logger.error(f"Unsupported video format: {video_path}")
74
- return []
75
-
76
- # Create output directory if not provided
77
- if output_dir is None:
78
- output_dir = tempfile.mkdtemp(prefix="video_frames_")
79
-
80
- try:
81
- # Get video info
82
- duration = get_video_duration(video_path)
83
- logger.info(f"Video duration: {duration:.2f} seconds")
84
-
85
- # Limit duration if necessary
86
- process_duration = min(duration, max_duration)
87
- logger.info(f"Processing {process_duration:.2f} seconds of video")
88
-
89
- # Open video
90
- cap = cv2.VideoCapture(video_path)
91
- if not cap.isOpened():
92
- logger.error(f"Could not open video: {video_path}")
93
- return []
94
-
95
- fps = cap.get(cv2.CAP_PROP_FPS)
96
- if fps <= 0:
97
- logger.error(f"Invalid FPS: {fps}")
98
- cap.release()
99
- return []
100
-
101
- # Calculate frame positions
102
- frame_positions = []
103
- current_time = 0
104
- while current_time < process_duration:
105
- frame_number = int(current_time * fps)
106
- frame_positions.append(frame_number)
107
- current_time += frame_interval
108
-
109
- logger.info(f"Extracting {len(frame_positions)} frames")
110
-
111
- # Extract frames
112
- frame_paths = []
113
- video_name = os.path.splitext(os.path.basename(video_path))[0]
114
-
115
- for i, frame_number in enumerate(frame_positions):
116
- # Set position to desired frame
117
- cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number)
118
-
119
- ret, frame = cap.read()
120
- if not ret:
121
- logger.warning(f"Could not read frame {frame_number}")
122
- continue
123
-
124
- # Convert BGR to RGB
125
- frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
126
-
127
- # Convert to PIL Image
128
- pil_image = Image.fromarray(frame_rgb)
129
-
130
- # Save frame
131
- frame_filename = f"{video_name}_frame_{i+1:03d}.png"
132
- frame_path = os.path.join(output_dir, frame_filename)
133
- pil_image.save(frame_path, 'PNG')
134
- frame_paths.append(frame_path)
135
-
136
- logger.debug(f"Saved frame {i+1}/{len(frame_positions)}: {frame_filename}")
137
-
138
- cap.release()
139
- logger.info(f"Successfully extracted {len(frame_paths)} frames from {video_path}")
140
- return frame_paths
141
-
142
- except Exception as e:
143
- logger.error(f"Error extracting frames from {video_path}: {str(e)}")
144
- return []
145
-
146
- def process_video_upload(video_path: str, max_duration: int = 30, frame_interval: int = 1) -> Tuple[List[str], str]:
147
- """
148
- Process a video upload and extract frames.
149
-
150
- Args:
151
- video_path: Path to the uploaded video
152
- max_duration: Maximum duration to process (seconds)
153
- frame_interval: Interval between frames (seconds)
154
-
155
- Returns:
156
- Tuple of (list of frame paths, output directory)
157
- """
158
- # Create temporary directory for frames
159
- output_dir = tempfile.mkdtemp(prefix="video_frames_")
160
-
161
- # Extract frames
162
- frame_paths = extract_frames_from_video(
163
- video_path,
164
- max_duration,
165
- frame_interval,
166
- output_dir
167
- )
168
-
169
- return frame_paths, output_dir
170
-
171
- def get_video_info(video_path: str) -> dict:
172
- """Get detailed information about a video file."""
173
- try:
174
- cap = cv2.VideoCapture(video_path)
175
- if not cap.isOpened():
176
- return {"error": "Could not open video"}
177
-
178
- fps = cap.get(cv2.CAP_PROP_FPS)
179
- frame_count = cap.get(cv2.CAP_PROP_FRAME_COUNT)
180
- width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
181
- height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
182
- duration = frame_count / fps if fps > 0 else 0
183
-
184
- cap.release()
185
-
186
- return {
187
- "duration": duration,
188
- "fps": fps,
189
- "frame_count": frame_count,
190
- "width": width,
191
- "height": height,
192
- "resolution": f"{width}x{height}"
193
- }
194
-
195
- except Exception as e:
196
- return {"error": str(e)}
197
-
198
- # Export functions
199
- __all__ = [
200
- 'is_video_file',
201
- 'get_video_duration',
202
- 'extract_frames_from_video',
203
- 'process_video_upload',
204
- 'get_video_info',
205
- 'SUPPORTED_VIDEO_FORMATS'
206
- ]
 
1
+ import os
2
+ import cv2
3
+ import tempfile
4
+ from typing import List, Tuple, Optional
5
+ from PIL import Image
6
+
7
+ # Supported video formats
8
+ SUPPORTED_VIDEO_FORMATS = ['.mp4', '.avi', '.mov', '.mkv', '.webm', '.flv', '.wmv', '.m4v']
9
+
10
+ def is_video_file(file_path: str) -> bool:
11
+ """Check if the file is a supported video format."""
12
+ if not file_path:
13
+ return False
14
+ _, ext = os.path.splitext(file_path.lower())
15
+ return ext in SUPPORTED_VIDEO_FORMATS
16
+
17
+ def get_video_duration(video_path: str) -> float:
18
+ """Get the duration of a video in seconds."""
19
+ try:
20
+ cap = cv2.VideoCapture(video_path)
21
+ if not cap.isOpened():
22
+ print(f"Could not open video: {video_path}")
23
+ return 0.0
24
+
25
+ fps = cap.get(cv2.CAP_PROP_FPS)
26
+ frame_count = cap.get(cv2.CAP_PROP_FRAME_COUNT)
27
+
28
+ if fps <= 0:
29
+ print(f"Invalid FPS for video {video_path}, using fallback method")
30
+ # Fallback method: get duration directly
31
+ duration = cap.get(cv2.CAP_PROP_POS_MSEC) / 1000.0
32
+ # Seek to end to get duration
33
+ cap.set(cv2.CAP_PROP_POS_AVI_RATIO, 1.0)
34
+ duration = cap.get(cv2.CAP_PROP_POS_MSEC) / 1000.0
35
+ else:
36
+ duration = frame_count / fps
37
+
38
+ cap.release()
39
+ return max(0.0, duration) # Ensure non-negative duration
40
+
41
+ except Exception as e:
42
+ print(f"Error getting video duration for {video_path}: {str(e)}")
43
+ return 0.0
44
+
45
+ def extract_frames_from_video(
46
+ video_path: str,
47
+ max_duration: int = 30,
48
+ frame_interval: int = 1,
49
+ output_dir: Optional[str] = None
50
+ ) -> List[str]:
51
+ """
52
+ Extract frames from a video at specified intervals.
53
+
54
+ Args:
55
+ video_path: Path to the video file
56
+ max_duration: Maximum duration to process (seconds)
57
+ frame_interval: Interval between frames (seconds)
58
+ output_dir: Directory to save frames (creates temp if None)
59
+
60
+ Returns:
61
+ List of paths to extracted frame images
62
+ """
63
+ if not os.path.exists(video_path):
64
+ print(f"Video file does not exist: {video_path}")
65
+ return []
66
+
67
+ if not is_video_file(video_path):
68
+ print(f"Unsupported video format: {video_path}")
69
+ return []
70
+
71
+ # Create output directory if not provided
72
+ if output_dir is None:
73
+ output_dir = tempfile.mkdtemp(prefix="video_frames_")
74
+
75
+ try:
76
+ # Get video info
77
+ duration = get_video_duration(video_path)
78
+ print(f"Video duration: {duration:.2f} seconds")
79
+
80
+ # Limit duration if necessary
81
+ process_duration = min(duration, max_duration)
82
+ print(f"Processing {process_duration:.2f} seconds of video")
83
+
84
+ # Open video
85
+ cap = cv2.VideoCapture(video_path)
86
+ if not cap.isOpened():
87
+ print(f"Could not open video: {video_path}")
88
+ return []
89
+
90
+ fps = cap.get(cv2.CAP_PROP_FPS)
91
+ if fps <= 0:
92
+ print(f"Invalid FPS: {fps}")
93
+ cap.release()
94
+ return []
95
+
96
+ # Calculate frame positions
97
+ frame_positions = []
98
+ current_time = 0
99
+ while current_time < process_duration:
100
+ frame_number = int(current_time * fps)
101
+ frame_positions.append(frame_number)
102
+ current_time += frame_interval
103
+
104
+ print(f"Extracting {len(frame_positions)} frames")
105
+
106
+ # Extract frames
107
+ frame_paths = []
108
+ video_name = os.path.splitext(os.path.basename(video_path))[0]
109
+
110
+ for i, frame_number in enumerate(frame_positions):
111
+ # Set position to desired frame
112
+ cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number)
113
+
114
+ ret, frame = cap.read()
115
+ if not ret:
116
+ print(f"Could not read frame {frame_number}")
117
+ continue
118
+
119
+ # Convert BGR to RGB
120
+ frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
121
+
122
+ # Convert to PIL Image
123
+ pil_image = Image.fromarray(frame_rgb)
124
+
125
+ # Save frame
126
+ frame_filename = f"{video_name}_frame_{i+1:03d}.png"
127
+ frame_path = os.path.join(output_dir, frame_filename)
128
+ pil_image.save(frame_path, 'PNG')
129
+ frame_paths.append(frame_path)
130
+
131
+ print(f"Saved frame {i+1}/{len(frame_positions)}: {frame_filename}")
132
+
133
+ cap.release()
134
+ print(f"Successfully extracted {len(frame_paths)} frames from {video_path}")
135
+ return frame_paths
136
+
137
+ except Exception as e:
138
+ print(f"Error extracting frames from {video_path}: {str(e)}")
139
+ return []
140
+
141
+ def process_video_upload(video_path: str, max_duration: int = 30, frame_interval: int = 1) -> Tuple[List[str], str]:
142
+ """
143
+ Process a video upload and extract frames.
144
+
145
+ Args:
146
+ video_path: Path to the uploaded video
147
+ max_duration: Maximum duration to process (seconds)
148
+ frame_interval: Interval between frames (seconds)
149
+
150
+ Returns:
151
+ Tuple of (list of frame paths, output directory)
152
+ """
153
+ # Create temporary directory for frames
154
+ output_dir = tempfile.mkdtemp(prefix="video_frames_")
155
+
156
+ # Extract frames
157
+ frame_paths = extract_frames_from_video(
158
+ video_path,
159
+ max_duration,
160
+ frame_interval,
161
+ output_dir
162
+ )
163
+
164
+ return frame_paths, output_dir
165
+
166
+ def get_video_info(video_path: str) -> dict:
167
+ """Get detailed information about a video file."""
168
+ try:
169
+ cap = cv2.VideoCapture(video_path)
170
+ if not cap.isOpened():
171
+ return {"error": "Could not open video"}
172
+
173
+ fps = cap.get(cv2.CAP_PROP_FPS)
174
+ frame_count = cap.get(cv2.CAP_PROP_FRAME_COUNT)
175
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
176
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
177
+ duration = frame_count / fps if fps > 0 else 0
178
+
179
+ cap.release()
180
+
181
+ return {
182
+ "duration": duration,
183
+ "fps": fps,
184
+ "frame_count": frame_count,
185
+ "width": width,
186
+ "height": height,
187
+ "resolution": f"{width}x{height}"
188
+ }
189
+
190
+ except Exception as e:
191
+ return {"error": str(e)}
192
+
193
+ # Export functions
194
+ __all__ = [
195
+ 'is_video_file',
196
+ 'get_video_duration',
197
+ 'extract_frames_from_video',
198
+ 'process_video_upload',
199
+ 'get_video_info',
200
+ 'SUPPORTED_VIDEO_FORMATS'
201
+ ]