vertalius commited on
Commit
f9f78c7
·
verified ·
1 Parent(s): c25c4dd

Update utils.py

Browse files
Files changed (1) hide show
  1. utils.py +48 -39
utils.py CHANGED
@@ -2,6 +2,7 @@ import cv2
2
  import tempfile
3
  import numpy as np
4
  import os
 
5
  from animation_renderer import AnimationRenderer
6
 
7
  def process_image(image, pose_detector, skeleton_generator):
@@ -22,16 +23,17 @@ def process_image(image, pose_detector, skeleton_generator):
22
 
23
  def process_video(video_path, pose_detector, skeleton_generator):
24
  """
25
- Process video for pose detection and skeleton generation with improved error handling and chunked processing
 
26
  """
27
  cap = None
28
  out = None
29
  try:
30
  # Optimize video processing
31
- chunk_size = 5 # Process fewer frames at a time
32
- buffer_size = 512 * 1024 # Smaller buffer for stability
33
- cv2.setNumThreads(2) # Allow 2 threads for better performance
34
- cv2.ocl.setUseOpenCL(False) # Disable OpenCL to prevent crashes
35
 
36
  cap = cv2.VideoCapture(video_path)
37
  if not cap.isOpened():
@@ -50,7 +52,7 @@ def process_video(video_path, pose_detector, skeleton_generator):
50
  frame_width = target_width
51
  frame_height = int(frame_height * scale)
52
 
53
- # Create temporary file with better error handling
54
  try:
55
  temp_output = tempfile.NamedTemporaryFile(suffix='.mp4', delete=False)
56
  output_path = temp_output.name
@@ -68,24 +70,22 @@ def process_video(video_path, pose_detector, skeleton_generator):
68
  frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
69
  frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
70
  fps = int(cap.get(cv2.CAP_PROP_FPS))
71
- if fps == 0: # Handle GIF files which might report 0 fps
72
  fps = 30
73
 
74
- # Initialize animation renderer
75
  renderer = AnimationRenderer(fps=fps)
76
 
77
  # Create temporary file for processed video
78
  temp_output = tempfile.NamedTemporaryFile(suffix='.mp4', delete=False)
79
  output_path = temp_output.name
80
 
81
- # Initialize video writer with more efficient settings
82
- max_dimension = 480 # Reduced max dimension
83
  if frame_width > max_dimension or frame_height > max_dimension:
84
  scale = min(max_dimension / frame_width, max_dimension / frame_height)
85
  frame_width = int(frame_width * scale)
86
  frame_height = int(frame_height * scale)
87
 
88
- # Use MP4V codec which is more memory efficient
89
  fourcc = cv2.VideoWriter_fourcc(*'mp4v')
90
  out = cv2.VideoWriter(output_path, fourcc, min(fps, 30), (frame_width, frame_height))
91
 
@@ -103,21 +103,21 @@ def process_video(video_path, pose_detector, skeleton_generator):
103
  frame = cv2.resize(frame, (frame_width, frame_height))
104
 
105
  try:
106
- # Resize frame for better detection
107
  fh, fw = frame.shape[:2]
108
  if fw > 640:
109
  scale = 640 / fw
110
  frame = cv2.resize(frame, (640, int(fh * scale)))
111
 
112
- # Process frame with error handling and retry
113
  retries = 3
 
114
  while retries > 0:
115
  landmarks, annotated_frame = pose_detector.detect_video_frame(frame)
116
  if landmarks is not None:
117
  break
118
  retries -= 1
119
 
120
- # Ensure frame is properly encoded before writing
121
  if annotated_frame is not None:
122
  out.write(annotated_frame)
123
  else:
@@ -130,10 +130,13 @@ def process_video(video_path, pose_detector, skeleton_generator):
130
  renderer.add_keyframe(landmarks, pose_detector.pose_connections, frame_time)
131
  except Exception as e:
132
  print(f"Frame {frame_count} skeleton generation error: {str(e)}")
 
133
  if animation_frames:
134
  animation_frames.append(animation_frames[-1])
135
- elif animation_frames:
136
- animation_frames.append(animation_frames[-1])
 
 
137
 
138
  except Exception as e:
139
  print(f"Frame {frame_count} processing error: {str(e)}")
@@ -142,19 +145,18 @@ def process_video(video_path, pose_detector, skeleton_generator):
142
  frame_count += 1
