Amanpreet commited on
Commit
4e2aa43
·
1 Parent(s): 440b9db
VideoToNPZ/gen_skes.py CHANGED
@@ -37,7 +37,6 @@ adj = adj_mx_from_skeleton(skeleton)
37
  joints_left, joints_right = [4, 5, 6, 11, 12, 13], [1, 2, 3, 14, 15, 16]
38
  kps_left, kps_right = [4, 5, 6, 11, 12, 13], [1, 2, 3, 14, 15, 16]
39
 
40
- # Set up signal handler for keyboard interrupt
41
  def signal_handler(sig, frame):
42
  print("\nInterrupted by user, shutting down...")
43
  if 'pool' in locals() and pool is not None:
@@ -67,8 +66,13 @@ def generate_skeletons(video=''):
67
  cap = cv2.VideoCapture(video)
68
  width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
69
  height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
 
70
 
 
 
 
71
  keypoints, scores = hrnet_pose(video, det_dim=416, gen_output=True)
 
72
  keypoints, scores, valid_frames = h36m_coco_format(keypoints, scores)
73
  re_kpts = revise_kpts(keypoints, scores, valid_frames)
74
  num_person = len(re_kpts)
@@ -78,15 +82,19 @@ def generate_skeletons(video=''):
78
  pad = (81 - 1) // 2
79
  causal_shift = 0
80
 
81
- prediction = gen_pose(re_kpts, valid_frames, width, height, model_pos, pad, causal_shift)
82
-
83
  print('Recording 3D Pose:')
 
 
 
 
 
 
 
 
 
 
84
 
85
- # Add a loading bar
86
- for i in tqdm(range(100)):
87
- time.sleep(0.01)
88
-
89
- # Create output directory with absolute path
90
  output_dir = os.path.abspath('../outputs/')
91
  print(f"Creating output directory: {output_dir}")
92
  os.makedirs(output_dir, exist_ok=True)
@@ -108,7 +116,6 @@ def arg_parse():
108
 
109
  if __name__ == "__main__":
110
  args = arg_parse()
111
- # Use the video path as-is if absolute, otherwise prepend data_root
112
  if os.path.isabs(args.video):
113
  video_path = args.video
114
  else:
 
37
  joints_left, joints_right = [4, 5, 6, 11, 12, 13], [1, 2, 3, 14, 15, 16]
38
  kps_left, kps_right = [4, 5, 6, 11, 12, 13], [1, 2, 3, 14, 15, 16]
39
 
 
40
  def signal_handler(sig, frame):
41
  print("\nInterrupted by user, shutting down...")
42
  if 'pool' in locals() and pool is not None:
 
66
  cap = cv2.VideoCapture(video)
67
  width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
68
  height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
69
+ total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
70
 
71
+ # 2D Keypoint Generation (handled by gen_video_kpts)
72
+ print('Generating 2D Keypoints:')
73
+ sys.stdout.flush()
74
  keypoints, scores = hrnet_pose(video, det_dim=416, gen_output=True)
75
+
76
  keypoints, scores, valid_frames = h36m_coco_format(keypoints, scores)
77
  re_kpts = revise_kpts(keypoints, scores, valid_frames)
78
  num_person = len(re_kpts)
 
82
  pad = (81 - 1) // 2
83
  causal_shift = 0
84
 
85
+ # 3D Pose Generation
 
86
  print('Recording 3D Pose:')
87
+ print(f"PROGRESS:100.00") # Start 3D at 100%
88
+ sys.stdout.flush()
89
+ total_valid_frames = len(valid_frames) if valid_frames else total_frames
90
+ prediction = gen_pose(re_kpts, valid_frames, width, height, model_pos, pad, causal_shift)
91
+ # Simulate 3D progress (replace with gen_pose loop if shared)
92
+ for i in range(total_valid_frames):
93
+ progress = 100 + ((i + 1) / total_valid_frames * 100) # 100-200% for 3D
94
+ print(f"PROGRESS:{progress:.2f}")
95
+ sys.stdout.flush()
96
+ time.sleep(0.01) # Placeholder; remove if gen_pose has its own loop
97
 
 
 
 
 
 
98
  output_dir = os.path.abspath('../outputs/')
99
  print(f"Creating output directory: {output_dir}")
100
  os.makedirs(output_dir, exist_ok=True)
 
116
 
117
  if __name__ == "__main__":
118
  args = arg_parse()
 
119
  if os.path.isabs(args.video):
120
  video_path = args.video
121
  else:
VideoToNPZ/lib/pose/hrnet/pose_estimation/gen_kpts.py CHANGED
@@ -174,15 +174,14 @@ def gen_video_kpts(video, det_dim=416, num_person=1, gen_output=False, batch_siz
174
 
175
  video_length = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
176
  print('Recording 2D pose ...')
177
- import sys
178
- from io import StringIO
179
 
180
  if animation:
181
  # Animation mode uses frame-by-frame processing like in the backup code
182
  kpts_result = []
183
  scores_result = []
184
 
185
- for i in tqdm(range(video_length)):
186
  ret, frame = cap.read()
187
  if not ret:
188
  break
@@ -192,6 +191,7 @@ def gen_video_kpts(video, det_dim=416, num_person=1, gen_output=False, batch_siz
192
 
193
  if bboxs is None or not bboxs.any():
194
  print('No person detected!')
 
195
  continue
196
 
197
  # Track people
@@ -248,10 +248,7 @@ def gen_video_kpts(video, det_dim=416, num_person=1, gen_output=False, batch_siz
248
  if key & 0xFF == ord('q'):
249
  break
250
  else:
251
- # Optimized batch processing with Queue
252
- old_stdout = sys.stdout
253
- sys.stdout = StringIO()
254
-
255
  frame_queue = mp.Queue(maxsize=batch_size * 2)
256
  loader_thread = Thread(target=frame_loader, args=(video, frame_queue, video_length))
257
  loader_thread.start()
@@ -268,11 +265,14 @@ def gen_video_kpts(video, det_dim=416, num_person=1, gen_output=False, batch_siz
268
  # GPU batch processing
269
  batch_frames = []
270
  with torch.no_grad():
271
- for _ in tqdm(range(video_length)):
272
  frame = frame_queue.get()
273
  if frame is None:
274
  break
275
  batch_frames.append(frame)
 
 
 
276
 
277
  if len(batch_frames) >= batch_size:
278
  kpts_batch, scores_batch = process_batch(batch_frames, human_model, pose_model,
@@ -283,6 +283,22 @@ def gen_video_kpts(video, det_dim=416, num_person=1, gen_output=False, batch_siz
283
  scores_result[frame_idx:frame_idx + 1] = scores[None, :num_person]
284
  frame_idx += 1
285
  batch_frames = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
286
 
287
  # Process remaining frames
288
  if batch_frames:
@@ -293,6 +309,9 @@ def gen_video_kpts(video, det_dim=416, num_person=1, gen_output=False, batch_siz
293
  kpts_result[frame_idx:frame_idx + 1] = kpts[None, :num_person]
294
  scores_result[frame_idx:frame_idx + 1] = scores[None, :num_person]
295
  frame_idx += 1
 
 
 
296
  else:
297
  # CPU batch processing with multiprocessing
298
  pool = mp.Pool(processes=mp.cpu_count())
@@ -302,11 +321,14 @@ def gen_video_kpts(video, det_dim=416, num_person=1, gen_output=False, batch_siz
302
 
303
  batch_frames = []
304
  with torch.no_grad():
305
- for _ in tqdm(range(video_length)):
306
  frame = frame_queue.get()
307
  if frame is None:
308
  break
309
  batch_frames.append(frame)
 
 
 
310
  if len(batch_frames) >= batch_size:
311
  kpts_batch, scores_batch = process_func(batch_frames)
312
  for kpts, scores in zip(kpts_batch, scores_batch):
@@ -314,6 +336,9 @@ def gen_video_kpts(video, det_dim=416, num_person=1, gen_output=False, batch_siz
314
  scores_result[frame_idx:frame_idx + 1] = scores[None, :num_person]
315
  frame_idx += 1
316
  batch_frames = []
 
 
 
317
 
318
  # Process remaining frames
319
  if batch_frames:
@@ -322,20 +347,22 @@ def gen_video_kpts(video, det_dim=416, num_person=1, gen_output=False, batch_siz
322
  kpts_result[frame_idx:frame_idx + 1] = kpts[None, :num_person]
323
  scores_result[frame_idx:frame_idx + 1] = scores[None, :num_person]
324
  frame_idx += 1
 
 
 
325
 
326
  pool.close()
327
  pool.join()
328
  except KeyboardInterrupt:
329
  print("\nInterrupted by user, shutting down...")
 
330
  if pool is not None:
331
  pool.terminate()
332
  pool.join()
333
  loader_thread.join()
334
- sys.stdout = old_stdout
335
  sys.exit(0)
336
 
337
  loader_thread.join()
338
- sys.stdout = old_stdout
339
 
340
  if gen_output and kpts_result.any():
341
  keypoints = kpts_result[:frame_idx].transpose(1, 0, 2, 3)
 
174
 
175
  video_length = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
176
  print('Recording 2D pose ...')
177
+ sys.stdout.flush() # Ensure initial message shows up immediately
 
178
 
179
  if animation:
180
  # Animation mode uses frame-by-frame processing like in the backup code
181
  kpts_result = []
182
  scores_result = []
183
 
184
+ for i in range(video_length):
185
  ret, frame = cap.read()
186
  if not ret:
187
  break
 
191
 
192
  if bboxs is None or not bboxs.any():
193
  print('No person detected!')
194
+ sys.stdout.flush()
195
  continue
196
 
197
  # Track people
 
248
  if key & 0xFF == ord('q'):
249
  break
250
  else:
251
+ # Optimized batch processing with Queue (no StringIO redirection)
 
 
 
252
  frame_queue = mp.Queue(maxsize=batch_size * 2)
253
  loader_thread = Thread(target=frame_loader, args=(video, frame_queue, video_length))
254
  loader_thread.start()
 
265
  # GPU batch processing
266
  batch_frames = []
267
  with torch.no_grad():
268
+ for i in range(video_length):
269
  frame = frame_queue.get()
270
  if frame is None:
271
  break
272
  batch_frames.append(frame)
273
+ progress = (i + 1) / video_length * 100
274
+ print(f"PROGRESS:{progress:.2f}")
275
+ sys.stdout.flush() # Force per-frame update
276
 
277
  if len(batch_frames) >= batch_size:
278
  kpts_batch, scores_batch = process_batch(batch_frames, human_model, pose_model,
 
283
  scores_result[frame_idx:frame_idx + 1] = scores[None, :num_person]
284
  frame_idx += 1
285
  batch_frames = []
286
+ progress = (frame_idx / video_length) * 100
287
+ print(f"PROGRESS:{progress:.2f}")
288
+ sys.stdout.flush() # Force after batch
289
+
290
+ # Process remaining frames
291
+ if batch_frames:
292
+ kpts_batch, scores_batch = process_batch(batch_frames, human_model, pose_model,
293
+ det_dim, num_person, args.thred_score,
294
+ use_fp16, device)
295
+ for kpts, scores in zip(kpts_batch, scores_batch):
296
+ kpts_result[frame_idx:frame_idx + 1] = kpts[None, :num_person]
297
+ scores_result[frame_idx:frame_idx + 1] = scores[None, :num_person]
298
+ frame_idx += 1
299
+ progress = (frame_idx / video_length) * 100
300
+ print(f"PROGRESS:{progress:.2f}")
301
+ sys.stdout.flush() # Force after batch
302
 
303
  # Process remaining frames
304
  if batch_frames:
 
309
  kpts_result[frame_idx:frame_idx + 1] = kpts[None, :num_person]
310
  scores_result[frame_idx:frame_idx + 1] = scores[None, :num_person]
311
  frame_idx += 1
312
+ progress = (frame_idx / video_length) * 100
313
+ print(f"PROGRESS:{progress:.2f}")
314
+ sys.stdout.flush() # Force final update
315
  else:
316
  # CPU batch processing with multiprocessing
317
  pool = mp.Pool(processes=mp.cpu_count())
 
321
 
322
  batch_frames = []
323
  with torch.no_grad():
324
+ for i in range(video_length):
325
  frame = frame_queue.get()
326
  if frame is None:
327
  break
328
  batch_frames.append(frame)
329
+ progress = (i + 1) / video_length * 100
330
+ print(f"PROGRESS:{progress:.2f}")
331
+ sys.stdout.flush() # Force per-frame update
332
  if len(batch_frames) >= batch_size:
333
  kpts_batch, scores_batch = process_func(batch_frames)
334
  for kpts, scores in zip(kpts_batch, scores_batch):
 
336
  scores_result[frame_idx:frame_idx + 1] = scores[None, :num_person]
337
  frame_idx += 1
338
  batch_frames = []
339
+ progress = (frame_idx / video_length) * 100
340
+ print(f"PROGRESS:{progress:.2f}")
341
+ sys.stdout.flush() # Force after batch
342
 
343
  # Process remaining frames
344
  if batch_frames:
 
347
  kpts_result[frame_idx:frame_idx + 1] = kpts[None, :num_person]
348
  scores_result[frame_idx:frame_idx + 1] = scores[None, :num_person]
349
  frame_idx += 1
350
+ progress = (frame_idx / video_length) * 100
351
+ print(f"PROGRESS:{progress:.2f}")
352
+ sys.stdout.flush() # Force final update
353
 
354
  pool.close()
355
  pool.join()
356
  except KeyboardInterrupt:
357
  print("\nInterrupted by user, shutting down...")
358
+ sys.stdout.flush()
359
  if pool is not None:
360
  pool.terminate()
361
  pool.join()
362
  loader_thread.join()
 
363
  sys.exit(0)
364
 
365
  loader_thread.join()
 
366
 
367
  if gen_output and kpts_result.any():
368
  keypoints = kpts_result[:frame_idx].transpose(1, 0, 2, 3)
app.py CHANGED
@@ -5,109 +5,187 @@ import os
5
  import time
6
  import shutil
7
  import tempfile
 
 
 
 
8
 
9
- def run_command(command, description, working_dir):
10
- st.write(f"Running command: {' '.join(command)}")
11
  try:
12
- start_time = time.time()
13
- process = subprocess.run(
 
 
14
  command,
15
- check=True,
16
  cwd=working_dir,
17
- capture_output=True,
18
- text=True
 
 
 
 
19
  )
20
- end_time = time.time()
21
- execution_time = end_time - start_time
22
- st.write(f"{description} completed in {execution_time:.2f} seconds")
23
- if process.stdout:
24
- st.text(f"Output:\n{process.stdout}")
25
- if process.stderr:
26
- st.text(f"Warnings:\n{process.stderr}")
27
- return True
28
 
29
- except subprocess.CalledProcessError as e:
30
- st.error(f"Error in {description}: {e.stderr}")
31
- return False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
  except Exception as e:
34
- st.error(f"Unexpected error in {description}: {str(e)}")
35
  return False
36
 
 
 
 
 
 
 
37
  def process_video(video_file):
38
- base_dir = os.path.dirname(os.path.abspath(__file__))
39
- gen_skes_path = os.path.join(base_dir, "VideoToNPZ", "gen_skes.py")
40
- convert_obj_path = os.path.join(base_dir, "convertNPZtoBVH", "conver_obj.py")
41
- convert_bvh_path = os.path.join(base_dir, "convertNPZtoBVH", "conver_bvh.py")
42
 
43
- st.write(f"Base directory: {base_dir}")
44
- st.write(f"gen_skes_path: {gen_skes_path}")
 
45
 
46
  for script_path in [gen_skes_path, convert_obj_path, convert_bvh_path]:
47
- if not os.path.exists(script_path):
48
  st.error(f"Required script not found: {script_path}")
49
  return None
50
 
51
  with tempfile.TemporaryDirectory() as tmp_dir:
52
- video_path = os.path.join(tmp_dir, "input_video.mp4")
53
  with open(video_path, "wb") as f:
54
  f.write(video_file.read())
55
 
56
- if not os.path.exists(video_path):
57
  st.error(f"Video file not found at: {video_path}")
58
  return None
59
- st.write(f"Video file saved at: {video_path}, size: {os.path.getsize(video_path)} bytes")
 
 
 
 
 
 
 
 
 
60
 
61
  pipeline_steps = [
62
- {
63
- "command": [sys.executable, gen_skes_path, "-v", video_path],
64
- "description": "Generating skeletons",
65
- "working_dir": os.path.dirname(gen_skes_path)
66
- },
67
- {
68
- "command": [sys.executable, convert_obj_path],
69
- "description": "Converting to OBJ sequence",
70
- "working_dir": os.path.dirname(convert_obj_path)
71
- },
72
- {
73
- "command": [sys.executable, convert_bvh_path],
74
- "description": "Converting to BVH",
75
- "working_dir": os.path.dirname(convert_bvh_path)
76
- }
77
  ]
78
 
79
- successful = 0
80
- failed = 0
81
- progress_bar = st.progress(0)
82
- status_text = st.empty()
83
 
84
  for i, step in enumerate(pipeline_steps):
85
- status_text.text(f"Processing: {step['description']}...")
86
- if run_command(step["command"], step["description"], step["working_dir"]):
87
- successful += 1
88
- if step["description"] == "Generating skeletons":
89
- npz_path = os.path.join(base_dir, "outputs", "npz", "input_video.npz")
90
- st.write(f"Expected NPZ file at: {npz_path}")
91
- if os.path.exists(npz_path):
92
- st.write(f"NPZ file found, size: {os.path.getsize(npz_path)} bytes")
93
- else:
94
- st.error("NPZ file not generated!")
95
- else:
96
- failed += 1
97
- progress_bar.progress((i + 1) / len(pipeline_steps))
98
-
99
- status_text.text("Processing complete!")
100
-
101
- bvh_output_dir = os.path.join(os.path.dirname(convert_bvh_path), "../outputs/bvh")
102
- bvh_file = os.path.join(bvh_output_dir, "output.bvh")
103
- st.write(f"Checking for BVH file at: {bvh_file}")
104
-
105
- if os.path.exists(bvh_file) and successful == len(pipeline_steps):
106
- return bvh_file
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  else:
108
- st.error("Failed to generate BVH file")
 
 
109
  return None
110
 
 
 
 
 
 
 
 
111
  def main():
112
  st.title("Video to BVH Converter")
113
  st.write("Upload a video file to convert it to BVH format")
@@ -118,25 +196,27 @@ def main():
118
  st.video(uploaded_file)
119
 
120
  if st.button("Convert to BVH"):
121
- with st.spinner("Processing video... This may take a few minutes"):
122
- bvh_file = process_video(uploaded_file)
123
 
124
- if bvh_file:
125
- with open(bvh_file, "rb") as f:
126
  st.download_button(
127
  label="Download BVH File",
128
  data=f,
129
  file_name="output.bvh",
130
- mime="application/octet-stream"
 
 
131
  )
132
- st.success("Conversion completed successfully!")
133
 
134
  st.markdown("""
135
  ### Instructions
136
  1. Upload a video file using the uploader above
137
  2. Click 'Convert to BVH' to start the conversion process
138
- 3. Wait for processing to complete
139
- 4. Download the resulting BVH file using the provided button
140
  """)
141
 
142
  if __name__ == "__main__":
 
5
  import time
6
  import shutil
7
  import tempfile
8
+ import threading
9
+ import queue
10
+ from datetime import datetime
11
+ from pathlib import Path
12
 
13
+ def run_command(command, working_dir, progress_bar, progress_text, step_start_progress, step_weight, show_progress=True):
 
14
  try:
15
+ env = os.environ.copy()
16
+ env["PYTHONUNBUFFERED"] = "1"
17
+
18
+ process = subprocess.Popen(
19
  command,
 
20
  cwd=working_dir,
21
+ stdout=subprocess.PIPE,
22
+ stderr=subprocess.PIPE,
23
+ text=True,
24
+ bufsize=1,
25
+ universal_newlines=True,
26
+ env=env
27
  )
 
 
 
 
 
 
 
 
28
 
29
+ stdout_queue = queue.Queue()
30
+ stderr_queue = queue.Queue()
31
+
32
+ def read_output(pipe, q, source):
33
+ for line in iter(pipe.readline, ''):
34
+ q.put((source, line))
35
+ pipe.close()
36
+
37
+ stdout_thread = threading.Thread(target=read_output, args=(process.stdout, stdout_queue, 'stdout'))
38
+ stderr_thread = threading.Thread(target=read_output, args=(process.stderr, stderr_queue, 'stderr'))
39
+ stdout_thread.daemon = True
40
+ stderr_thread.daemon = True
41
+ stdout_thread.start()
42
+ stderr_thread.start()
43
+
44
+ total_progress = step_start_progress
45
+ stderr_lines = []
46
+
47
+ while process.poll() is None or not (stdout_queue.empty() and stderr_queue.empty()):
48
+ try:
49
+ source, line = next((q.get_nowait() for q in [stdout_queue, stderr_queue] if not q.empty()), (None, None))
50
+ if line:
51
+ if source == 'stdout':
52
+ if show_progress and line.startswith("PROGRESS:"):
53
+ try:
54
+ progress = float(line.strip().split("PROGRESS:")[1]) / 100
55
+ if Path(command[1]).name == 'gen_skes.py':
56
+ # Scale 0-200% to 0-80%
57
+ adjusted_progress = step_start_progress + (progress / 2) * step_weight
58
+ else:
59
+ # Scale 0-100% to 20% for conver_bvh.py
60
+ adjusted_progress = step_start_progress + (progress * step_weight)
61
+ total_progress = min(adjusted_progress, step_start_progress + step_weight)
62
+ progress_bar.progress(total_progress)
63
+ progress_text.text(f"{int(total_progress * 100)}%")
64
+ except ValueError:
65
+ pass
66
+ elif source == 'stderr':
67
+ stderr_lines.append(line.strip())
68
+ except queue.Empty:
69
+ time.sleep(0.01)
70
+
71
+ stdout_thread.join()
72
+ stderr_thread.join()
73
+
74
+ if process.returncode != 0:
75
+ stderr_output = '\n'.join(stderr_lines)
76
+ st.error(f"Error in {Path(command[1]).name}:\n{stderr_output}")
77
+ return False
78
+
79
+ if show_progress:
80
+ progress_bar.progress(step_start_progress + step_weight)
81
+ progress_text.text(f"{int((step_start_progress + step_weight) * 100)}%")
82
+ return True
83
 
84
  except Exception as e:
85
+ st.error(f"Exception in {Path(command[1]).name}: {str(e)}")
86
  return False
87
 
88
+ def cleanup_output_folder(output_dir, delay=1800):
89
+ time.sleep(delay)
90
+ if os.path.exists(output_dir):
91
+ shutil.rmtree(output_dir, ignore_errors=True)
92
+ print(f"Deleted temporary output folder after timeout: {output_dir}")
93
+
94
  def process_video(video_file):
95
+ base_dir = Path(__file__).parent.resolve()
 
 
 
96
 
97
+ gen_skes_path = base_dir / "VideoToNPZ" / "gen_skes.py"
98
+ convert_obj_path = base_dir / "convertNPZtoBVH" / "conver_obj.py"
99
+ convert_bvh_path = base_dir / "convertNPZtoBVH" / "conver_bvh.py"
100
 
101
  for script_path in [gen_skes_path, convert_obj_path, convert_bvh_path]:
102
+ if not script_path.exists():
103
  st.error(f"Required script not found: {script_path}")
104
  return None
105
 
106
  with tempfile.TemporaryDirectory() as tmp_dir:
107
+ video_path = Path(tmp_dir) / "input_video.mp4"
108
  with open(video_path, "wb") as f:
109
  f.write(video_file.read())
110
 
111
+ if not video_path.exists():
112
  st.error(f"Video file not found at: {video_path}")
113
  return None
114
+
115
+ timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
116
+ output_dir = base_dir / f"outputs_{timestamp}"
117
+ output_dir.mkdir(exist_ok=True)
118
+
119
+ if not os.access(output_dir, os.W_OK):
120
+ st.error(f"Cannot write to output directory: {output_dir}")
121
+ return None
122
+
123
+ default_output_dir = base_dir / "outputs"
124
 
125
  pipeline_steps = [
126
+ {"command": [sys.executable, str(gen_skes_path), "-v", str(video_path)], "working_dir": gen_skes_path.parent, "weight": 0.8, "show_progress": True},
127
+ {"command": [sys.executable, str(convert_obj_path), "--output-dir", str(output_dir)], "working_dir": convert_obj_path.parent, "weight": 0.0, "show_progress": False},
128
+ {"command": [sys.executable, str(convert_bvh_path), "--output-dir", str(output_dir)], "working_dir": convert_bvh_path.parent, "weight": 0.2, "show_progress": True}
 
 
 
 
 
 
 
 
 
 
 
 
129
  ]
130
 
131
+ progress_bar = st.progress(0.0)
132
+ progress_text = st.empty() # Added for percentage display
133
+ total_progress = 0.0
 
134
 
135
  for i, step in enumerate(pipeline_steps):
136
+ success = run_command(
137
+ step["command"],
138
+ step["working_dir"],
139
+ progress_bar,
140
+ progress_text,
141
+ total_progress,
142
+ step["weight"],
143
+ show_progress=step["show_progress"]
144
+ )
145
+ if not success:
146
+ st.error(f"Failed at step: {' '.join(map(str, step['command']))}")
147
+ if default_output_dir.exists():
148
+ shutil.rmtree(default_output_dir, ignore_errors=True)
149
+ return None
150
+
151
+ if i == 0 and default_output_dir.exists():
152
+ npz_dir = default_output_dir / "npz"
153
+ if npz_dir.exists():
154
+ target_npz_dir = output_dir / "npz"
155
+ shutil.move(str(npz_dir), str(target_npz_dir))
156
+ if default_output_dir.exists():
157
+ shutil.rmtree(default_output_dir, ignore_errors=True)
158
+
159
+ total_progress += step["weight"]
160
+ if step["show_progress"]:
161
+ progress_bar.progress(min(total_progress, 1.0))
162
+ progress_text.text(f"{int(total_progress * 100)}%")
163
+
164
+ bvh_output_dir = output_dir / "bvh"
165
+ bvh_file = bvh_output_dir / "output.bvh"
166
+
167
+ if bvh_file.exists():
168
+ cleanup_thread = threading.Thread(target=cleanup_output_folder, args=(output_dir,))
169
+ cleanup_thread.daemon = True
170
+ cleanup_thread.start()
171
+
172
+ return {
173
+ 'bvh_file': bvh_file,
174
+ 'output_dir': output_dir
175
+ }
176
  else:
177
+ st.error(f"Failed to generate BVH file at: {bvh_file}")
178
+ if default_output_dir.exists():
179
+ shutil.rmtree(default_output_dir, ignore_errors=True)
180
  return None
181
 
182
+ def cleanup_immediate(output_dir):
183
+ if output_dir and os.path.exists(output_dir):
184
+ shutil.rmtree(output_dir, ignore_errors=True)
185
+ st.success("")
186
+ else:
187
+ st.warning("")
188
+
189
  def main():
190
  st.title("Video to BVH Converter")
191
  st.write("Upload a video file to convert it to BVH format")
 
196
  st.video(uploaded_file)
197
 
198
  if st.button("Convert to BVH"):
199
+ with st.spinner("Processing video..."):
200
+ conversion_result = process_video(uploaded_file)
201
 
202
+ if conversion_result:
203
+ with open(conversion_result['bvh_file'], "rb") as f:
204
  st.download_button(
205
  label="Download BVH File",
206
  data=f,
207
  file_name="output.bvh",
208
+ mime="application/octet-stream",
209
+ on_click=cleanup_immediate,
210
+ args=(conversion_result['output_dir'],)
211
  )
212
+ st.success("Conversion completed successfully! File will be deleted after download or in 30 minutes.")
213
 
214
  st.markdown("""
215
  ### Instructions
216
  1. Upload a video file using the uploader above
217
  2. Click 'Convert to BVH' to start the conversion process
218
+ 3. Watch the progress bar as it processes
219
+ 4. Download the resulting BVH file - folder will be deleted immediately after download or after 30 minutes
220
  """)
221
 
222
  if __name__ == "__main__":
convertNPZtoBVH/conver_bvh.py CHANGED
@@ -3,10 +3,11 @@ import numpy as np
3
  from scipy.spatial.transform import Rotation
4
  from collections import deque
5
  from tqdm import tqdm
 
 
6
 
7
  print(f"Saving 3D Motion")
8
 
9
-
10
  def parse_obj(filename):
11
  vertices = []
12
  lines = []
@@ -23,7 +24,6 @@ def parse_obj(filename):
23
  except Exception as e:
24
  raise ValueError(f"Error parsing OBJ file {filename}: {str(e)}")
25
 
26
-
27
  def build_hierarchy(lines, root=0):
28
  num_joints = max(max(line) for line in lines) + 1
29
  adj = [[] for _ in range(num_joints)]
@@ -49,7 +49,6 @@ def build_hierarchy(lines, root=0):
49
  children[parent[c]].append(c)
50
  return parent, children
51
 
52
-
53
  def compute_offsets(vertices_ref, parent):
54
  num_joints = len(vertices_ref)
55
  offsets = np.zeros((num_joints, 3))
@@ -58,7 +57,6 @@ def compute_offsets(vertices_ref, parent):
58
  offsets[j] = vertices_ref[j] - vertices_ref[parent[j]]
59
  return offsets
60
 
61
-
62
  def compute_R_world(joint, vertices_ref, vertices_cur, children):
63
  if not children[joint]:
64
  return np.eye(3)
@@ -94,12 +92,9 @@ def compute_R_world(joint, vertices_ref, vertices_cur, children):
94
  R = U @ Vh
95
  return R
96
 
97
-
98
- def main():
99
- output_dir = os.path.abspath('../outputs/')
100
- os.makedirs(output_dir, exist_ok=True)
101
  folder = os.path.join(output_dir, 'obj_sequence')
102
-
103
  try:
104
  obj_files = sorted([f for f in os.listdir(folder) if f.endswith('.obj')])
105
  except Exception as e:
@@ -118,36 +113,35 @@ def main():
118
  root = 0
119
 
120
  hierarchy_order = []
121
-
122
  def dfs(joint):
123
  hierarchy_order.append(joint)
124
  for child in children[joint]:
125
  dfs(child)
126
-
127
  dfs(root)
128
 
129
  motion_data = []
130
- for obj_file in tqdm(obj_files):
 
 
131
  vertices_cur = parse_obj(os.path.join(folder, obj_file))[0]
132
  R_world = [compute_R_world(j, vertices_ref, vertices_cur, children) for j in range(num_joints)]
133
  R_local = [R_world[j] if parent[j] == -1 else R_world[parent[j]].T @ R_world[j] for j in range(num_joints)]
134
  euler_angles = [Rotation.from_matrix(R).as_euler('ZYX', degrees=True) for R in R_local]
135
  root_pos = vertices_cur[root]
136
  motion_line = list(root_pos) + list(euler_angles[root])
137
- for j in hierarchy_order[1:]:
138
  motion_line.extend(euler_angles[j])
 
 
 
139
  motion_data.append(motion_line)
140
 
141
- # Note: Smoothing function has been removed
142
- # Note: Elbow constraints have been removed
143
-
144
  bvh_dir = os.path.join(output_dir, 'bvh')
145
  os.makedirs(bvh_dir, exist_ok=True)
146
  bvh_file = os.path.join(bvh_dir, 'output.bvh')
147
 
148
  with open(bvh_file, 'w') as f:
149
  f.write("HIERARCHY\n")
150
-
151
  def write_hierarchy(joint, parent, f, indent=0):
152
  if parent == -1:
153
  f.write("ROOT Joint{}\n".format(joint))
@@ -156,8 +150,7 @@ def main():
156
  f.write(" " * indent + "{\n")
157
  f.write(" " * (indent + 1) + "OFFSET {:.6f} {:.6f} {:.6f}\n".format(*offsets[joint]))
158
  if parent == -1:
159
- f.write(" " * (
160
- indent + 1) + "CHANNELS 6 Xposition Yposition Zposition Zrotation Yrotation Xrotation\n")
161
  else:
162
  f.write(" " * (indent + 1) + "CHANNELS 3 Zrotation Yrotation Xrotation\n")
163
  for child in children[joint]:
@@ -180,6 +173,8 @@ def main():
180
  except Exception as e:
181
  print(f"Error during processing: {e}")
182
 
183
-
184
  if __name__ == "__main__":
185
- main()
 
 
 
 
3
  from scipy.spatial.transform import Rotation
4
  from collections import deque
5
  from tqdm import tqdm
6
+ import sys
7
+ import argparse
8
 
9
  print(f"Saving 3D Motion")
10
 
 
11
  def parse_obj(filename):
12
  vertices = []
13
  lines = []
 
24
  except Exception as e:
25
  raise ValueError(f"Error parsing OBJ file {filename}: {str(e)}")
26
 
 
27
  def build_hierarchy(lines, root=0):
28
  num_joints = max(max(line) for line in lines) + 1
29
  adj = [[] for _ in range(num_joints)]
 
49
  children[parent[c]].append(c)
50
  return parent, children
51
 
 
52
  def compute_offsets(vertices_ref, parent):
53
  num_joints = len(vertices_ref)
54
  offsets = np.zeros((num_joints, 3))
 
57
  offsets[j] = vertices_ref[j] - vertices_ref[parent[j]]
58
  return offsets
59
 
 
60
  def compute_R_world(joint, vertices_ref, vertices_cur, children):
61
  if not children[joint]:
62
  return np.eye(3)
 
92
  R = U @ Vh
93
  return R
94
 
95
+ def main(output_dir):
 
 
 
96
  folder = os.path.join(output_dir, 'obj_sequence')
97
+
98
  try:
99
  obj_files = sorted([f for f in os.listdir(folder) if f.endswith('.obj')])
100
  except Exception as e:
 
113
  root = 0
114
 
115
  hierarchy_order = []
 
116
  def dfs(joint):
117
  hierarchy_order.append(joint)
118
  for child in children[joint]:
119
  dfs(child)
 
120
  dfs(root)
121
 
122
  motion_data = []
123
+ total_files = len(obj_files)
124
+ for i in range(total_files):
125
+ obj_file = obj_files[i]
126
  vertices_cur = parse_obj(os.path.join(folder, obj_file))[0]
127
  R_world = [compute_R_world(j, vertices_ref, vertices_cur, children) for j in range(num_joints)]
128
  R_local = [R_world[j] if parent[j] == -1 else R_world[parent[j]].T @ R_world[j] for j in range(num_joints)]
129
  euler_angles = [Rotation.from_matrix(R).as_euler('ZYX', degrees=True) for R in R_local]
130
  root_pos = vertices_cur[root]
131
  motion_line = list(root_pos) + list(euler_angles[root])
132
+ for j_idx, j in enumerate(hierarchy_order[1:], 1):
133
  motion_line.extend(euler_angles[j])
134
+ progress = ((i / total_files) + (j_idx / len(hierarchy_order) / total_files)) * 100
135
+ print(f"PROGRESS:{progress:.2f}")
136
+ sys.stdout.flush()
137
  motion_data.append(motion_line)
138
 
 
 
 
139
  bvh_dir = os.path.join(output_dir, 'bvh')
140
  os.makedirs(bvh_dir, exist_ok=True)
141
  bvh_file = os.path.join(bvh_dir, 'output.bvh')
142
 
143
  with open(bvh_file, 'w') as f:
144
  f.write("HIERARCHY\n")
 
145
  def write_hierarchy(joint, parent, f, indent=0):
146
  if parent == -1:
147
  f.write("ROOT Joint{}\n".format(joint))
 
150
  f.write(" " * indent + "{\n")
151
  f.write(" " * (indent + 1) + "OFFSET {:.6f} {:.6f} {:.6f}\n".format(*offsets[joint]))
152
  if parent == -1:
153
+ f.write(" " * (indent + 1) + "CHANNELS 6 Xposition Yposition Zposition Zrotation Yrotation Xrotation\n")
 
154
  else:
155
  f.write(" " * (indent + 1) + "CHANNELS 3 Zrotation Yrotation Xrotation\n")
156
  for child in children[joint]:
 
173
  except Exception as e:
174
  print(f"Error during processing: {e}")
175
 
 
176
  if __name__ == "__main__":
177
+ parser = argparse.ArgumentParser('Convert OBJ sequence to BVH.')
178
+ parser.add_argument('--output-dir', type=str, default='../outputs/', help='Output directory containing obj_sequence')
179
+ args = parser.parse_args()
180
+ main(args.output_dir)
convertNPZtoBVH/conver_obj.py CHANGED
@@ -1,51 +1,19 @@
1
  import numpy as np
2
  import os
3
  from datetime import datetime
 
4
 
5
  def define_human_connections():
6
- """
7
- Define connections for human stick figure with support for various poses
8
- including crossed legs and complex movements
9
- """
10
  return [
11
- # Core body structure
12
- [0, 7], # Base spine to upper spine
13
- [7, 8], # Upper spine to neck
14
- [8, 9], # Neck to head base
15
- [9, 10], # Head extension
16
-
17
- # Arms (with complete chains)
18
- # Left arm
19
- [7, 14], # Spine to left shoulder
20
- [14, 15], # Left upper arm
21
- [15, 16], # Left forearm/hand
22
-
23
- # Right arm
24
- [7, 11], # Spine to right shoulder
25
- [11, 12], # Right upper arm
26
- [12, 13], # Right forearm/hand
27
-
28
- # Legs with crossed support
29
- # Left leg (now crossing to right)
30
- [0, 1], # Hip to left thigh
31
- [1, 2], # Left thigh to knee
32
- [2, 3], # Left knee to foot
33
-
34
- # Right leg
35
- [0, 4], # Hip to right thigh
36
- [4, 5], # Right thigh to knee
37
- [5, 6], # Right knee to foot
38
-
39
- # Structural connections
40
- [14, 11], # Shoulder cross support
41
- [1, 4], # Hip cross support
42
  ]
43
 
44
  def npz_to_obj_sequence(npz_path, output_dir):
45
- """
46
- Convert NPZ motion capture data to OBJ sequence
47
- with enhanced support for various poses and movements
48
- """
49
  os.makedirs(output_dir, exist_ok=True)
50
  data = np.load(npz_path)
51
  reconstruction = data['reconstruction'][0]
@@ -53,62 +21,35 @@ def npz_to_obj_sequence(npz_path, output_dir):
53
  num_frames = reconstruction.shape[0]
54
  connections = define_human_connections()
55
 
56
- # Increased scale for better visibility
57
- scale = 150.0 # Adjusted scale factor
58
 
59
  for frame_idx in range(num_frames):
60
  vertices = reconstruction[frame_idx]
61
  output_path = os.path.join(output_dir, f"frame_{frame_idx:04d}.obj")
62
 
63
  with open(output_path, 'w') as f:
64
- # Write vertices with enhanced precision
65
  for v in vertices:
66
- # Coordinate system transformation with improved scaling
67
  x, y, z = v[0] * scale, v[2] * scale, v[1] * scale
68
  f.write(f"v {x:.8f} {y:.8f} {z:.8f}\n")
69
-
70
- # Write connections
71
  for conn in connections:
72
  f.write(f"l {conn[0] + 1} {conn[1] + 1}\n")
73
-
74
 
75
  def analyze_vertex_data(npz_path):
76
- """
77
- Enhanced analysis function to help understand the motion data
78
- and verify correct vertex positions
79
- """
80
  data = np.load(npz_path)
81
  reconstruction = data['reconstruction'][0]
82
-
83
-
84
- # Calculate full range of motion
85
  x_min, x_max = reconstruction[:,:,0].min(), reconstruction[:,:,0].max()
86
  y_min, y_max = reconstruction[:,:,1].min(), reconstruction[:,:,1].max()
87
  z_min, z_max = reconstruction[:,:,2].min(), reconstruction[:,:,2].max()
88
-
89
 
90
- def process_motion_capture(npz_file):
91
  try:
92
- # Verify input file exists
93
  if not os.path.exists(npz_file):
94
  raise FileNotFoundError(f"Input file {npz_file} not found")
95
 
96
- # Define base output directory
97
- base_output_dir = os.path.abspath('../outputs/')
98
- # print(output_dir)
99
- os.makedirs(base_output_dir, exist_ok=True)
100
- # base_output_dir = r"C:\Users\ROGST\Programming\Python\videotobvh\convertNPZtoBVH\outputs"
101
-
102
- # Create a unique output directory with timestamp to avoid overwriting
103
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
104
- output_dir = os.path.join(base_output_dir, f"obj_sequence")
105
-
106
- # Analyze data first
107
  analyze_vertex_data(npz_file)
108
-
109
- # Convert to OBJ sequence
110
- npz_to_obj_sequence(npz_path=npz_file, output_dir=output_dir)
111
-
112
 
113
  except Exception as e:
114
  print(f"Error processing motion capture data: {str(e)}")
@@ -118,24 +59,25 @@ def get_npz_paths(folder_path):
118
  if not os.path.isdir(folder_path):
119
  raise FileNotFoundError(f"Directory not found: {folder_path}")
120
 
121
- # Find the first .npz file in the directory
122
  for file in os.listdir(folder_path):
123
  if file.endswith('.npz'):
124
- npz_path = os.path.join(folder_path, file)
125
- return npz_path
126
 
127
- # If no .npz file is found
128
  raise FileNotFoundError(f"No NPZ files found in directory: {folder_path}")
129
 
 
 
 
 
 
 
130
  if __name__ == "__main__":
131
- # Define the directory where the NPZ file is located
132
- output_dir = os.path.abspath('../outputs/npz/')
133
- os.makedirs(output_dir, exist_ok=True)
134
- input_dir = output_dir
135
 
136
  try:
137
- # Get the first available NPZ file from the directory
138
  npz_file = get_npz_paths(input_dir)
139
- process_motion_capture(npz_file)
140
  except FileNotFoundError as e:
141
  print(f"Error: {str(e)}")
 
1
  import numpy as np
2
  import os
3
  from datetime import datetime
4
+ import argparse
5
 
6
  def define_human_connections():
 
 
 
 
7
  return [
8
+ [0, 7], [7, 8], [8, 9], [9, 10], # Core body
9
+ [7, 14], [14, 15], [15, 16], # Left arm
10
+ [7, 11], [11, 12], [12, 13], # Right arm
11
+ [0, 1], [1, 2], [2, 3], # Left leg
12
+ [0, 4], [4, 5], [5, 6], # Right leg
13
+ [14, 11], [1, 4], # Structural connections
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  ]
15
 
16
  def npz_to_obj_sequence(npz_path, output_dir):
 
 
 
 
17
  os.makedirs(output_dir, exist_ok=True)
18
  data = np.load(npz_path)
19
  reconstruction = data['reconstruction'][0]
 
21
  num_frames = reconstruction.shape[0]
22
  connections = define_human_connections()
23
 
24
+ scale = 150.0
 
25
 
26
  for frame_idx in range(num_frames):
27
  vertices = reconstruction[frame_idx]
28
  output_path = os.path.join(output_dir, f"frame_{frame_idx:04d}.obj")
29
 
30
  with open(output_path, 'w') as f:
 
31
  for v in vertices:
 
32
  x, y, z = v[0] * scale, v[2] * scale, v[1] * scale
33
  f.write(f"v {x:.8f} {y:.8f} {z:.8f}\n")
 
 
34
  for conn in connections:
35
  f.write(f"l {conn[0] + 1} {conn[1] + 1}\n")
 
36
 
37
  def analyze_vertex_data(npz_path):
 
 
 
 
38
  data = np.load(npz_path)
39
  reconstruction = data['reconstruction'][0]
 
 
 
40
  x_min, x_max = reconstruction[:,:,0].min(), reconstruction[:,:,0].max()
41
  y_min, y_max = reconstruction[:,:,1].min(), reconstruction[:,:,1].max()
42
  z_min, z_max = reconstruction[:,:,2].min(), reconstruction[:,:,2].max()
 
43
 
44
+ def process_motion_capture(npz_file, output_dir):
45
  try:
 
46
  if not os.path.exists(npz_file):
47
  raise FileNotFoundError(f"Input file {npz_file} not found")
48
 
49
+ # Use the provided output_dir with 'obj_sequence' subfolder
50
+ obj_output_dir = os.path.join(output_dir, "obj_sequence")
 
 
 
 
 
 
 
 
 
51
  analyze_vertex_data(npz_file)
52
+ npz_to_obj_sequence(npz_path=npz_file, output_dir=obj_output_dir)
 
 
 
53
 
54
  except Exception as e:
55
  print(f"Error processing motion capture data: {str(e)}")
 
59
  if not os.path.isdir(folder_path):
60
  raise FileNotFoundError(f"Directory not found: {folder_path}")
61
 
 
62
  for file in os.listdir(folder_path):
63
  if file.endswith('.npz'):
64
+ return os.path.join(folder_path, file)
 
65
 
 
66
  raise FileNotFoundError(f"No NPZ files found in directory: {folder_path}")
67
 
68
+ def arg_parse():
69
+ parser = argparse.ArgumentParser('Convert NPZ to OBJ sequence.')
70
+ parser.add_argument('--output-dir', type=str, default='../outputs/', help='Output directory for results')
71
+ args = parser.parse_args()
72
+ return args
73
+
74
  if __name__ == "__main__":
75
+ args = arg_parse()
76
+ input_dir = os.path.join(os.path.abspath(args.output_dir), 'npz')
77
+ os.makedirs(input_dir, exist_ok=True)
 
78
 
79
  try:
 
80
  npz_file = get_npz_paths(input_dir)
81
+ process_motion_capture(npz_file, args.output_dir)
82
  except FileNotFoundError as e:
83
  print(f"Error: {str(e)}")