akarsh999 commited on
Commit
1c5d7df
·
verified ·
1 Parent(s): 0d6723b

Upload 10 files

Browse files
Files changed (2) hide show
  1. app.py +263 -115
  2. athletic_performance.py +51 -116
app.py CHANGED
@@ -5,7 +5,7 @@ from athletic_performance import (
5
  analyze_youtube_video, analyze_video_file, get_performance_insights,
6
  get_ai_sports_coaching_analysis, test_gemini_api_connection,
7
  generate_annotated_video_from_youtube, generate_annotated_video_from_file,
8
- complete_analysis_with_video_stream
9
  )
10
 
11
  def analyze_jump_from_youtube(youtube_url, user_height_cm, user_weight_kg, progress=gr.Progress()):
@@ -63,7 +63,7 @@ def analyze_jump_from_youtube(youtube_url, user_height_cm, user_weight_kg, progr
63
 
64
  ### 📈 Performance Insights
65
  """
66
-
67
  # Add performance insights using the new function
68
  insights = get_performance_insights(result)
69
  for insight in insights:
@@ -100,11 +100,11 @@ def analyze_jump_from_file(video_file, user_height_cm, user_weight_kg, progress=
100
  # Handle errors
101
  if "error" in result:
102
  return f"❌ {result['error']}", None, None
103
-
104
- if result is None:
105
- return "⚠️ Could not analyze jump. Make sure the video shows a person clearly performing a vertical jump.", None, None
106
-
107
- # Format results (same as YouTube function)
108
  # Handle potential None values safely
109
  jump_height = result.get('jump_height_cm', 0) or 0
110
  flight_time = result.get('flight_time_s', 0) or 0
@@ -142,7 +142,7 @@ def analyze_jump_from_file(video_file, user_height_cm, user_weight_kg, progress=
142
 
143
  ### 📈 Performance Insights
144
  """
145
-
146
  # Add performance insights using the new function
147
  insights = get_performance_insights(result)
148
  for insight in insights:
@@ -397,95 +397,173 @@ Your annotated video is ready for download!
397
  return results_text, summary_df, video_path
398
 
399
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
  # Create Gradio interface
401
  def create_interface():
402
- # Custom CSS for dark theme with green accents
403
- custom_css = """
404
- .gradio-container {
405
- background: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 100%) !important;
406
- color: #00ff88 !important;
407
- }
408
- .gr-button {
409
- background: linear-gradient(45deg, #00ff88, #00cc6a) !important;
410
- border: none !important;
411
- color: #000 !important;
412
- font-weight: bold !important;
413
- border-radius: 8px !important;
414
- transition: all 0.3s ease !important;
415
- }
416
- .gr-button:hover {
417
- transform: translateY(-2px) !important;
418
- box-shadow: 0 6px 20px rgba(0, 255, 136, 0.3) !important;
419
- }
420
- .gr-textbox, .gr-file, .gr-number {
421
- background: #222 !important;
422
- border: 1px solid #00ff88 !important;
423
- color: #fff !important;
424
- border-radius: 8px !important;
425
- }
426
- .gr-markdown {
427
- color: #e0e0e0 !important;
428
- }
429
- .gr-markdown h1 {
430
- color: #00ff88 !important;
431
- text-shadow: 0 0 10px rgba(0, 255, 136, 0.5) !important;
432
- text-align: center !important;
433
- }
434
- .gr-markdown h2 {
435
- color: #00cc6a !important;
436
- }
437
- .gr-radio label {
438
- background: #222 !important;
439
- border: 1px solid #555 !important;
440
- border-radius: 8px !important;
441
- color: #fff !important;
442
- }
443
- .gr-radio label:checked {
444
- background: #00ff88 !important;
445
- color: #000 !important;
446
- }
447
- .gr-tab {
448
- background: #333 !important;
449
- border: 1px solid #00ff88 !important;
450
- border-radius: 8px 8px 0 0 !important;
451
- color: #fff !important;
452
- }
453
- .gr-tab.selected {
454
- background: linear-gradient(45deg, #00ff88, #00cc6a) !important;
455
- color: #000 !important;
456
- }
457
- .gr-dataframe {
458
- background: #222 !important;
459
- border: 1px solid #00ff88 !important;
460
- border-radius: 8px !important;
461
- }
462
- .gr-video {
463
- border: 2px solid #00ff88 !important;
464
- border-radius: 12px !important;
465
- box-shadow: 0 0 20px rgba(0, 255, 136, 0.3) !important;
466
- background: #111 !important;
467
- }
468
- .gr-form {
469
- background: #1a1a1a !important;
470
- border-radius: 12px !important;
471
- padding: 20px !important;
472
- }
473
- """
474
-
475
- with gr.Blocks(title="🚀 Athletic Performance AI", css=custom_css, theme=gr.themes.Soft()) as app:
476
  gr.Markdown("""
477
- # 🚀 Athletic Performance AI
478
- **Advanced jump analysis with AI coaching & pose tracking**
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
479
  """)
480
 
481
  with gr.Row():
482
  with gr.Column():
483
  user_height = gr.Number(
484
- label="Your Height (cm)",
485
- value=175,
486
- minimum=100,
487
- maximum=250
488
- )
489
  with gr.Column():
490
  user_weight = gr.Number(
491
  label="Your Weight (kg)",
@@ -493,27 +571,50 @@ def create_interface():
493
  minimum=30,
494
  maximum=200
495
  )
 
496
 
497
  with gr.Tabs():
498
  # YouTube URL Tab
499
- with gr.TabItem("🎥 YouTube"):
 
500
  youtube_url = gr.Textbox(
501
  label="YouTube URL",
502
  placeholder="https://youtube.com/watch?v=..."
503
  )
504
- youtube_btn = gr.Button("🚀 Analyze", variant="primary")
505
 
506
  # File Upload Tab
507
- with gr.TabItem("📁 Upload"):
 
508
  video_file = gr.File(
509
- label="Upload Video",
510
  file_types=[".mp4", ".avi", ".mov", ".mkv", ".webm"]
511
  )
512
- file_btn = gr.Button("🚀 Analyze", variant="primary")
513
 
514
  # Complete Analysis Tab
515
- with gr.TabItem("🎬 AI Coach + Video"):
516
- gr.Markdown("**🤖 AI coaching + 🎥 annotated video + 📊 pose tracking**")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
517
 
518
  with gr.Row():
519
  with gr.Column():
@@ -522,58 +623,96 @@ def create_interface():
522
  label="Gender",
523
  value="Male"
524
  )
 