143
  frame_time = frame_count / fps
144
 
145
- if frame_count > 1000: # Safety limit for very long videos
146
  break
147
 
148
- # Release resources
149
  if cap is not None:
150
  cap.release()
151
  if out is not None:
152
  out.release()
153
 
154
- # Convert output video to proper format using more compatible settings
155
  converted_output = tempfile.NamedTemporaryFile(suffix='.mp4', delete=False)
156
  os.system(f'ffmpeg -y -i {output_path} -vcodec libx264 -preset ultrafast -pix_fmt yuv420p {converted_output.name}')
157
- os.unlink(output_path) # Remove the original output
158
 
159
  return converted_output.name, animation_frames
160
 
@@ -169,39 +171,38 @@ def process_video(video_path, pose_detector, skeleton_generator):
169
  def process_gif(gif_path, pose_detector, skeleton_generator):
170
  """
171
  Process GIF for pose detection and skeleton generation.
172
- This implementation uses Pillow to extract frames from the GIF, processes each frame,
173
  and creates a temporary MP4 video with the processed frames.
174
  """
175
  try:
176
  from PIL import Image, ImageSequence
177
- # Открываем GIF с помощью Pillow
178
  gif = Image.open(gif_path)
179
  frames = []
180
  for frame in ImageSequence.Iterator(gif):
181
  frame = frame.convert("RGB")
182
  frame_np = np.array(frame)
183
- # Переводим RGB в BGR (OpenCV использует BGR)
184
  frame_cv = cv2.cvtColor(frame_np, cv2.COLOR_RGB2BGR)
185
  frames.append(frame_cv)
186
 
187
  processed_frames = []
188
  animation_frames = []
189
- # Обрабатываем каждый кадр
190
  for frame in frames:
191
  landmarks, annotated_frame = pose_detector.detect_video_frame(frame)
192
  if annotated_frame is None:
193
  annotated_frame = frame
 
194
  processed_frames.append(annotated_frame)
 
195
  if landmarks is not None:
196
  skeleton_data = skeleton_generator.generate_skeleton(landmarks)
197
  else:
198
- # Если не удалось получить данные, используем предыдущий, если он есть
199
  skeleton_data = animation_frames[-1] if animation_frames else {}
200
  animation_frames.append(skeleton_data)
201
 
202
- # Сохраняем обработанные кадры в временное видео (MP4)
203
  height, width = processed_frames[0].shape[:2]
204
- fps = 10 # Подобранное значение для GIF
205
  fourcc = cv2.VideoWriter_fourcc(*'mp4v')
206
  temp_video = tempfile.NamedTemporaryFile(suffix='.mp4', delete=False)
207
  out = cv2.VideoWriter(temp_video.name, fourcc, fps, (width, height))
@@ -217,37 +218,42 @@ def process_gif(gif_path, pose_detector, skeleton_generator):
217
  def process_video_upload(uploaded_file, components, processed_file, db, is_gif, col1, col2):
218
  """
219
  Handle video/GIF file upload processing.
220
- Для GIF-файлов оригинальное воспроизведение заменяется на обработанное видео,
221
- чтобы обеспечить корректное отображение анимации со скелетом на каждом кадре.
222
  """
223
  pose_detector, skeleton_generator, animation_exporter = components
 
224
  # Считываем байты файла
225
  file_bytes = uploaded_file.read()
226
- # Сохраняем в временный файл
227
- temp_input = tempfile.NamedTemporaryFile(suffix=('.gif' if is_gif else '.mp4'), delete=False)
 
 
 
 
 
 
 
 
 
 
 
228
  temp_input.write(file_bytes)
229
  temp_input.seek(0)
230
  video_path = temp_input.name
231
 
232
  if is_gif:
233
  processed_video_path, animation_frames = process_gif(video_path, pose_detector, skeleton_generator)
234
- with col1:
235
- if processed_video_path:
236
- with open(processed_video_path, "rb") as f:
237
- st.video(f.read())
238
- else:
239
- st.error("Error processing GIF.")
240
  else:
241
- with col1:
242
- st.video(file_bytes)
243
  processed_video_path, animation_frames = process_video(video_path, pose_detector, skeleton_generator)
244
 
245
  if not animation_frames:
246
  raise ValueError("No poses detected in the video/gif")
247
 
 
248
  save_video_data(db, processed_file.id, animation_frames)
