chenemii commited on
Commit
6baea5b
·
1 Parent(s): 27522c2

Add Git LFS and update with new logo and improvements

Browse files

- Setup Git LFS for PNG files to comply with Hugging Face requirements
- Add new horizontal logo (par-ity project horizontal logo.png)
- Improve navigation with simplified sidebar menu
- Enhance RAG system stability and error handling
- Add footer with clickable link to par-ityproject.org
- Remove old logo files (transparent.png, transparent2.png)

app/.gitattributes ADDED
@@ -0,0 +1 @@
 
 
1
+ *.png filter=lfs diff=lfs merge=lfs -text
app/1.png CHANGED

Git LFS Details

  • SHA256: 4658675b83d0a05dbe0a0812c2785a10c6b3f18d0ab0bae27bb857bcbab621d5
  • Pointer size: 128 Bytes
  • Size of remote file: 104 Bytes
app/golf_swing_rag.py CHANGED
@@ -369,81 +369,162 @@ class GolfSwingRAG:
369
  print(f"Created {len(all_chunks)} text chunks")
370
 
371
  def create_embeddings(self, force_recreate: bool = False):
372
- """Create embeddings for all text chunks"""
373
- # Determine the correct base directory for embeddings files
374
- if os.path.exists("golf_swing_articles_complete.csv"):
375
- # Running from project root
376
- embeddings_file = "golf_swing_embeddings.pkl"
377
- index_file = "golf_swing_index.faiss"
378
- else:
379
- # Running from app directory
380
- embeddings_file = "../golf_swing_embeddings.pkl"
381
- index_file = "../golf_swing_index.faiss"
382
-
383
- if not force_recreate and os.path.exists(embeddings_file) and os.path.exists(index_file):
384
- print("Loading existing embeddings...")
385
- with open(embeddings_file, 'rb') as f:
386
- data = pickle.load(f)
387
- self.chunks = data['chunks']
388
- self.metadata = data['metadata']
389
- self.index = faiss.read_index(index_file)
390
- print(f"Loaded {len(self.chunks)} chunks with embeddings")
391
- return
392
-
393
- print("Creating embeddings...")
394
- if not self.chunks:
395
- self.load_and_process_data()
396
-
397
- # Create embeddings in batches
398
- batch_size = 32
399
- all_embeddings = []
400
-
401
- for i in range(0, len(self.chunks), batch_size):
402
- batch_chunks = self.chunks[i:i+batch_size]
403
- batch_embeddings = self.embedding_model.encode(batch_chunks, show_progress_bar=True)
404
- all_embeddings.append(batch_embeddings)
405
- print(f"Processed {min(i+batch_size, len(self.chunks))}/{len(self.chunks)} chunks")
406
-
407
- # Combine all embeddings
408
- embeddings = np.vstack(all_embeddings)
409
-
410
- # Create FAISS index
411
- dimension = embeddings.shape[1]
412
- self.index = faiss.IndexFlatIP(dimension) # Inner product for cosine similarity
413
-
414
- # Normalize embeddings for cosine similarity
415
- faiss.normalize_L2(embeddings)
416
- self.index.add(embeddings)
417
-
418
- # Save embeddings and index
419
- with open(embeddings_file, 'wb') as f:
420
- pickle.dump({
421
- 'chunks': self.chunks,
422
- 'metadata': self.metadata
423
- }, f)
424
- faiss.write_index(self.index, index_file)
425
-
426
- print(f"Created and saved embeddings for {len(self.chunks)} chunks")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
427
 
428
  def search_similar_chunks(self, query: str, top_k: int = 5) -> List[Dict]:
429
- """Search for similar chunks using semantic similarity"""
430
- # Create query embedding
431
- query_embedding = self.embedding_model.encode([query])
432
- faiss.normalize_L2(query_embedding)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
433
 
434
- # Search in FAISS index
435
- scores, indices = self.index.search(query_embedding, top_k)
436
 
437
- results = []
438
- for score, idx in zip(scores[0], indices[0]):
439
- if idx < len(self.chunks): # Valid index
440
- results.append({
441
- 'chunk': self.chunks[idx],
442
- 'metadata': self.metadata[idx],
443
- 'similarity_score': float(score)
 
 
 
 
 
 
 
444
  })
445
 
446
- return results
 
 
447
 
448
  def generate_response(self, query: str, context_chunks: List[Dict]) -> str:
449
  """Generate response using OpenAI API with context"""
 
369
  print(f"Created {len(all_chunks)} text chunks")
370
 
371
  def create_embeddings(self, force_recreate: bool = False):
372
+ """Create embeddings for all text chunks with enhanced error handling"""
373
+ try:
374
+ # Determine the correct base directory for embeddings files
375
+ if os.path.exists("golf_swing_articles_complete.csv"):
376
+ # Running from project root
377
+ embeddings_file = "golf_swing_embeddings.pkl"
378
+ index_file = "golf_swing_index.faiss"
379
+ else:
380
+ # Running from app directory
381
+ embeddings_file = "../golf_swing_embeddings.pkl"
382
+ index_file = "../golf_swing_index.faiss"
383
+
384
+ if not force_recreate and os.path.exists(embeddings_file) and os.path.exists(index_file):
385
+ print("Loading existing embeddings...")
386
+ try:
387
+ with open(embeddings_file, 'rb') as f:
388
+ data = pickle.load(f)
389
+ self.chunks = data['chunks']
390
+ self.metadata = data['metadata']
391
+ self.index = faiss.read_index(index_file)
392
+ print(f"Loaded {len(self.chunks)} chunks with embeddings")
393
+ return
394
+ except Exception as e:
395
+ print(f"Failed to load existing embeddings: {e}")
396
+ print("Will create new embeddings...")
397
+
398
+ print("Creating embeddings...")
399
+ if not self.chunks:
400
+ self.load_and_process_data()
401
+
402
+ # Reduce batch size to prevent memory issues
403
+ batch_size = 16 # Reduced from 32
404
+ all_embeddings = []
405
+
406
+ # Add memory management
407
+ import gc
408
+
409
+ for i in range(0, len(self.chunks), batch_size):
410
+ try:
411
+ batch_chunks = self.chunks[i:i+batch_size]
412
+ print(f"Processing batch {i//batch_size + 1}/{(len(self.chunks) + batch_size - 1)//batch_size}")
413
+
414
+ # Create embeddings with reduced progress bar output
415
+ batch_embeddings = self.embedding_model.encode(
416
+ batch_chunks,
417
+ show_progress_bar=True,
418
+ convert_to_numpy=True,
419
+ normalize_embeddings=True # Normalize during encoding
420
+ )
421
+ all_embeddings.append(batch_embeddings)
422
+ print(f"Processed {min(i+batch_size, len(self.chunks))}/{len(self.chunks)} chunks")
423
+
424
+ # Force garbage collection after each batch
425
+ gc.collect()
426
+
427
+ except Exception as e:
428
+ print(f"Error processing batch {i//batch_size + 1}: {e}")
429
+ # Continue with next batch instead of failing completely
430
+ continue
431
+
432
+ if not all_embeddings:
433
+ raise Exception("Failed to create any embeddings")
434
+
435
+ # Combine all embeddings
436
+ print("Combining embeddings...")
437
+ embeddings = np.vstack(all_embeddings)
438
+
439
+ # Create FAISS index with error handling
440
+ print("Creating FAISS index...")
441
+ dimension = embeddings.shape[1]
442
+
443
+ # Use simpler FAISS index that's more stable
444
+ self.index = faiss.IndexFlatL2(dimension) # L2 distance instead of inner product
445
+
446
+ # Add embeddings to index
447
+ self.index.add(embeddings.astype('float32'))
448
+
449
+ # Save embeddings and index
450
+ print("Saving embeddings...")
451
+ try:
452
+ with open(embeddings_file, 'wb') as f:
453
+ pickle.dump({
454
+ 'chunks': self.chunks,
455
+ 'metadata': self.metadata
456
+ }, f)
457
+ faiss.write_index(self.index, index_file)
458
+ print(f"Created and saved embeddings for {len(self.chunks)} chunks")
459
+ except Exception as e:
460
+ print(f"Failed to save embeddings: {e}")
461
+ print("Embeddings created but not saved to disk")
462
+
463
+ except Exception as e:
464
+ print(f"Critical error in create_embeddings: {e}")
465
+ print("RAG system will operate in limited mode")
466
+ # Set up minimal fallback
467
+ self.chunks = self.chunks if hasattr(self, 'chunks') and self.chunks else []
468
+ self.metadata = self.metadata if hasattr(self, 'metadata') and self.metadata else []
469
+ self.index = None
470
 