525
 
 
526
  default_api_key = os.getenv("GEMINI_API_KEY", "")
527
  complete_gemini_key = gr.Textbox(
528
- label="Gemini API Key (Optional)",
529
- placeholder="Enter API key for AI coaching" if not default_api_key else "API key loaded",
530
  type="password",
531
  value=default_api_key
532
  )
533
 
534
- test_api_btn = gr.Button("🧪 Test", size="sm")
 
 
535
  api_test_result = gr.Textbox(
536
- label="Test Result",
537
- lines=2,
538
  interactive=False,
539
  visible=False
540
  )
 
 
 
 
 
 
541
 
542
  with gr.Column():
543
  complete_youtube_url = gr.Textbox(
544
- label="YouTube URL",
545
  placeholder="https://youtube.com/watch?v=..."
546
  )
547
  complete_video_file = gr.File(
548
- label="Or Upload Video",
549
  file_types=[".mp4", ".avi", ".mov", ".mkv", ".webm"]
550
  )
 
551
 
552
- complete_analysis_btn = gr.Button("🚀 Generate Analysis + Video", variant="primary", size="lg")
553
 
554
  # Results section
555
- gr.Markdown("## 📊 Results")
556
-
557
- # Video streaming section - make it prominent
558
- with gr.Row():
559
- analysis_video = gr.Video(
560
- label="📺 Analysis Video",
561
- visible=False,
562
- height=400
563
- )
564
 
565
  with gr.Row():
566
  with gr.Column(scale=2):
567
- results_text = gr.Markdown(label="Analysis")
568
  with gr.Column(scale=1):
569
  results_table = gr.Dataframe(
570
- label="Metrics",
571
  headers=["Metric", "Value"],
572
  datatype=["str", "str"]
573
  )
574
 
 
 
 
 
 
 
 
 
575
  status_message = gr.Textbox(label="Status", interactive=False)
576
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
577
  # Event handlers
578
  youtube_btn.click(
579
  fn=analyze_jump_from_youtube,
@@ -618,6 +757,15 @@ def create_interface():
618
  inputs=[complete_gemini_key],
619
  outputs=[api_test_result]
620
  )
 
 
 
 
 
 
 
 
 
621
 
622
  return app
623
 
 
5
  analyze_youtube_video, analyze_video_file, get_performance_insights,
6
  get_ai_sports_coaching_analysis, test_gemini_api_connection,
7
  generate_annotated_video_from_youtube, generate_annotated_video_from_file,
8
+ generate_complete_analysis_with_video
9
  )
10
 