249
  animation_data_binary = animation_exporter.export_animation(animation_frames)
250
 
 
251
  with col2:
252
  if processed_video_path:
253
  with open(processed_video_path, "rb") as f:
@@ -270,6 +276,9 @@ def save_animation_data(db, file_id: int, skeleton_data: dict):
270
  def save_video_data(db, file_id: int, animation_frames: list):
271
  from database import PoseData
272
  for frame_num, frame_data in enumerate(animation_frames):
 
 
 
273
  pose_data = PoseData(file_id=file_id, frame_number=frame_num, landmarks=frame_data)
274
  db.add(pose_data)
275
  db.commit()
 
2
  import tempfile
3
  import numpy as np
4
  import os
5
+ import streamlit as st
6
  from animation_renderer import AnimationRenderer
7
 
8
  def process_image(image, pose_detector, skeleton_generator):
 
23
 
24
  def process_video(video_path, pose_detector, skeleton_generator):
25
  """
26
+ Process video for pose detection and skeleton generation
27
+ with improved error handling and chunked processing
28
  """
29
  cap = None
30
  out = None
31
  try:
32
  # Optimize video processing
33
+ chunk_size = 5
34
+ buffer_size = 512 * 1024
35
+ cv2.setNumThreads(2)
36
+ cv2.ocl.setUseOpenCL(False)
37
 
38
  cap = cv2.VideoCapture(video_path)
39
  if not cap.isOpened():
 
52
  frame_width = target_width
53
  frame_height = int(frame_height * scale)
54
 
55
+ # Create temporary file
56
  try:
57
  temp_output = tempfile.NamedTemporaryFile(suffix='.mp4', delete=False)
58
  output_path = temp_output.name
 
70
  frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
71
  frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
72
  fps = int(cap.get(cv2.CAP_PROP_FPS))
73
+ if fps == 0:
74
  fps = 30
75
 
 
76
  renderer = AnimationRenderer(fps=fps)
77
 
78
  # Create temporary file for processed video
79
  temp_output = tempfile.NamedTemporaryFile(suffix='.mp4', delete=False)
80
  output_path = temp_output.name
81
 
82
+ # Initialize video writer
83
+ max_dimension = 480
84
  if frame_width > max_dimension or frame_height > max_dimension:
85
  scale = min(max_dimension / frame_width, max_dimension / frame_height)
86
  frame_width = int(frame_width * scale)
87
  frame_height = int(frame_height * scale)
88
 
 
89
  fourcc = cv2.VideoWriter_fourcc(*'mp4v')
90
  out = cv2.VideoWriter(output_path, fourcc, min(fps, 30), (frame_width, frame_height))
91
 
 
103
  frame = cv2.resize(frame, (frame_width, frame_height))
104
 
105
  try:
 
106
  fh, fw = frame.shape[:2]
107
  if fw > 640:
108
  scale = 640 / fw
109
  frame = cv2.resize(frame, (640, int(fh * scale)))
110
 
111
+ # Process frame
112
  retries = 3
113
+ landmarks, annotated_frame = None, None
114
  while retries > 0:
115
  landmarks, annotated_frame = pose_detector.detect_video_frame(frame)
116
  if landmarks is not None:
117
  break
118
  retries -= 1
119
 
120
+ # Write to output
121
  if annotated_frame is not None:
122
  out.write(annotated_frame)
123
  else:
 
130
  renderer.add_keyframe(landmarks, pose_detector.pose_connections, frame_time)
131
  except Exception as e:
132
  print(f"Frame {frame_count} skeleton generation error: {str(e)}")
133
+ # Если возникла ошибка, используем последний корректный кадр
134
  if animation_frames:
135
  animation_frames.append(animation_frames[-1])
136
+ else:
137
+ # Нет новых landmarks, дублируем предыдущий, если есть
138
+ if animation_frames:
139
+ animation_frames.append(animation_frames[-1])
140
 
141
  except Exception as e:
142
  print(f"Frame {frame_count} processing error: {str(e)}")
 
145
  frame_count += 1
146
  frame_time = frame_count / fps
147
 
148
+ if frame_count > 1000: # Safety limit
149
  break
150
 
 
151
  if cap is not None:
152
  cap.release()
153
  if out is not None:
154
  out.release()
155
 
156
+ # Convert output video to x264
157
  converted_output = tempfile.NamedTemporaryFile(suffix='.mp4', delete=False)