471
  def search_similar_chunks(self, query: str, top_k: int = 5) -> List[Dict]:
472
+ """Search for similar chunks using semantic similarity with fallback"""
473
+ try:
474
+ if self.index is None:
475
+ print("FAISS index not available, using simple text matching fallback")
476
+ return self._fallback_search(query, top_k)
477
+
478
+ # Create query embedding
479
+ query_embedding = self.embedding_model.encode([query], convert_to_numpy=True, normalize_embeddings=True)
480
+
481
+ # Search in FAISS index (L2 distance, so lower scores are better)
482
+ scores, indices = self.index.search(query_embedding.astype('float32'), top_k)
483
+
484
+ # Convert results to list format
485
+ results = []
486
+ for i, (score, idx) in enumerate(zip(scores[0], indices[0])):
487
+ if idx < len(self.chunks): # Ensure valid index
488
+ results.append({
489
+ 'chunk': self.chunks[idx],
490
+ 'metadata': self.metadata[idx],
491
+ 'similarity_score': 1.0 / (1.0 + score) # Convert L2 distance to similarity
492
+ })
493
+
494
+ return results
495
+
496
+ except Exception as e:
497
+ print(f"Error in semantic search: {e}")
498
+ print("Falling back to simple text matching")
499
+ return self._fallback_search(query, top_k)
500
+
501
+ def _fallback_search(self, query: str, top_k: int = 5) -> List[Dict]:
502
+ """Simple text-based search fallback when semantic search fails"""
503
+ if not self.chunks:
504
+ return []
505
 
506
+ query_lower = query.lower()
507
+ query_words = set(query_lower.split())
508
 
509
+ # Score chunks based on word overlap
510
+ scored_chunks = []
511
+ for i, chunk in enumerate(self.chunks):
512
+ chunk_lower = chunk.lower()
513
+ chunk_words = set(chunk_lower.split())
514
+
515
+ # Calculate simple word overlap score
516
+ overlap = len(query_words.intersection(chunk_words))
517
+ if overlap > 0:
518
+ score = overlap / len(query_words)
519
+ scored_chunks.append({
520
+ 'chunk': chunk,
521
+ 'metadata': self.metadata[i] if i < len(self.metadata) else {},
522
+ 'similarity_score': score
523
  })
524
 
525
+ # Sort by score and return top_k
526
+ scored_chunks.sort(key=lambda x: x['similarity_score'], reverse=True)
527
+ return scored_chunks[:top_k]
528
 
529
  def generate_response(self, query: str, context_chunks: List[Dict]) -> str:
530
  """Generate response using OpenAI API with context"""
app/par-ity project horizontal logo.png ADDED

Git LFS Details

  • SHA256: efc2a6689a33aeba482922af0dcc86ada447d7c129f0dd30b2fef6e39cae17d7
  • Pointer size: 132 Bytes
  • Size of remote file: 1.41 MB
app/streamlit_app.py CHANGED
@@ -121,9 +121,11 @@ st.markdown("""
121
 
122
  @st.cache_resource
123
  def load_rag_system():
124
- """Load and initialize the RAG system (cached for performance)"""
125
  if not RAG_AVAILABLE:
 
126
  return None
 
127
  try:
128
  print("=== RAG System Loading Debug ===")
129
  with st.spinner("Loading golf swing knowledge base..."):
@@ -135,19 +137,30 @@ def load_rag_system():
135
  rag.load_and_process_data()
136
  print("✓ Data loaded and processed successfully")
137
 
138
- print("Creating embeddings...")
139
- rag.create_embeddings()
140
- print("✓ Embeddings created successfully")
 
 
 
 
 
 
 
 
 
 
141
 
142
- print("✓ RAG system loaded successfully!")
143
  print("=== End RAG System Loading Debug ===")
144
  return rag
 
145
  except Exception as e:
146
- print(f"✗ Error loading RAG system: {str(e)}")
147
  print(f"Error type: {type(e).__name__}")
148
  import traceback
149
  print(f"Full traceback: {traceback.format_exc()}")
150
- st.error(f"Error loading RAG system: {str(e)}")
151
  return None
152
 
153
  def display_rag_sources(sources):
@@ -636,14 +649,14 @@ def main():
636
  """, unsafe_allow_html=True)
637
 
638
  # Centered logo at the top
639
- col1, col2, col3 = st.columns([1, 2, 1])
640
 
641
  with col1:
642
  st.write("") # Empty space