11
  def analyze_jump_from_youtube(youtube_url, user_height_cm, user_weight_kg, progress=gr.Progress()):
 
63
 
64
  ### 📈 Performance Insights
65
  """
66
+
67
  # Add performance insights using the new function
68
  insights = get_performance_insights(result)
69
  for insight in insights:
 
100
  # Handle errors
101
  if "error" in result:
102
  return f"❌ {result['error']}", None, None
103
+
104
+ if result is None:
105
+ return "⚠️ Could not analyze jump. Make sure the video shows a person clearly performing a vertical jump.", None, None
106
+
107
+ # Format results (same as YouTube function)
108
  # Handle potential None values safely
109
  jump_height = result.get('jump_height_cm', 0) or 0
110
  flight_time = result.get('flight_time_s', 0) or 0
 
142
 
143
  ### 📈 Performance Insights
144
  """
145
+
146
  # Add performance insights using the new function
147
  insights = get_performance_insights(result)
148
  for insight in insights:
 
397
  return results_text, summary_df, video_path
398
 
399
 
400
+ def complete_analysis_with_video_stream(youtube_url, video_file, user_height_cm, user_weight_kg, gender, gemini_api_key, progress=gr.Progress()):
401
+ """Complete analysis with AI coaching and video streaming."""
402
+
403
+ # Validate inputs
404
+ if not user_height_cm or user_height_cm <= 0:
405
+ return "❌ Please provide a valid height", None, None, None
406
+
407
+ if not user_weight_kg or user_weight_kg <= 0:
408
+ return "❌ Please provide a valid weight", None, None, None
409
+
410
+ if not gender:
411
+ return "❌ Please select your gender", None, None, None
412
+
413
+ # Determine video source and path
414
+ video_source = None
415
+ video_path_or_url = None
416
+
417
+ if youtube_url and youtube_url.strip():
418
+ video_source = "youtube"
419
+ video_path_or_url = youtube_url.strip()
420
+ progress(0.05, desc="Processing YouTube video...")
421
+ elif video_file:
422
+ video_source = "file"
423
+ video_path_or_url = video_file.name
424
+ progress(0.05, desc="Processing uploaded video...")
425
+ else:
426
+ return "❌ Please provide either a YouTube URL or upload a video file", None, None, None
427
+
428
+ try:
429
+ # Create progress callback
430
+ def progress_callback(prog, desc):
431
+ progress(prog, desc=desc)
432
+
433
+ # Run complete analysis
434
+ result = generate_complete_analysis_with_video(
435
+ video_source=video_source,
436
+ video_path_or_url=video_path_or_url,
437
+ user_height_cm=user_height_cm,
438
+ user_weight_kg=user_weight_kg,
439
+ gender=gender,
440
+ api_key=gemini_api_key.strip() if gemini_api_key else None,
441
+ progress_callback=progress_callback
442
+ )
443
+
444
+ # Handle errors
445
+ if "error" in result:
446
+ return f"❌ Analysis failed: {result['error']}", None, None, None
447
+
448
+ if not result.get("success"):
449
+ return "❌ Analysis failed. Please ensure the video shows a clear vertical jump.", None, None, None
450
+
451
+ # Extract results
452
+ video_path = result.get("video_path")
453
+ jump_metrics = result.get("jump_metrics", {})
454
+ jump_references = result.get("jump_references", {})
455
+ ai_coaching = result.get("ai_coaching")
456
+
457
+ # Handle potential None values safely
458
+ jump_height = jump_metrics.get('jump_height_cm', 0) or 0
459
+ flight_time = jump_metrics.get('flight_time_s', 0) or 0
460
+ peak_power = jump_metrics.get('peak_power_watts', 0) or 0
461
+ rfd = jump_metrics.get('rate_of_force_development', 0) or 0
462
+
463
+ # Format comprehensive results
464
+ results_text = f"""
465
+ # 🎬 Complete Athletic Performance Analysis
466
+
467
+ ## 📊 Performance Metrics
468
+ - **Jump Height**: {jump_height:.2f} cm
469
+ - **Relative Jump**: {(jump_height/user_height_cm*100):.1f}% of body height
470
+ - **Flight Time**: {flight_time:.3f} seconds
471
+ - **Peak Power**: {peak_power:.0f} watts
472
+ - **Rate of Force Development**: {rfd:.2f}
473
+
474
+ ## 📏 Performance Comparison
475
+ - **Your Performance**: {jump_height:.1f} cm
476
+ - **Average for {gender}**: {jump_references.get('average', 0):.1f} cm
477
+ - **Professional Level**: {jump_references.get('professional', 0):.1f} cm
478
+
479
+ ## 🎥 Video Analysis Features
480
+ - ✅ **Real-time Pose Tracking**: Skeleton overlay throughout jump
481
+ - ✅ **Performance Reference Lines**: Average vs Professional benchmarks
482
+ - ✅ **Knee Strain Detection**: Automatic form analysis with warnings
483
+ - ✅ **Live Metrics Display**: Frame-by-frame jump height tracking
484
+
485
+ """
486
+
487
+ # Add AI coaching analysis if available
488
+ if ai_coaching and ai_coaching.get("success"):
489
+ results_text += f"""
490
+ ## 🤖 AI Sports Coach Analysis
491
+
492
+ {ai_coaching.get('analysis', 'AI analysis not available')}
493
+
494
+ ---
495
+ *Analysis powered by Google Gemini AI*
496
+ """
497
+ elif gemini_api_key:
498
+ results_text += """
499
+ ## 🤖 AI Sports Coach Analysis
500
+
501
+ ❌ AI coaching analysis failed. Please check your API key and try again.
502
+ """
503
+ else:
504
+ results_text += """
505
+ ## 🤖 AI Sports Coach Analysis
506
+
507
+ 💡 **Provide a Gemini API key to get personalized sports recommendations and technique improvements!**
508
+
509
+ Get your free API key at: [Google AI Studio](https://aistudio.google.com/app/apikey)
510
+ """
511
+
512
+ # Create comprehensive summary dataframe
513
+ summary_data = [
514
+ ["Jump Height", f"{jump_height:.2f} cm"],
515
+ ["Flight Time", f"{flight_time:.3f} seconds"],
516
+ ["Peak Power", f"{peak_power:.0f} watts"],
517
+ ["Rate of Force Development", f"{rfd:.2f}"],
518
+ ["Performance vs Average", f"{((jump_height/jump_references.get('average', 1))*100):.0f}%"],
519
+ ["Performance vs Pro", f"{((jump_height/jump_references.get('professional', 1))*100):.0f}%"],
520
+ ["Video Features", "Pose + References + Strain Detection"],
521
+ ["AI Coaching", "✅ Included" if ai_coaching and ai_coaching.get("success") else "❌ Not Available"],
522
+ ]
523
+
524
+ summary_df = pd.DataFrame(summary_data, columns=["Metric", "Value"])
525
+
526
+ # Return results with video for streaming
527
+ return results_text, summary_df, video_path, "✅ Complete analysis ready!"
528
+
529
+ except Exception as e:
530
+ return f"❌ Unexpected error: {str(e)}", None, None, None
531
+
532
+
533
  # Create Gradio interface