158
  os.system(f'ffmpeg -y -i {output_path} -vcodec libx264 -preset ultrafast -pix_fmt yuv420p {converted_output.name}')
159
+ os.unlink(output_path)
160
 
161
  return converted_output.name, animation_frames
162
 
 
171
  def process_gif(gif_path, pose_detector, skeleton_generator):
172
  """
173
  Process GIF for pose detection and skeleton generation.
174
+ Uses Pillow to extract frames, processes each frame,
175
  and creates a temporary MP4 video with the processed frames.
176
  """
177
  try:
178
  from PIL import Image, ImageSequence
 
179
  gif = Image.open(gif_path)
180
  frames = []
181
  for frame in ImageSequence.Iterator(gif):
182
  frame = frame.convert("RGB")
183
  frame_np = np.array(frame)
 
184
  frame_cv = cv2.cvtColor(frame_np, cv2.COLOR_RGB2BGR)
185
  frames.append(frame_cv)
186
 
187
  processed_frames = []
188
  animation_frames = []
 
189
  for frame in frames:
190
  landmarks, annotated_frame = pose_detector.detect_video_frame(frame)
191
  if annotated_frame is None:
192
  annotated_frame = frame
193
+
194
  processed_frames.append(annotated_frame)
195
+
196
  if landmarks is not None:
197
  skeleton_data = skeleton_generator.generate_skeleton(landmarks)
198
  else:
199
+ # Если не удалось получить новые landmarks, берём предыдущий скелет
200
  skeleton_data = animation_frames[-1] if animation_frames else {}
201
  animation_frames.append(skeleton_data)
202
 
203
+ # Собираем обработанные кадры в MP4
204
  height, width = processed_frames[0].shape[:2]
205
+ fps = 10
206
  fourcc = cv2.VideoWriter_fourcc(*'mp4v')
207
  temp_video = tempfile.NamedTemporaryFile(suffix='.mp4', delete=False)
208
  out = cv2.VideoWriter(temp_video.name, fourcc, fps, (width, height))
 
218
  def process_video_upload(uploaded_file, components, processed_file, db, is_gif, col1, col2):
219
  """
220
  Handle video/GIF file upload processing.
221
+ Shows the original file in the left column and processed MP4 in the right column.
 
222
  """
223
  pose_detector, skeleton_generator, animation_exporter = components
224
+
225
  # Считываем байты файла
226
  file_bytes = uploaded_file.read()
227
+
228
+ # В зависимости от того, GIF это или нет,
229
+ # в "Original" показываем либо st.image (для GIF), либо st.video (для обычного видео).
230
+ with col1:
231
+ if is_gif:
232
+ st.image(file_bytes, use_column_width=True)
233
+ else:
234
+ st.video(file_bytes)
235
+
236
+ # Сохраняем во временный файл для дальнейшей обработки
237
+ temp_input = tempfile.NamedTemporaryFile(
238
+ suffix=('.gif' if is_gif else '.mp4'), delete=False
239
+ )
240
  temp_input.write(file_bytes)
241
  temp_input.seek(0)
242
  video_path = temp_input.name
243
 
244
  if is_gif:
245
  processed_video_path, animation_frames = process_gif(video_path, pose_detector, skeleton_generator)
 
 
 
 
 
 
246
  else:
 
 
247
  processed_video_path, animation_frames = process_video(video_path, pose_detector, skeleton_generator)
248
 
249
  if not animation_frames:
250
  raise ValueError("No poses detected in the video/gif")
251
 
252
+ # Сохраняем данные в БД
253
  save_video_data(db, processed_file.id, animation_frames)
254
  animation_data_binary = animation_exporter.export_animation(animation_frames)
255
 
256
+ # Показываем результат (MP4) в правой колонке
257
  with col2:
258
  if processed_video_path:
259
  with open(processed_video_path, "rb") as f:
 
276
  def save_video_data(db, file_id: int, animation_frames: list):
277
  from database import PoseData
278
  for frame_num, frame_data in enumerate(animation_frames):
279
+ # frame_data может быть пустым словарём, если не удалось получить landmarks
280
+ if not frame_data:
281
+ frame_data = {}
282
  pose_data = PoseData(file_id=file_id, frame_number=frame_num, landmarks=frame_data)
283
  db.add(pose_data)
284
  db.commit()