Spaces:
Paused
Paused
Remove annotated video and key frame analysis features from UI
Browse files- Remove annotated video option, button, and click handler from Streamlit interface
- Remove key frame analysis option, button, and click handler from Streamlit interface
- Adjust layout from 4 columns to 2 columns for remaining options
- Keep all backend logic intact for both features
- Interface now shows only: Improvement Recommendations and Golf Swing Chatbot
- app/streamlit_app.py +6 -173
app/streamlit_app.py
CHANGED
|
@@ -768,26 +768,16 @@ def main():
|
|
| 768 |
|
| 769 |
# Present the options after analysis
|
| 770 |
st.subheader("What would you like to do next?")
|
| 771 |
-
options_col1, options_col2
|
| 772 |
|
| 773 |
with options_col1:
|
| 774 |
st.info(
|
| 775 |
-
"**Option 1: Generate
|
| 776 |
-
)
|
| 777 |
-
|
| 778 |
-
with options_col2:
|
| 779 |
-
st.info(
|
| 780 |
-
"**Option 2: Generate Improvement Recommendations**\n\nGet AI-powered analysis of your swing with specific tips for improvement."
|
| 781 |
)
|
| 782 |
|
| 783 |
-
with
|
| 784 |
-
st.info(
|
| 785 |
-
"**Option 3: Key Frame Analysis**\n\nExtract and review your setup, top of backswing, and impact frames with helpful comments for each phase."
|
| 786 |
-
)
|
| 787 |
-
|
| 788 |
-
with options_col4:
|
| 789 |
st.info(
|
| 790 |
-
"**Option
|
| 791 |
)
|
| 792 |
|
| 793 |
except Exception as e:
|
|
@@ -806,76 +796,18 @@ def main():
|
|
| 806 |
language="text")
|
| 807 |
|
| 808 |
# Create columns for the action buttons
|
| 809 |
-
button_col1, button_col2
|
| 810 |
|
| 811 |
with button_col1:
|
| 812 |
-
annotated_video_clicked = st.button("Generate Annotated Video",
|
| 813 |
-
key="create_annotated",
|
| 814 |
-
use_container_width=True)
|
| 815 |
-
|
| 816 |
-
with button_col2:
|
| 817 |
improvements_clicked = st.button("Generate Improvements",
|
| 818 |
key="gpt_recommendations",
|
| 819 |
use_container_width=True)
|
| 820 |
|
| 821 |
-
with
|
| 822 |
-
keyframe_analysis_clicked = st.button("Key Frame Analysis",
|
| 823 |
-
key="keyframe_analysis",
|
| 824 |
-
use_container_width=True)
|
| 825 |
-
|
| 826 |
-
with button_col4:
|
| 827 |
chatbot_clicked = st.button("Golf Swing Chatbot",
|
| 828 |
key="rag_chatbot",
|
| 829 |
use_container_width=True)
|
| 830 |
|
| 831 |
-
# Handle annotated video creation
|
| 832 |
-
if annotated_video_clicked:
|
| 833 |
-
# Reset chatbot state when other buttons are clicked
|
| 834 |
-
st.session_state.show_chatbot = False
|
| 835 |
-
try:
|
| 836 |
-
with st.spinner("Creating annotated video..."):
|
| 837 |
-
# Create downloads directory if it doesn't exist
|
| 838 |
-
os.makedirs("downloads", exist_ok=True)
|
| 839 |
-
|
| 840 |
-
# Get data from session state
|
| 841 |
-
data = st.session_state.analysis_data
|
| 842 |
-
|
| 843 |
-
# Create the annotated video
|
| 844 |
-
output_path = create_annotated_video(
|
| 845 |
-
data['video_path'],
|
| 846 |
-
data['frames'],
|
| 847 |
-
data['detections'],
|
| 848 |
-
data['pose_data'],
|
| 849 |
-
data['swing_phases'],
|
| 850 |
-
data['trajectory_data'],
|
| 851 |
-
sample_rate=data['sample_rate'])
|
| 852 |
-
|
| 853 |
-
# Verify the file exists
|
| 854 |
-
if not os.path.exists(output_path):
|
| 855 |
-
raise FileNotFoundError(
|
| 856 |
-
f"Annotated video file not found at {output_path}")
|
| 857 |
-
|
| 858 |
-
# Store the annotated video path in session state
|
| 859 |
-
st.session_state.annotated_video_path = output_path
|
| 860 |
-
|
| 861 |
-
# Display success message and video after spinner completes
|
| 862 |
-
st.success("Annotated video created successfully!")
|
| 863 |
-
display_video(output_path, width=400)
|
| 864 |
-
|
| 865 |
-
# Show download button
|
| 866 |
-
with open(output_path, "rb") as file:
|
| 867 |
-
video_bytes = file.read()
|
| 868 |
-
st.download_button(label="Download Annotated Video",
|
| 869 |
-
data=video_bytes,
|
| 870 |
-
file_name=os.path.basename(output_path),
|
| 871 |
-
mime="video/mp4")
|
| 872 |
-
|
| 873 |
-
except Exception as e:
|
| 874 |
-
st.error(f"Error creating annotated video: {str(e)}")
|
| 875 |
-
st.error(
|
| 876 |
-
"Please check if the downloads directory exists and is writable"
|
| 877 |
-
)
|
| 878 |
-
|
| 879 |
# Handle improvement recommendations generation
|
| 880 |
if improvements_clicked:
|
| 881 |
# Reset chatbot state when other buttons are clicked
|
|
@@ -923,105 +855,6 @@ def main():
|
|
| 923 |
# Show error message if analysis failed
|
| 924 |
st.error(analysis)
|
| 925 |
|
| 926 |
-
# Handle key frame analysis (new tab/option)
|
| 927 |
-
if keyframe_analysis_clicked:
|
| 928 |
-
# Reset chatbot state when other buttons are clicked
|
| 929 |
-
st.session_state.show_chatbot = False
|
| 930 |
-
try:
|
| 931 |
-
with st.spinner("Extracting key frames from your swing..."):
|
| 932 |
-
user_video_path = st.session_state.analysis_data['video_path']
|
| 933 |
-
user_swing_phases = st.session_state.analysis_data['swing_phases']
|
| 934 |
-
frames = st.session_state.analysis_data['frames']
|
| 935 |
-
key_frames = extract_key_swing_frames(user_video_path, frames, user_swing_phases)
|
| 936 |
-
|
| 937 |
-
st.success("Key frame analysis complete!")
|
| 938 |
-
st.subheader("Key Frame Analysis: Your Swing's Critical Positions")
|
| 939 |
-
|
| 940 |
-
# Define helpful comments for each phase
|
| 941 |
-
phase_comments = {
|
| 942 |
-
'setup': [
|
| 943 |
-
"Balanced stance with feet shoulder-width apart.",
|
| 944 |
-
"Even weight distribution on both feet.",
|
| 945 |
-
"Neutral grip with hands in proper position.",
|
| 946 |
-
"Athletic posture with slight forward bend.",
|
| 947 |
-
"Ball positioned correctly for club selection."
|
| 948 |
-
],
|
| 949 |
-
'backswing': [
|
| 950 |
-
"Full shoulder rotation with stable lower body.",
|
| 951 |
-
"Club on proper swing plane at top.",
|
| 952 |
-
"Consistent spine angle throughout.",
|
| 953 |
-
"Minimal weight shift to right side."
|
| 954 |
-
],
|
| 955 |
-
'impact': [
|
| 956 |
-
"Weight shifted to front foot (70-80%).",
|
| 957 |
-
"Hands ahead of ball at impact.",
|
| 958 |
-
"Square club face to target line.",
|
| 959 |
-
"Head behind ball with steady position.",
|
| 960 |
-
"Hips and shoulders aligned to target."
|
| 961 |
-
]
|
| 962 |
-
}
|
| 963 |
-
phase_titles = {
|
| 964 |
-
'setup': 'Starting Position',
|
| 965 |
-
'backswing': 'Top of Backswing',
|
| 966 |
-
'impact': 'Impact with Ball'
|
| 967 |
-
}
|
| 968 |
-
phases = ['setup', 'backswing', 'impact']
|
| 969 |
-
for phase in phases:
|
| 970 |
-
st.subheader(f"{phase_titles[phase]}")
|
| 971 |
-
img_col, comment_col = st.columns([1, 1])
|
| 972 |
-
with img_col:
|
| 973 |
-
if key_frames.get(phase) is not None:
|
| 974 |
-
frame = key_frames[phase]
|
| 975 |
-
|
| 976 |
-
# Verify frame is in color before conversion
|
| 977 |
-
if len(frame.shape) == 3 and frame.shape[2] == 3:
|
| 978 |
-
try:
|
| 979 |
-
# Save frame to temp file for display
|
| 980 |
-
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.jpg')
|
| 981 |
-
|
| 982 |
-
# Convert BGR (OpenCV) to RGB (PIL) format
|
| 983 |
-
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
| 984 |
-
|
| 985 |
-
# Debug: Log frame dimensions after extraction and color conversion
|
| 986 |
-
height, width = rgb_frame.shape[:2]
|
| 987 |
-
print(f"Frame dimensions for {phase}: {width}x{height}")
|
| 988 |
-
|
| 989 |
-
# Resize frame proportionally for better display
|
| 990 |
-
# Target width of 400 pixels while maintaining aspect ratio
|
| 991 |
-
target_width = 400
|
| 992 |
-
aspect_ratio = height / width
|
| 993 |
-
target_height = int(target_width * aspect_ratio)
|
| 994 |
-
|
| 995 |
-
pil_img = Image.fromarray(rgb_frame)
|
| 996 |
-
# Resize the image proportionally
|
| 997 |
-
pil_img = pil_img.resize((target_width, target_height), Image.Resampling.LANCZOS)
|
| 998 |
-
pil_img.save(temp_file.name, format="JPEG", quality=95)
|
| 999 |
-
|
| 1000 |
-
# Display the image with fixed width
|
| 1001 |
-
st.image(temp_file.name, width=target_width)
|
| 1002 |
-
|
| 1003 |
-
# Clean up temp file
|
| 1004 |
-
try:
|
| 1005 |
-
os.unlink(temp_file.name)
|
| 1006 |
-
except:
|
| 1007 |
-
pass # Ignore cleanup errors
|
| 1008 |
-
|
| 1009 |
-
except Exception as e:
|
| 1010 |
-
st.error(f"Error displaying {phase} frame: {str(e)}")
|
| 1011 |
-
st.warning("Frame could not be displayed properly.")
|
| 1012 |
-
else:
|
| 1013 |
-
st.warning(f"Frame for {phase} is not in color format. Shape: {frame.shape}")
|
| 1014 |
-
else:
|
| 1015 |
-
st.warning("Frame not found.")
|
| 1016 |
-
with comment_col:
|
| 1017 |
-
st.markdown("**Comments:**")
|
| 1018 |
-
for comment in phase_comments[phase]:
|
| 1019 |
-
st.markdown(f"- {comment}")
|
| 1020 |
-
st.markdown("---")
|
| 1021 |
-
except Exception as e:
|
| 1022 |
-
st.error(f"Error during key frame analysis: {str(e)}")
|
| 1023 |
-
st.info("Please ensure your video is in a supported format and try again.")
|
| 1024 |
-
|
| 1025 |
# Handle RAG chatbot
|
| 1026 |
if chatbot_clicked:
|
| 1027 |
st.session_state.show_chatbot = True
|
|
|
|
| 768 |
|
| 769 |
# Present the options after analysis
|
| 770 |
st.subheader("What would you like to do next?")
|
| 771 |
+
options_col1, options_col2 = st.columns(2)
|
| 772 |
|
| 773 |
with options_col1:
|
| 774 |
st.info(
|
| 775 |
+
"**Option 1: Generate Improvement Recommendations**\n\nGet AI-powered analysis of your swing with specific tips for improvement."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 776 |
)
|
| 777 |
|
| 778 |
+
with options_col2:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 779 |
st.info(
|
| 780 |
+
"**Option 2: Golf Swing Chatbot**\n\nAsk specific questions about golf swing technique and get expert advice from our knowledge base."
|
| 781 |
)
|
| 782 |
|
| 783 |
except Exception as e:
|
|
|
|
| 796 |
language="text")
|
| 797 |
|
| 798 |
# Create columns for the action buttons
|
| 799 |
+
button_col1, button_col2 = st.columns(2)
|
| 800 |
|
| 801 |
with button_col1:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 802 |
improvements_clicked = st.button("Generate Improvements",
|
| 803 |
key="gpt_recommendations",
|
| 804 |
use_container_width=True)
|
| 805 |
|
| 806 |
+
with button_col2:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 807 |
chatbot_clicked = st.button("Golf Swing Chatbot",
|
| 808 |
key="rag_chatbot",
|
| 809 |
use_container_width=True)
|
| 810 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 811 |
# Handle improvement recommendations generation
|
| 812 |
if improvements_clicked:
|
| 813 |
# Reset chatbot state when other buttons are clicked
|
|
|
|
| 855 |
# Show error message if analysis failed
|
| 856 |
st.error(analysis)
|
| 857 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 858 |
# Handle RAG chatbot
|
| 859 |
if chatbot_clicked:
|
| 860 |
st.session_state.show_chatbot = True
|