534
  def create_interface():
535
+ with gr.Blocks(title="🏃‍♂️ Athletic Ability Analysis") as app:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
536
  gr.Markdown("""
537
+ # 🏃‍♂️ Athletic Ability Analysis & AI Sports Coach
538
+
539
+ Analyze jumping performance from videos using computer vision and get AI-powered sports coaching recommendations.
540
+ Upload a video or provide a YouTube URL to get detailed metrics and personalized coaching insights.
541
+
542
+ ## 🚀 Features
543
+ - **📊 Biomechanical Analysis**: Comprehensive jump metrics (height, power, force, RFD)
544
+ - **🤖 AI Sports Coach**: Personalized sport recommendations and technique improvements
545
+ - **🎬 Annotated Videos**: Generate training videos with pose tracking and performance overlays
546
+ - **⚠️ Technique Analysis**: Real-time knee strain detection and form corrections
547
+ - **🎯 Performance Insights**: Professional-grade analysis and training suggestions
548
+ - **📺 Video Streaming**: Watch your annotated analysis video directly in the browser
549
+
550
+ ## 📋 Instructions
551
+ 1. Enter your height in centimeters and weight in kilograms
552
+ 2. Choose your analysis type:
553
+ - **📊 Standard Analysis**: Get detailed biomechanical metrics only
554
+ - **🎬 Complete Analysis**: Get annotated video + AI coaching + streaming (Recommended!)
555
+ 3. Provide a video (YouTube URL or file upload)
556
+ 4. Get comprehensive results with video streaming and AI coaching insights
557
  """)
558
 
559
  with gr.Row():
560
  with gr.Column():
561
  user_height = gr.Number(
562
+ label="Your Height (cm)",
563
+ value=175,
564
+ minimum=100,
565
+ maximum=250
566
+ )
567
  with gr.Column():
568
  user_weight = gr.Number(
569
  label="Your Weight (kg)",
 
571
  minimum=30,
572
  maximum=200
573
  )