643
 
644
  with col2:
645
  try:
646
- st.image("app/transparent.png", width=400)
647
  except:
648
  st.markdown('<div class="main-header"><h1>⛳ Par-ity Project</h1></div>', unsafe_allow_html=True)
649
 
@@ -651,7 +664,7 @@ def main():
651
  st.write("") # Empty space
652
 
653
  # Reduce spacing after logo
654
- st.markdown('<div style="margin-top: -20px;"></div>', unsafe_allow_html=True)
655
 
656
  # Initialize session state for step-based flow
657
  if 'current_step' not in st.session_state:
@@ -684,113 +697,27 @@ def main():
684
  enable_gpt = any_service_available
685
  sample_rate = 1
686
 
687
- # Hamburger menu navigation (appears after Step 3)
688
  if st.session_state.current_step >= 3:
689
- # Add hamburger menu CSS and HTML
690
- st.markdown("""
691
- <style>
692
- .hamburger-menu {
693
- position: fixed;
694
- top: 20px;
695
- left: 20px;
696
- z-index: 1000;
697
- background: #0B3B0B;
698
- color: white;
699
- border: none;
700
- border-radius: 8px;
701
- padding: 10px;
702
- font-size: 18px;
703
- cursor: pointer;
704
- box-shadow: 0 2px 8px rgba(11, 59, 11, 0.3);
705
- }
706
-
707
- .hamburger-menu:hover {
708
- background: #4CAF50;
709
- }
710
-
711
- .menu-content {
712
- position: fixed;
713
- top: 60px;
714
- left: 20px;
715
- background: rgba(255, 255, 255, 0.95);
716
- border: 2px solid #4CAF50;
717
- border-radius: 10px;
718
- padding: 15px;
719
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
720
- z-index: 999;
721
- min-width: 180px;
722
- backdrop-filter: blur(10px);
723
- }
724
-
725
- .menu-hidden {
726
- display: none;
727
- }
728
- </style>
729
-
730
- <div class="hamburger-menu" onclick="toggleMenu()">
731
-
732
- </div>
733
-
734
- <div id="menu-content" class="menu-content menu-hidden">
735
- <h4 style="color: #0B3B0B; margin: 0 0 10px 0;">Navigation</h4>
736
- </div>
737
-
738
- <script>
739
- function toggleMenu() {
740
- const menu = document.getElementById('menu-content');
741
- menu.classList.toggle('menu-hidden');
742
- }
743
-
744
- // Close menu when clicking outside
745
- document.addEventListener('click', function(event) {
746
- const menu = document.getElementById('menu-content');
747
- const hamburger = document.querySelector('.hamburger-menu');
748
- if (!menu.contains(event.target) && !hamburger.contains(event.target)) {
749
- menu.classList.add('menu-hidden');
750
- }
751
- });
752
- </script>
753
- """, unsafe_allow_html=True)
754
-
755
- # Navigation menu in main content (only show when menu should be visible)
756
- if 'show_menu' not in st.session_state:
757
- st.session_state.show_menu = False
758
 
759
- # Menu toggle button
760
- col1, col2, col3 = st.columns([1, 8, 1])
761
- with col1:
762
- if st.button("☰", key="menu_toggle", help="Navigation Menu"):
763
- st.session_state.show_menu = not st.session_state.show_menu
 
 
 
 
 
 
 
 
 
764
  st.rerun()
765
-
766
- # Show menu content if toggled
767
- if st.session_state.show_menu:
768
- with st.container():
769
- st.markdown("#### 🧭 Navigation")
770
-
771
- nav_col1, nav_col2 = st.columns(2)
772
-
773
- with nav_col1:
774
- if st.button("🎯 Improvements", key="nav_improvements", use_container_width=True):
775
- st.session_state.current_step = 4
776
- st.session_state.show_menu = False
777
- st.rerun()
778
-
779
- with nav_col2:
780
- if st.button("💬 Chatbot", key="nav_chatbot", use_container_width=True):
781
- st.session_state.current_step = 5
782
- st.session_state.show_menu = False
783
- st.rerun()
784
-
785
- if st.button("🔄 Start Over", key="nav_start_over", use_container_width=True):
786
- # Reset all session state
787
- for key in list(st.session_state.keys()):
788
- if key != 'session_initialized':
789
- del st.session_state[key]
790
- st.session_state.current_step = 1
791
- st.rerun()
792
-
793
- st.markdown("---")
794
 
