Amanpreet commited on
Commit
9d2691d
Β·
1 Parent(s): ef58188
Files changed (1) hide show
  1. app.py +353 -30
app.py CHANGED
@@ -53,17 +53,15 @@ def run_command(command, working_dir, progress_bar, progress_text, step_start_pr
53
  try:
54
  progress = float(line.strip().split("PROGRESS:")[1]) / 100
55
  if Path(command[1]).name == 'gen_skes.py':
56
- # Scale 0-100% (2D) to 0-60%, 100-200% (3D) to 60-80%
57
  if progress <= 1.0:
58
- adjusted_progress = step_start_progress + (progress * 0.6) # 60% for 2D
59
  else:
60
- adjusted_progress = step_start_progress + 0.6 + ((progress - 1.0) * 0.2) # 20% for 3D
61
  else:
62
- # Scale 0-100% to 20% for conver_bvh.py
63
  adjusted_progress = step_start_progress + (progress * step_weight)
64
  total_progress = min(adjusted_progress, step_start_progress + step_weight)
65
  progress_bar.progress(total_progress)
66
- progress_text.text(f"{int(total_progress * 100)}%")
67
  except ValueError:
68
  pass
69
  elif source == 'stderr':
@@ -81,7 +79,7 @@ def run_command(command, working_dir, progress_bar, progress_text, step_start_pr
81
 
82
  if show_progress:
83
  progress_bar.progress(step_start_progress + step_weight)
84
- progress_text.text(f"{int((step_start_progress + step_weight) * 100)}%")
85
  return True
86
 
87
  except Exception as e:
@@ -132,7 +130,7 @@ def process_video(video_file):
132
  ]
133
 
134
  progress_bar = st.progress(0.0)
135
- progress_text = st.empty() # Added for percentage display
136
  total_progress = 0.0
137
 
138
  for i, step in enumerate(pipeline_steps):
@@ -162,7 +160,7 @@ def process_video(video_file):
162
  total_progress += step["weight"]
163
  if step["show_progress"]:
164
  progress_bar.progress(min(total_progress, 1.0))
165
- progress_text.text(f"{int(total_progress * 100)}%")
166
 
167
  bvh_output_dir = output_dir / "bvh"
168
  bvh_file = bvh_output_dir / "output.bvh"
@@ -185,42 +183,367 @@ def process_video(video_file):
185
  def cleanup_immediate(output_dir):
186
  if output_dir and os.path.exists(output_dir):
187
  shutil.rmtree(output_dir, ignore_errors=True)
188
- st.success("")
189
  else:
190
- st.warning("")
191
 
192
  def main():
193
- st.title("Video to BVH Converter")
194
- st.write("Upload a video file to convert it to BVH format")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
 
196
- uploaded_file = st.file_uploader("Choose a video file", type=['mp4', 'avi', 'mov'])
 
 
 
 
 
 
 
 
197
 
198
- if uploaded_file is not None:
199
- st.video(uploaded_file)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
 
201
- if st.button("Convert to BVH"):
202
- with st.spinner("Processing video..."):
203
- conversion_result = process_video(uploaded_file)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
 
205
- if conversion_result:
206
- with open(conversion_result['bvh_file'], "rb") as f:
 
 
 
 
 
207
  st.download_button(
208
- label="Download BVH File",
209
  data=f,
210
- file_name="output.bvh",
211
  mime="application/octet-stream",
212
  on_click=cleanup_immediate,
213
- args=(conversion_result['output_dir'],)
 
214
  )
215
- st.success("Conversion completed successfully! File will be deleted after download or in 30 minutes.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
 
 
217
  st.markdown("""
218
- ### Instructions
219
- 1. Upload a video file using the uploader above
220
- 2. Click 'Convert to BVH' to start the conversion process
221
- 3. Watch the progress bar as it processes
222
- 4. Download the resulting BVH file - folder will be deleted immediately after download or after 30 minutes
223
- """)
224
 
225
  if __name__ == "__main__":
226
  main()
 
53
  try:
54
  progress = float(line.strip().split("PROGRESS:")[1]) / 100
55
  if Path(command[1]).name == 'gen_skes.py':
 
56
  if progress <= 1.0:
57
+ adjusted_progress = step_start_progress + (progress * 0.6)
58
  else:
59
+ adjusted_progress = step_start_progress + 0.6 + ((progress - 1.0) * 0.2)
60
  else:
 
61
  adjusted_progress = step_start_progress + (progress * step_weight)
62
  total_progress = min(adjusted_progress, step_start_progress + step_weight)
63
  progress_bar.progress(total_progress)
64
+ progress_text.text(f"Progress: {int(total_progress * 100)}%")
65
  except ValueError:
66
  pass
67
  elif source == 'stderr':
 
79
 
80
  if show_progress:
81
  progress_bar.progress(step_start_progress + step_weight)
82
+ progress_text.text(f"Progress: {int((step_start_progress + step_weight) * 100)}%")
83
  return True
84
 
85
  except Exception as e:
 
130
  ]
131
 
132
  progress_bar = st.progress(0.0)
133
+ progress_text = st.empty()
134
  total_progress = 0.0
135
 
136
  for i, step in enumerate(pipeline_steps):
 
160
  total_progress += step["weight"]
161
  if step["show_progress"]:
162
  progress_bar.progress(min(total_progress, 1.0))
163
+ progress_text.text(f"Progress: {int(total_progress * 100)}%")
164
 
165
  bvh_output_dir = output_dir / "bvh"
166
  bvh_file = bvh_output_dir / "output.bvh"
 
183
  def cleanup_immediate(output_dir):
184
  if output_dir and os.path.exists(output_dir):
185
  shutil.rmtree(output_dir, ignore_errors=True)
186
+ st.success("Output folder cleaned up successfully.")
187
  else:
188
+ st.warning("No output folder to clean up.")
189
 
190
  def main():
191
+ st.set_page_config(
192
+ page_title="Motion Capture Studio | Video to BVH Converter",
193
+ page_icon="🎬",
194
+ layout="wide",
195
+ initial_sidebar_state="collapsed"
196
+ )
197
+
198
+ st.markdown("""
199
+ <style>
200
+ :root {
201
+ --bg-color: #1a1a1a;
202
+ --card-bg: #252525;
203
+ --primary-color: #bb86fc;
204
+ --secondary-color: #03dac6;
205
+ --error-color: #cf6679;
206
+ --text-color: #e0e0e0;
207
+ --text-secondary: #a0a0a0;
208
+ --border-color: #404040;
209
+ --shadow-color: rgba(0, 0, 0, 0.5);
210
+ }
211
+
212
+ .stApp {
213
+ background-color: var(--bg-color);
214
+ font-family: 'Arial', sans-serif;
215
+ }
216
+
217
+ h1, h2, h3, h4, h5, h6, p, li, div {
218
+ color: var(--text-color) !important;
219
+ }
220
+
221
+ .card {
222
+ background-color: var(--card-bg);
223
+ border-radius: 20px;
224
+ padding: 2rem;
225
+ margin: 1rem auto;
226
+ border: 1px solid var(--border-color);
227
+ box-shadow: 0 8px 30px var(--shadow-color);
228
+ max-width: 1200px;
229
+ }
230
+
231
+ .main-title {
232
+ font-size: 3.5rem;
233
+ font-weight: 900;
234
+ background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
235
+ -webkit-background-clip: text;
236
+ -webkit-text-fill-color: transparent;
237
+ text-align: center;
238
+ margin: 1.5rem 0 0.5rem;
239
+ text-shadow: 0 2px 10px rgba(187, 134, 252, 0.3);
240
+ }
241
+
242
+ .subtitle {
243
+ font-size: 1.3rem;
244
+ color: var(--text-secondary);
245
+ text-align: center;
246
+ margin-bottom: 2.5rem;
247
+ font-weight: 300;
248
+ letter-spacing: 0.5px;
249
+ }
250
+
251
+ .section-title {
252
+ font-size: 1.5rem;
253
+ font-weight: 700;
254
+ color: var(--primary-color) !important;
255
+ margin-bottom: 1.2rem;
256
+ text-transform: uppercase;
257
+ letter-spacing: 1px;
258
+ }
259
+
260
+ .stButton > button {
261
+ background: linear-gradient(135deg, var(--primary-color), #9b59f5);
262
+ color: #fff !important;
263
+ border-radius: 12px;
264
+ padding: 0.8rem 2.5rem;
265
+ font-weight: 600;
266
+ font-size: 1.2rem;
267
+ border: none;
268
+ transition: all 0.3s ease;
269
+ width: 100%;
270
+ box-shadow: 0 4px 15px rgba(187, 134, 252, 0.3);
271
+ }
272
+
273
+ .stButton > button:hover {
274
+ transform: translateY(-3px);
275
+ box-shadow: 0 6px 20px rgba(187, 134, 252, 0.5);
276
+ background: linear-gradient(135deg, #9b59f5, var(--primary-color));
277
+ }
278
+
279
+ .stDownloadButton > button {
280
+ background: linear-gradient(135deg, var(--secondary-color), #02b3a3);
281
+ color: #fff !important;
282
+ border-radius: 12px;
283
+ padding: 0.8rem 2.5rem;
284
+ font-weight: 600;
285
+ font-size: 1.2rem;
286
+ border: none;
287
+ transition: all 0.3s ease;
288
+ width: 100%;
289
+ box-shadow: 0 4px 15px rgba(3, 218, 198, 0.3);
290
+ }
291
+
292
+ .stDownloadButton > button:hover {
293
+ transform: translateY(-3px);
294
+ box-shadow: 0 6px 20px rgba(3, 218, 198, 0.5);
295
+ background: linear-gradient(135deg, #02b3a3, var(--secondary-color));
296
+ }
297
+
298
+ .upload-container {
299
+ border: 2px dashed var(--border-color);
300
+ border-radius: 15px;
301
+ padding: 2.5rem;
302
+ text-align: center;
303
+ background-color: rgba(255, 255, 255, 0.05);
304
+ transition: all 0.3s ease;
305
+ }
306
+
307
+ .upload-container:hover {
308
+ border-color: var(--primary-color);
309
+ box-shadow: 0 0 20px rgba(187, 134, 252, 0.2);
310
+ }
311
+
312
+ .video-container {
313
+ border-radius: 15px;
314
+ overflow: hidden;
315
+ box-shadow: 0 6px 25px var(--shadow-color);
316
+ margin: 1rem 0;
317
+ border: 1px solid var(--border-color);
318
+ background-color: #000;
319
+ }
320
+
321
+ .status-indicator {
322
+ padding: 1rem;
323
+ border-radius: 12px;
324
+ margin: 0.8rem 0;
325
+ display: flex;
326
+ align-items: center;
327
+ gap: 0.5rem;
328
+ font-size: 1.1rem;
329
+ }
330
+
331
+ .status-indicator.info {
332
+ background-color: rgba(187, 134, 252, 0.1);
333
+ border-left: 5px solid var(--primary-color);
334
+ }
335
+
336
+ .status-indicator.success {
337
+ background-color: rgba(3, 218, 198, 0.1);
338
+ border-left: 5px solid var(--secondary-color);
339
+ }
340
+
341
+ .control-section {
342
+ text-align: center;
343
+ margin: 2rem 0;
344
+ padding: 1rem;
345
+ background-color: rgba(255, 255, 255, 0.03);
346
+ border-radius: 12px;
347
+ border: 1px solid var(--border-color);
348
+ }
349
+
350
+ .stProgress {
351
+ width: 100%;
352
+ height: 12px;
353
+ border-radius: 6px;
354
+ background-color: #333;
355
+ margin: 1rem 0;
356
+ }
357
+
358
+ .stProgress > div > div {
359
+ background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
360
+ border-radius: 6px;
361
+ }
362
 
363
+ .progress-text {
364
+ font-weight: 600;
365
+ padding: 0.5rem 1rem;
366
+ background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
367
+ color: #fff !important;
368
+ border-radius: 20px;
369
+ box-shadow: 0 2px 10px rgba(187, 134, 252, 0.3);
370
+ display: inline-block;
371
+ }
372
 
373
+ .separator {
374
+ height: 2px;
375
+ background: linear-gradient(90deg, transparent, var(--border-color), transparent);
376
+ margin: 2.5rem 0;
377
+ }
378
+
379
+ .footer {
380
+ text-align: center;
381
+ padding: 2.5rem 0 1.5rem;
382
+ font-size: 0.95rem;
383
+ color: var(--text-secondary);
384
+ border-top: 1px solid var(--border-color);
385
+ letter-spacing: 0.5px;
386
+ }
387
+
388
+ .pipeline-step {
389
+ display: flex;
390
+ align-items: center;
391
+ background-color: rgba(255, 255, 255, 0.05);
392
+ padding: 1rem;
393
+ border-radius: 12px;
394
+ margin-bottom: 1rem;
395
+ border: 1px solid var(--border-color);
396
+ transition: all 0.3s ease;
397
+ }
398
+
399
+ .pipeline-step:hover {
400
+ background-color: rgba(255, 255, 255, 0.08);
401
+ border-color: var(--primary-color);
402
+ box-shadow: 0 4px 15px rgba(187, 134, 252, 0.1);
403
+ }
404
+
405
+ .step-number {
406
+ background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
407
+ color: #fff;
408
+ border-radius: 50%;
409
+ width: 40px;
410
+ height: 40px;
411
+ display: flex;
412
+ align-items: center;
413
+ justify-content: center;
414
+ margin-right: 1.2rem;
415
+ font-weight: 600;
416
+ font-size: 1.2rem;
417
+ box-shadow: 0 2px 10px rgba(187, 134, 252, 0.3);
418
+ }
419
+
420
+ .step-title {
421
+ font-weight: 600;
422
+ font-size: 1.1rem;
423
+ margin-bottom: 0.3rem;
424
+ }
425
+
426
+ .step-description {
427
+ color: var(--text-secondary);
428
+ font-size: 0.95rem;
429
+ }
430
+
431
+ @keyframes pulse {
432
+ 0% { transform: scale(1); }
433
+ 50% { transform: scale(1.03); }
434
+ 100% { transform: scale(1); }
435
+ }
436
+
437
+ .animate-pulse {
438
+ animation: pulse 2s infinite;
439
+ }
440
+ </style>
441
+ """, unsafe_allow_html=True)
442
+
443
+ # Header
444
+ st.markdown('<h1 class="main-title animate-pulse">Motion Capture Studio</h1>', unsafe_allow_html=True)
445
+ st.markdown('<p class="subtitle">Transform your videos into professional BVH motion files with cutting-edge AI</p>', unsafe_allow_html=True)
446
+
447
+ # Main content
448
+ st.markdown('<div class="card">', unsafe_allow_html=True)
449
+
450
+ col1, col2 = st.columns([1, 1], gap="medium")
451
+
452
+ with col1:
453
+ st.markdown('<h3 class="section-title">Upload Your Video</h3>', unsafe_allow_html=True)
454
+ st.markdown('<div class="upload-container">', unsafe_allow_html=True)
455
+ uploaded_file = st.file_uploader(
456
+ "Drop your video here or click to browse",
457
+ type=['mp4', 'avi', 'mov'],
458
+ help="For best results, use clear full-body motion videos with good lighting",
459
+ key="file_uploader"
460
+ )
461
+ if uploaded_file:
462
+ st.session_state['uploaded_file'] = uploaded_file
463
+ st.markdown('</div>', unsafe_allow_html=True)
464
 
465
+ if not st.session_state.get('uploaded_file'):
466
+ st.markdown("""
467
+ <div class="status-indicator info">
468
+ πŸ’‘ <strong>Pro Tip:</strong> Use MP4, AVI, or MOV files with clear, well-lit full-body motion
469
+ </div>
470
+ """, unsafe_allow_html=True)
471
+
472
+ with col2:
473
+ st.markdown('<h3 class="section-title">Video Preview</h3>', unsafe_allow_html=True)
474
+ if uploaded_file := st.session_state.get('uploaded_file', None):
475
+ st.markdown('<div class="video-container">', unsafe_allow_html=True)
476
+ st.video(uploaded_file)
477
+ st.markdown('</div>', unsafe_allow_html=True)
478
+ else:
479
+ st.markdown("""
480
+ <div style="height: 250px; display: flex; align-items: center; justify-content: center;
481
+ border: 2px dashed var(--border-color); border-radius: 15px; background-color: rgba(255, 255, 255, 0.05);">
482
+ <span style="color: var(--text-secondary); font-size: 1.2rem;">Your video preview will appear here</span>
483
+ </div>
484
+ """, unsafe_allow_html=True)
485
+
486
+ # Control Section (Button and Progress Bar)
487
+ if st.session_state.get('uploaded_file'):
488
+ st.markdown('<div class="control-section">', unsafe_allow_html=True)
489
+ if st.button("⚑ Start Motion Capture", key="convert_btn"):
490
+ with st.spinner("Processing your video..."):
491
+ progress_bar = st.progress(0.0)
492
+ progress_text = st.empty()
493
 
494
+ st.markdown('<div class="status-indicator info">πŸ”„ Analyzing motion patterns...</div>', unsafe_allow_html=True)
495
+
496
+ result = process_video(st.session_state['uploaded_file'])
497
+
498
+ if result:
499
+ st.markdown('<div class="status-indicator success">βœ… Motion capture complete!</div>', unsafe_allow_html=True)
500
+ with open(result['bvh_file'], "rb") as f:
501
  st.download_button(
502
+ label="πŸ“₯ Download BVH File",
503
  data=f,
504
+ file_name="motion_capture.bvh",
505
  mime="application/octet-stream",
506
  on_click=cleanup_immediate,
507
+ args=(result['output_dir'],),
508
+ key="download_btn"
509
  )
510
+ st.markdown('</div>', unsafe_allow_html=True)
511
+
512
+ # Pipeline Info
513
+ st.markdown('<div class="separator"></div>', unsafe_allow_html=True)
514
+ st.markdown('<h3 class="section-title">Processing Pipeline</h3>', unsafe_allow_html=True)
515
+ st.markdown("""
516
+ <div class="pipeline-step">
517
+ <div class="step-number">1</div>
518
+ <div>
519
+ <div class="step-title">Pose Estimation</div>
520
+ <div class="step-description">AI detects and tracks human movements frame-by-frame</div>
521
+ </div>
522
+ </div>
523
+ <div class="pipeline-step">
524
+ <div class="step-number">2</div>
525
+ <div>
526
+ <div class="step-title">3D Conversion</div>
527
+ <div class="step-description">Converts 2D poses into 3D spatial data</div>
528
+ </div>
529
+ </div>
530
+ <div class="pipeline-step">
531
+ <div class="step-number">3</div>
532
+ <div>
533
+ <div class="step-title">BVH Generation</div>
534
+ <div class="step-description">Formats motion data into industry-standard BVH files</div>
535
+ </div>
536
+ </div>
537
+ """, unsafe_allow_html=True)
538
+
539
+ st.markdown('</div>', unsafe_allow_html=True)
540
 
541
+ # Footer
542
  st.markdown("""
543
+ <div class="footer">
544
+ Β© 2025 Motion Capture Studio | Powered by Streamlit & Advanced AI Technology
545
+ </div>
546
+ """, unsafe_allow_html=True)
 
 
547
 
548
  if __name__ == "__main__":
549
  main()