574
+ gr.Markdown("💡 *Enter your height and weight for accurate biomechanical calculations*")
575
 
576
  with gr.Tabs():
577
  # YouTube URL Tab
578
+ with gr.TabItem("🎥 YouTube Video"):
579
+ gr.Markdown("📺 *Paste a YouTube URL containing a video of someone jumping*")
580
  youtube_url = gr.Textbox(
581
  label="YouTube URL",
582
  placeholder="https://youtube.com/watch?v=..."
583
  )
584
+ youtube_btn = gr.Button("🚀 Analyze YouTube Video", variant="primary")
585
 
586
  # File Upload Tab
587
+ with gr.TabItem("📁 Upload Video"):
588
+ gr.Markdown("📁 *Upload a video file showing someone performing a jump*")
589
  video_file = gr.File(
590
+ label="Upload Video File",
591
  file_types=[".mp4", ".avi", ".mov", ".mkv", ".webm"]
592
  )
593
+ file_btn = gr.Button("🚀 Analyze Uploaded Video", variant="primary")
594
 
595
  # Complete Analysis Tab
596
+ with gr.TabItem("🎬 Complete Analysis + AI Coach"):
597
+ gr.Markdown("""
598
+ ## 🎬 Complete Athletic Performance Analysis
599
+
600
+ Get the ultimate training analysis combining:
601
+
602
+ ### 🎥 Annotated Training Video
603
+ - **🦴 Real-time Pose Tracking**: Skeleton overlay throughout jump
604
+ - **📏 Performance Reference Lines**: Average vs Professional benchmarks
605
+ - **⚠️ Knee Strain Detection**: Automatic form analysis with red warnings
606
+ - **📊 Live Metrics Display**: Frame-by-frame jump height tracking
607
+
608
+ ### 🤖 AI Sports Coach Analysis
609
+ - **🏆 Sport Recommendations**: Top 3 sports matching your athletic profile
610
+ - **🎯 Technique Improvements**: Specific jump form corrections
611
+ - **📈 Training Insights**: Personalized performance enhancement tips
612
+
613
+ ### 📺 Video Streaming
614
+ - **Watch directly in browser**: No downloads required
615
+ - **Professional annotations**: Training-ready video output
616
+ - **Shareable results**: Perfect for coaches and athletes
617
+ """)
618
 
619
  with gr.Row():
620
  with gr.Column():
 
623
  label="Gender",
624
  value="Male"
625
  )
626
+ gr.Markdown("*Used for performance references and AI analysis*")
627
 
628
+ # Check if API key is available in environment
629
  default_api_key = os.getenv("GEMINI_API_KEY", "")
630
  complete_gemini_key = gr.Textbox(
631
+ label="Gemini API Key (Optional for AI Coaching)",
632
+ placeholder="Enter your Google Gemini API key for AI coaching" if not default_api_key else "API key loaded from environment",
633
  type="password",
634
  value=default_api_key
635
  )
636
 
637
+ with gr.Row():
638
+ test_api_btn = gr.Button("🧪 Test API Key", size="sm")
639
+
640
  api_test_result = gr.Textbox(
641
+ label="API Test Result",
642
+ lines=3,
643
  interactive=False,
644
  visible=False
645
  )
646
+
647
+ gr.Markdown("""
648
+ 💡 **Get your free API key**: [Google AI Studio](https://aistudio.google.com/app/apikey)
649
+
650
+ ⚠️ **Note**: Video generation works without API key, but AI coaching requires one
651
+ """)
652
 
653
  with gr.Column():
654
  complete_youtube_url = gr.Textbox(
655
+ label="YouTube URL (Option 1)",
656
  placeholder="https://youtube.com/watch?v=..."
657
  )
658
  complete_video_file = gr.File(
659
+ label="Upload Video File (Option 2)",
660
  file_types=[".mp4", ".avi", ".mov", ".mkv", ".webm"]
661
  )
662
+ gr.Markdown("*Provide either a YouTube URL or upload a video file*")
663
 
664
+ complete_analysis_btn = gr.Button("🚀 Get Complete Analysis + Video", variant="primary", size="lg")
665
 
666
  # Results section
667
+ gr.Markdown("## 📊 Analysis Results")
 
 
 
 
 
 
 
 
668
 
669
  with gr.Row():
670
  with gr.Column(scale=2):
671
+ results_text = gr.Markdown(label="Results")
672
  with gr.Column(scale=1):
673
  results_table = gr.Dataframe(
674
+ label="Metrics Summary",
675
  headers=["Metric", "Value"],
676
  datatype=["str", "str"]
677
  )