795
  # Step-based content rendering
796
  if st.session_state.current_step == 1:
@@ -803,6 +730,15 @@ def main():
803
  render_step_4()
804
  elif st.session_state.current_step == 5:
805
  render_step_5()
 
 
 
 
 
 
 
 
 
806
 
807
  def render_step_1():
808
  """Step 1: Upload Video"""
@@ -814,11 +750,13 @@ def render_step_1():
814
 
815
  with col1:
816
  st.markdown("#### YouTube URL")
817
- youtube_url = st.text_input("Enter YouTube URL of golf swing:", key="youtube_input")
 
818
 
819
  with col2:
820
  st.markdown("#### Upload Video File")
821
- uploaded_file = st.file_uploader("Upload a golf swing video", type=["mp4", "mov", "avi"], key="video_upload")
 
822
 
823
  # Analyze button
824
  if st.button("🏌️ Start Analysis", key="start_analysis", use_container_width=True):
@@ -859,7 +797,6 @@ def render_step_2():
859
  st.markdown('<h2 style="color: #0B3B0B; font-family: Georgia, serif;">Step 2: Analyzing Video and Pose</h2>', unsafe_allow_html=True)
860
 
861
  st.markdown("🔄 **Processing your swing video...**")
862
- st.markdown("Please wait while we analyze your golf swing.")
863
 
864
  video_path = st.session_state.analysis_data.get('video_path')
865
 
@@ -943,7 +880,7 @@ def render_step_3():
943
  with col1:
944
  st.markdown("""
945
  <div style="text-align: center; padding: 20px; border: 2px solid #4CAF50; border-radius: 15px; margin: 10px 0;">
946
- <h3 style="color: #0B3B0B;">🎯 AI-Powered Improvements</h3>
947
  <p>Get personalized swing analysis with specific tips for improvement</p>
948
  </div>
949
  """, unsafe_allow_html=True)
@@ -955,7 +892,7 @@ def render_step_3():
955
  with col2:
956
  st.markdown("""
957
  <div style="text-align: center; padding: 20px; border: 2px solid #4CAF50; border-radius: 15px; margin: 10px 0;">
958
- <h3 style="color: #0B3B0B;">💬 Golf Expert Q&A</h3>
959
  <p>Ask specific questions about golf swing technique from our knowledge base</p>
960
  </div>
961
  """, unsafe_allow_html=True)
 
121
 
122
  @st.cache_resource
123
  def load_rag_system():
124
+ """Load and initialize the RAG system (cached for performance) with enhanced error handling"""
125
  if not RAG_AVAILABLE:
126
+ st.warning("RAG system not available - missing dependencies")
127
  return None
128
+
129
  try:
130
  print("=== RAG System Loading Debug ===")
131
  with st.spinner("Loading golf swing knowledge base..."):
 
137
  rag.load_and_process_data()
138
  print("✓ Data loaded and processed successfully")
139
 
140
+ print("Creating embeddings (this may take a moment)...")
141
+ try:
142
+ rag.create_embeddings()
143
+ if hasattr(rag, 'index') and rag.index is not None:
144
+ print("✓ Embeddings created successfully with FAISS")
145
+ st.success("🎯 RAG system loaded with semantic search capabilities")
146
+ else:
147
+ print("⚠ Embeddings creation had issues, but fallback search available")
148
+ st.warning("⚠️ RAG system loaded with basic search (semantic search unavailable)")
149
+ except Exception as embedding_error:
150
+ print(f"⚠ Embedding creation failed: {embedding_error}")
151
+ print("RAG will use fallback search methods")
152
+ st.warning("⚠️ RAG system loaded with basic search only")
153
 
154
+ print("✓ RAG system initialization completed!")
155
  print("=== End RAG System Loading Debug ===")
156
  return rag
157
+
158
  except Exception as e:
159
+ print(f"✗ Critical error loading RAG system: {str(e)}")
160
  print(f"Error type: {type(e).__name__}")
161
  import traceback
162
  print(f"Full traceback: {traceback.format_exc()}")
163
+ st.error(f" RAG system failed to load: {str(e)}")
164
  return None
165
 
166
  def display_rag_sources(sources):
 