678
 
679
+ # Video streaming section
680
+ with gr.Row():
681
+ with gr.Column():
682
+ analysis_video = gr.Video(
683
+ label="📺 Annotated Training Video",
684
+ visible=False
685
+ )
686
+
687
  status_message = gr.Textbox(label="Status", interactive=False)
688
 
689
+ # Video requirements
690
+ gr.Markdown("""
691
+ ## 📝 Video Requirements
692
+
693
+ For best results, ensure your videos meet these criteria:
694
+
695
+ - **Full body visible**: The person should be completely visible in the frame
696
+ - **Clear movement**: Good lighting and minimal background clutter
697
+ - **Vertical jumps**: Works best with straight vertical jumps
698
+ - **Duration**: 3-30 seconds is optimal
699
+ - **Quality**: Higher quality videos produce better results
700
+ - **Public videos**: For YouTube, ensure the video is not private
701
+
702
+ ## 🔬 How it Works
703
+
704
+ 1. **Pose Detection**: Uses Google's MediaPipe to detect human pose landmarks
705
+ 2. **Hip Tracking**: Tracks the midpoint between left and right hip joints
706
+ 3. **Biomechanical Analysis**: Calculates comprehensive metrics based on hip trajectory:
707
+ - **Jump Height**: Relative to your body size
708
+ - **Flight Time**: Duration in the air
709
+ - **Peak Power Output**: Maximum power generated during takeoff
710
+ - **Rate of Force Development (RFD)**: Speed of force generation
711
+ - **Ground Contact Time**: Efficiency in stretch-shortening cycle
712
+ - **Impulse & Peak Force**: Force characteristics during takeoff
713
+ - **Takeoff Phase Duration**: Time from crouch to launch
714
+ """)
715
+
716
  # Event handlers
717
  youtube_btn.click(
718
  fn=analyze_jump_from_youtube,
 
757
  inputs=[complete_gemini_key],
758
  outputs=[api_test_result]
759
  )
760
+
761
+ # Example section
762
+ gr.Examples(
763
+ examples=[
764
+ ["https://www.youtube.com/watch?v=dQw4w9WgXcQ", 175, 75], # This is just a placeholder
765
+ ],
766
+ inputs=[youtube_url, user_height, user_weight],
767
+ label="📚 Example (Replace with actual jump video URLs)"
768
+ )
769
 
770
  return app
771
 
athletic_performance.py CHANGED
@@ -9,7 +9,6 @@ import yt_dlp
9
  import json
10
  import requests
11
  import math
12
- import pandas as pd
13
 
14
  # MediaPipe pose landmarks
15
  LHIP, RHIP = 23, 24
@@ -827,44 +826,31 @@ def generate_annotated_video_from_file(video_file_path, user_height_cm, user_wei
827
  return {"error": f"Error during video generation: {str(e)}"}
828
 
829
 
830
- def complete_analysis_with_video_stream(youtube_url, video_file, user_height_cm, user_weight_kg, gender, gemini_api_key, progress=None):
831
- """Complete analysis with AI coaching and video streaming."""
 
832
 
833
- # Validate inputs
834
- if not user_height_cm or user_height_cm <= 0:
835
- return "❌ Please provide a valid height", None, None, None
836
-
837
- if not user_weight_kg or user_weight_kg <= 0:
838
- return "❌ Please provide a valid weight", None, None, None
839
-
840
- if not gender:
841
- return "❌ Please select your gender", None, None, None
842
-
843
- # Determine video source and path
844
- video_source = None
845
- video_path_or_url = None
846
-
847
- if youtube_url and youtube_url.strip():
848
- video_source = "youtube"
849
- video_path_or_url = youtube_url.strip()
850
- if progress:
851
- progress(0.05, desc="Processing YouTube video...")
852
- elif video_file:
853
- video_source = "file"
854
- video_path_or_url = video_file.name
855
- if progress:
856
- progress(0.05, desc="Processing uploaded video...")
857
- else:
858
- return "❌ Please provide either a YouTube URL or upload a video file", None, None, None
859
 
860
  try:
861
  # Phase 1: Generate annotated video (50% of progress)
862
- if progress:
863
- progress(0.1, desc="Starting video generation...")
864
 
865
  def video_progress(prog, desc):
866
- if progress:
867
- progress(0.1 + (prog * 0.4), desc=f"Video: {desc}")
868
 
869
  # Generate annotated video
870
  if video_source == "youtube":
@@ -877,16 +863,20 @@ def complete_analysis_with_video_stream(youtube_url, video_file, user_height_cm,
877
  )
878
 
879
  if "error" in video_result:
880
- return f"❌ Video generation failed: {video_result['error']}", None, None, None
881
 
882
  # Phase 2: AI Sports Coaching Analysis (30% of progress)
883
- if progress:
884
- progress(0.5, desc="Generating AI sports coaching analysis...")
885
 
886
  ai_result = None
887
- if gemini_api_key:
888
  jump_metrics = video_result.get("jump_metrics", {})
889
 
 
 
 
 
890
  ai_result = get_ai_sports_coaching_analysis(
891
  jump_height_cm=jump_metrics.get('jump_height_cm', 0),
892
  user_height_cm=user_height_cm,
@@ -894,97 +884,42 @@ def complete_analysis_with_video_stream(youtube_url, video_file, user_height_cm,
894
  peak_power_watts=jump_metrics.get('peak_power_watts'),
895
  flight_time_s=jump_metrics.get('flight_time_s'),
896
  rfd=jump_metrics.get('rate_of_force_development'),
897
- api_key=gemini_api_key
898
  )
899
 
900
- if progress:
901
- progress(0.8, desc="AI analysis complete!")
902
 
903
  # Phase 3: Combine results (20% of progress)
904
- if progress:
905
- progress(0.9, desc="Finalizing comprehensive analysis...")
906
 
907
  jump_metrics = video_result.get("jump_metrics", {})
908
  jump_references = video_result.get("jump_references", {})
909
 
910
- # Handle potential None values safely
911
- jump_height = jump_metrics.get('jump_height_cm', 0) or 0
912
- flight_time = jump_metrics.get('flight_time_s', 0) or 0
913
- peak_power = jump_metrics.get('peak_power_watts', 0) or 0
914
- rfd = jump_metrics.get('rate_of_force_development', 0) or 0
915
-
916
- # Format comprehensive results
917
- results_text = f"""
918
- # 🎬 Complete Athletic Performance Analysis
919
-
920
- ## 📊 Performance Metrics
921
- - **Jump Height**: {jump_height:.2f} cm
922
- - **Relative Jump**: {(jump_height/user_height_cm*100):.1f}% of body height
923
- - **Flight Time**: {flight_time:.3f} seconds
924
- - **Peak Power**: {peak_power:.0f} watts
925
- - **Rate of Force Development**: {rfd:.2f}
926
-
927
- ## 📏 Performance Comparison
928
- - **Your Performance**: {jump_height:.1f} cm
929
- - **Average for {gender}**: {jump_references.get('average', 0):.1f} cm
930
- - **Professional Level**: {jump_references.get('professional', 0):.1f} cm
931
-
932
- ## 🎥 Video Analysis Features
933
- - ✅ **Real-time Pose Tracking**: Skeleton overlay throughout jump
934
- - ✅ **Performance Reference Lines**: Average vs Professional benchmarks
935
- - ✅ **Knee Strain Detection**: Automatic form analysis with warnings
936
- - ✅ **Live Metrics Display**: Frame-by-frame jump height tracking
937
-
938
- """
939
-
940
- # Add AI coaching analysis if available
941
- if ai_result and ai_result.get("success"):
942
- results_text += f"""
943
- ## 🤖 AI Sports Coach Analysis
944
-
945
- {ai_result.get('analysis', 'AI analysis not available')}
946
-
947
- ---
948
- *Analysis powered by Google Gemini AI*
949
- """
950
- elif gemini_api_key:
951
- results_text += """
952
- ## 🤖 AI Sports Coach Analysis
953
-
954
- ❌ AI coaching analysis failed. Please check your API key and try again.
955
- """
956
- else:
957
- results_text += """
958
- ## 🤖 AI Sports Coach Analysis
959
-
960
- 💡 **Provide a Gemini API key to get personalized sports recommendations and technique improvements!**
961
-
962
- Get your free API key at: [Google AI Studio](https://aistudio.google.com/app/apikey)
963
- """
964
-
965
- # Create comprehensive summary dataframe
966
- summary_data = [
967
- ["Jump Height", f"{jump_height:.2f} cm"],
968
- ["Flight Time", f"{flight_time:.3f} seconds"],
969
- ["Peak Power", f"{peak_power:.0f} watts"],
970
- ["Rate of Force Development", f"{rfd:.2f}"],
971
- ["Performance vs Average", f"{((jump_height/jump_references.get('average', 1))*100):.0f}%"],
972
- ["Performance vs Pro", f"{((jump_height/jump_references.get('professional', 1))*100):.0f}%"],
973
- ["Video Features", "Pose + References + Strain Detection"],
974
- ["AI Coaching", "✅ Included" if ai_result and ai_result.get("success") else "❌ Not Available"],
975
- ]
976
-
977
- summary_df = pd.DataFrame(summary_data, columns=["Metric", "Value"])
978
 
979
- # Return results with video for streaming
980
- video_path = video_result.get("output_video_path")
981
- if progress:
982
- progress(1.0, desc="Complete analysis ready!")
983
 
984
- return results_text, summary_df, video_path, "✅ Complete analysis ready!"
985
 
986
  except Exception as e:
987
- return f" Unexpected error: {str(e)}", None, None, None
988
 
989
 
990
  def get_performance_insights(result_dict):
 
9
  import json
10
  import requests
11
  import math
 
12
 
13
  # MediaPipe pose landmarks
14
  LHIP, RHIP = 23, 24
 
826
  return {"error": f"Error during video generation: {str(e)}"}
827
 
828
 
829
+ def generate_complete_analysis_with_video(video_source, video_path_or_url, user_height_cm, user_weight_kg, gender, api_key=None, progress_callback=None):
830
+ """
831
+ Complete analysis combining annotated video generation + AI sports coaching.
832
 
833
+ Args:
834
+ video_source (str): "youtube" or "file"
835
+ video_path_or_url (str): YouTube URL or file path
836
+ user_height_cm (float): User's height in centimeters
837
+ user_weight_kg (float): User's weight in kilograms
838
+ gender (str): "Male" or "Female"
839
+ api_key (str, optional): Gemini API key for AI coaching
840
+ progress_callback (callable, optional): Progress tracking function
841
+
842
+ Returns:
843
+ dict: Complete analysis with video path, AI coaching, and metrics
844
+ """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
845
 
846
  try:
847
  # Phase 1: Generate annotated video (50% of progress)
848
+ if progress_callback:
849
+ progress_callback(0.1, "Starting comprehensive analysis...")
850
 
851
  def video_progress(prog, desc):
852
+ if progress_callback:
853
+ progress_callback(0.1 + (prog * 0.4), f"Video: {desc}")
854
 
855
  # Generate annotated video
856
  if video_source == "youtube":
 
863
  )
864
 
865
  if "error" in video_result:
866
+ return video_result
867
 
868
  # Phase 2: AI Sports Coaching Analysis (30% of progress)
869
+ if progress_callback:
870
+ progress_callback(0.5, "Generating AI sports coaching analysis...")
871
 
872
  ai_result = None
873
+ if api_key:
874
  jump_metrics = video_result.get("jump_metrics", {})
875
 
876
+ def ai_progress(prog, desc):
877
+ if progress_callback:
878
+ progress_callback(0.5 + (prog * 0.3), f"AI: {desc}")
879
+
880
  ai_result = get_ai_sports_coaching_analysis(
881
  jump_height_cm=jump_metrics.get('jump_height_cm', 0),
882
  user_height_cm=user_height_cm,
 
884
  peak_power_watts=jump_metrics.get('peak_power_watts'),
885
  flight_time_s=jump_metrics.get('flight_time_s'),
886
  rfd=jump_metrics.get('rate_of_force_development'),
887
+ api_key=api_key
888
  )
889
 
890
+ if progress_callback:
891
+ progress_callback(0.8, "AI analysis complete!")
892
 
893
  # Phase 3: Combine results (20% of progress)
894
+ if progress_callback:
895
+ progress_callback(0.9, "Finalizing comprehensive analysis...")
896
 
897
  jump_metrics = video_result.get("jump_metrics", {})
898
  jump_references = video_result.get("jump_references", {})
899
 
900
+ # Create comprehensive result
901
+ result = {
902
+ "success": True,
903
+ "video_path": video_result.get("output_video_path"),
904
+ "jump_metrics": jump_metrics,
905
+ "jump_references": jump_references,
906
+ "ai_coaching": ai_result if ai_result and "error" not in ai_result else None,
907
+ "video_features": {
908
+ "pose_tracking": True,
909
+ "reference_lines": True,
910
+ "knee_strain_detection": True,
911
+ "live_metrics": True
912
+ },
913
+ "analysis_type": "complete_with_video"
914
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
915
 
916
+ if progress_callback:
917
+ progress_callback(1.0, "Complete analysis ready!")
 
 
918
 
919
+ return result
920
 
921
  except Exception as e:
922
+ return {"error": f"Error during complete analysis: {str(e)}"}
923
 
924
 
925
  def get_performance_insights(result_dict):