649
  """, unsafe_allow_html=True)
650
 
651
  # Centered logo at the top
652
+ col1, col2, col3 = st.columns([1, 1, 1])
653
 
654
  with col1:
655
  st.write("") # Empty space
656
 
657
  with col2:
658
  try:
659
+ st.image("app/par-ity project horizontal logo.png", width=850)
660
  except:
661
  st.markdown('<div class="main-header"><h1>⛳ Par-ity Project</h1></div>', unsafe_allow_html=True)
662
 
 
664
  st.write("") # Empty space
665
 
666
  # Reduce spacing after logo
667
+ st.markdown('<div style="margin-top: -25px;"></div>', unsafe_allow_html=True)
668
 
669
  # Initialize session state for step-based flow
670
  if 'current_step' not in st.session_state:
 
697
  enable_gpt = any_service_available
698
  sample_rate = 1
699
 
700
+ # Simple sidebar navigation (appears after Step 3)
701
  if st.session_state.current_step >= 3:
702
+ with st.sidebar:
703
+ st.markdown("### Navigation")
704
+ st.markdown("---")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
705
 
706
+ if st.button("🎯 Generate Improvements", key="nav_improvements", use_container_width=True):
707
+ st.session_state.current_step = 4
708
+ st.rerun()
709
+
710
+ if st.button("💬 Ask Questions", key="nav_chatbot", use_container_width=True):
711
+ st.session_state.current_step = 5
712
+ st.rerun()
713
+
714
+ if st.button("🔄 Start Over", key="nav_start_over", use_container_width=True):
715
+ # Reset all session state
716
+ for key in list(st.session_state.keys()):
717
+ if key != 'session_initialized':
718
+ del st.session_state[key]
719
+ st.session_state.current_step = 1
720
  st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
721
 
722
  # Step-based content rendering
723
  if st.session_state.current_step == 1:
 
730
  render_step_4()
731
  elif st.session_state.current_step == 5:
732
  render_step_5()
733
+
734
+ st.markdown("---")
735
+ # Footer with website link
736
+ st.markdown(
737
+ '<div style="text-align: center; margin-top: 5px; margin-bottom: 1px;">'
738
+ '<a href="https://par-ityproject.org" target="_blank" style="color: #0B3B0B; text-decoration: none; font-size: 14px;">par-ityproject.org</a>'
739
+ '</div>',
740
+ unsafe_allow_html=True
741
+ )
742
 
743
  def render_step_1():
744
  """Step 1: Upload Video"""
 
750
 
751
  with col1:
752
  st.markdown("#### YouTube URL")
753
+ st.markdown('<style>div[data-testid="stTextInput"] > label {display: none;}</style>', unsafe_allow_html=True)
754
+ youtube_url = st.text_input("", key="youtube_input", label_visibility="collapsed")
755
 
756
  with col2:
757
  st.markdown("#### Upload Video File")
758
+ st.markdown('<style>div[data-testid="stFileUploader"] > label {display: none;}</style>', unsafe_allow_html=True)
759
+ uploaded_file = st.file_uploader("", type=["mp4", "mov", "avi"], key="video_upload", label_visibility="collapsed")
760
 
761
  # Analyze button
762
  if st.button("🏌️ Start Analysis", key="start_analysis", use_container_width=True):
 
797
  st.markdown('<h2 style="color: #0B3B0B; font-family: Georgia, serif;">Step 2: Analyzing Video and Pose</h2>', unsafe_allow_html=True)
798
 
799
  st.markdown("🔄 **Processing your swing video...**")
 
800
 
801
  video_path = st.session_state.analysis_data.get('video_path')
802
 
 
880
  with col1:
881
  st.markdown("""
882
  <div style="text-align: center; padding: 20px; border: 2px solid #4CAF50; border-radius: 15px; margin: 10px 0;">
883
+ <h3 style="color: #0B3B0B;">🎯 Generate Improvements</h3>
884
  <p>Get personalized swing analysis with specific tips for improvement</p>
885
  </div>
886
  """, unsafe_allow_html=True)
 
892
  with col2:
893
  st.markdown("""
894
  <div style="text-align: center; padding: 20px; border: 2px solid #4CAF50; border-radius: 15px; margin: 10px 0;">
895
+ <h3 style="color: #0B3B0B;">💬 Ask Questions</h3>
896
  <p>Ask specific questions about golf swing technique from our knowledge base</p>
897
  </div>
898
  """, unsafe_allow_html=True)
app/transparent.png DELETED
Binary file (78.2 kB)
 
app/transparent2.png DELETED
Binary file (94.2 kB)