bsmith3715 commited on
Commit
9da39e0
·
verified ·
1 Parent(s): 82d7dc4

Upload 10 files

Browse files
Files changed (10) hide show
  1. .gitignore +41 -0
  2. Dockerfile +31 -0
  3. README.md +10 -11
  4. app.py +64 -0
  5. download_model.py +21 -0
  6. pilates_evaluator.py +226 -0
  7. pyproject.toml +46 -0
  8. rag_processor.py +61 -0
  9. test_pilates.py +10 -0
  10. uv.lock +0 -0
.gitignore ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Virtual Environment
2
+ .venv/
3
+ venv/
4
+ ENV/
5
+
6
+ # Python
7
+ __pycache__/
8
+ *.py[cod]
9
+ *$py.class
10
+ *.so
11
+ .Python
12
+ build/
13
+ develop-eggs/
14
+ dist/
15
+ downloads/
16
+ eggs/
17
+ .eggs/
18
+ lib/
19
+ lib64/
20
+ parts/
21
+ sdist/
22
+ var/
23
+ wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+
28
+ # IDE
29
+ .idea/
30
+ .vscode/
31
+ *.swp
32
+ *.swo
33
+
34
+ # OS
35
+ .DS_Store
36
+ .DS_Store?
37
+ ._*
38
+ .Spotlight-V100
39
+ .Trashes
40
+ ehthumbs.db
41
+ Thumbs.db
Dockerfile ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Get a distribution that has uv already installed
2
+ FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim
3
+
4
+ # Add user - this is the user that will run the app
5
+ # If you do not set user, the app will run as root (undesirable)
6
+ RUN useradd -m -u 1000 user
7
+ USER user
8
+
9
+ # Set the home directory and path
10
+ ENV HOME=/home/user \
11
+ PATH=/home/user/.local/bin:$PATH
12
+
13
+ ENV UVICORN_WS_PROTOCOL=websockets
14
+
15
+
16
+ # Set the working directory
17
+ WORKDIR $HOME/app
18
+
19
+ # Copy the app to the container
20
+ COPY --chown=user . $HOME/app
21
+
22
+ # Install the dependencies
23
+ # RUN uv sync --frozen
24
+ RUN uv sync
25
+
26
+ # Expose the port
27
+ EXPOSE 7860
28
+
29
+ # Run the app
30
+ CMD ["uv", "run", "chainlit", "run", "app.py", "--host", "0.0.0.0", "--port", "7860"]
31
+
README.md CHANGED
@@ -1,11 +1,10 @@
1
- ---
2
- title: DEMO COMBINED
3
- emoji: 🐨
4
- colorFrom: blue
5
- colorTo: purple
6
- sdk: docker
7
- pinned: false
8
- license: mit
9
- ---
10
-
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
+ ---
2
+ title: Project 2
3
+ emoji: 📈
4
+ colorFrom: pink
5
+ colorTo: pink
6
+ sdk: docker
7
+ pinned: false
8
+ ---
9
+
10
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
app.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import chainlit as cl
2
+ from pilates_evaluator import PilatesVideoEvaluator
3
+ from rag_processor import RAGProcessor
4
+ import os
5
+ from dotenv import load_dotenv
6
+
7
+ load_dotenv()
8
+
9
+ # Initialize RAG processor
10
+ rag_processor = RAGProcessor()
11
+
12
+ # Sample Pilates knowledge base - you can replace this with your own documents
13
+ PILATES_KNOWLEDGE = [
14
+ "The Hundred is a fundamental Pilates exercise that helps build core strength and breathing control.",
15
+ "Proper alignment is crucial in Pilates - maintain neutral spine position during exercises.",
16
+ "Pilates focuses on six principles: concentration, control, centering, flow, precision, and breathing.",
17
+ "The Pilates reformer is a versatile piece of equipment that provides resistance through springs.",
18
+ "Pilates exercises should be performed with controlled movements and proper breathing patterns.",
19
+ "The Pilates mat work series includes exercises like the Roll Up, Single Leg Stretch, and Double Leg Stretch.",
20
+ "Pilates helps improve posture, flexibility, and overall body awareness.",
21
+ "Joseph Pilates developed the method as a system of exercises to strengthen the mind and body.",
22
+ "Pilates exercises can be modified for different fitness levels and physical conditions.",
23
+ "Regular Pilates practice can help prevent injuries and improve athletic performance."
24
+ ]
25
+
26
+ # Add documents to RAG system
27
+ rag_processor.add_documents(PILATES_KNOWLEDGE)
28
+
29
+ @cl.on_chat_start
30
+ async def start():
31
+ await cl.Message(content="Welcome! You can chat with me about Pilates or upload a video to analyze your Pilates exercise.").send()
32
+
33
+ @cl.on_message
34
+ async def main(message: cl.Message):
35
+ # Handle video uploads
36
+ if message.elements:
37
+ video_file = message.elements[0]
38
+ if not video_file.name.endswith(('.mp4', '.avi', '.mov')):
39
+ await cl.Message(content="Please upload a valid video file (mp4, avi, mov).").send()
40
+ return
41
+
42
+ await cl.Message(content=f"Analyzing video: {video_file.name}...").send()
43
+
44
+ evaluator = PilatesVideoEvaluator()
45
+ try:
46
+ evaluator.process_video(video_file.path)
47
+ report_path = "pilates_evaluation_report.json"
48
+ evaluator.generate_report(report_path)
49
+ await cl.Message(content=f"Analysis complete! Report saved to {report_path}.").send()
50
+ except Exception as e:
51
+ await cl.Message(content=f"Error analyzing video: {e}").send()
52
+ return
53
+
54
+ # Handle chat messages
55
+ if message.content:
56
+ try:
57
+ # Generate response using RAG system
58
+ response = rag_processor.generate_response(message.content)
59
+ await cl.Message(content=response).send()
60
+ except Exception as e:
61
+ await cl.Message(content=f"Error processing your message: {e}").send()
62
+ return
63
+
64
+ await cl.Message(content="Please either send a message or upload a video file.").send()
download_model.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import urllib.request
2
+ import os
3
+
4
+ def download_model():
5
+ model_url = "https://github.com/CMU-Perceptual-Computing-Lab/openpose/raw/master/models/pose/coco/pose_iter_440000.caffemodel"
6
+ proto_url = "https://raw.githubusercontent.com/CMU-Perceptual-Computing-Lab/openpose/master/models/pose/coco/pose_deploy_linevec.prototxt"
7
+
8
+ print("Downloading pose estimation model...")
9
+
10
+ # Download the model file
11
+ urllib.request.urlretrieve(model_url, "pose_model.caffemodel")
12
+ print("Downloaded pose model")
13
+
14
+ # Download the prototxt file
15
+ urllib.request.urlretrieve(proto_url, "pose_deploy_linevec.prototxt")
16
+ print("Downloaded prototxt file")
17
+
18
+ print("Model files downloaded successfully!")
19
+
20
+ if __name__ == "__main__":
21
+ download_model()
pilates_evaluator.py ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+ import json
4
+ from datetime import datetime
5
+ import matplotlib.pyplot as plt
6
+ from pathlib import Path
7
+ import os
8
+ import urllib.request
9
+
10
+ class PilatesVideoEvaluator:
11
+ def __init__(self):
12
+ # Initialize OpenCV pose detection
13
+ self.BODY_PARTS = {
14
+ "Neck": 0, "RShoulder": 1, "RElbow": 2, "RWrist": 3,
15
+ "LShoulder": 4, "LElbow": 5, "LWrist": 6, "RHip": 7, "RKnee": 8,
16
+ "RAnkle": 9, "LHip": 10, "LKnee": 11, "LAnkle": 12
17
+ }
18
+
19
+ # Download the model if it doesn't exist
20
+ if not os.path.exists('pose_model.caffemodel') or not os.path.exists('pose_deploy.prototxt'):
21
+ print("Downloading pose estimation model...")
22
+ model_url = "https://github.com/opencv/opencv_3rdparty/raw/dnn_samples_face_detector_20170830/res10_300x300_ssd_iter_140000.caffemodel"
23
+ proto_url = "https://raw.githubusercontent.com/opencv/opencv/master/samples/dnn/face_detector/deploy.prototxt"
24
+
25
+ urllib.request.urlretrieve(model_url, "pose_model.caffemodel")
26
+ urllib.request.urlretrieve(proto_url, "pose_deploy.prototxt")
27
+ print("Model downloaded successfully!")
28
+
29
+ # Load the model
30
+ self.net = cv2.dnn.readNetFromCaffe("pose_deploy.prototxt", "pose_model.caffemodel")
31
+
32
+ # Evaluation metrics
33
+ self.metrics = {
34
+ 'total_frames': 0,
35
+ 'pose_detected_frames': 0,
36
+ 'movement_consistency': [],
37
+ 'balance_scores': [],
38
+ 'posture_alignment': [],
39
+ 'video_quality_score': 0,
40
+ 'exercise_duration': 0,
41
+ 'detected_exercises': []
42
+ }
43
+
44
+ def analyze_posture(self, frame):
45
+ """Analyze posture using OpenCV pose estimation"""
46
+ height, width = frame.shape[:2]
47
+ blob = cv2.dnn.blobFromImage(frame, 1.0/255, (368, 368), (0, 0, 0), swapRB=False, crop=False)
48
+ self.net.setInput(blob)
49
+ output = self.net.forward()
50
+
51
+ # Process the output to get keypoints
52
+ points = []
53
+ for i in range(len(self.BODY_PARTS)):
54
+ # Confidence map for the current keypoint
55
+ probMap = output[0, i, :, :]
56
+ probMap = cv2.resize(probMap, (width, height))
57
+
58
+ # Find global maxima of the probMap
59
+ minVal, prob, minLoc, point = cv2.minMaxLoc(probMap)
60
+
61
+ if prob > 0.1: # Confidence threshold
62
+ points.append((int(point[0]), int(point[1])))
63
+ else:
64
+ points.append(None)
65
+
66
+ return points
67
+
68
+ def detect_exercise_type(self, points):
69
+ """Detect exercise type based on keypoint positions"""
70
+ if not points or len(points) < 18:
71
+ return "Unknown"
72
+
73
+ # Example: Detect plank position
74
+ if (points[self.BODY_PARTS["RShoulder"]] and points[self.BODY_PARTS["RElbow"]] and
75
+ points[self.BODY_PARTS["LShoulder"]] and points[self.BODY_PARTS["LElbow"]]):
76
+
77
+ r_shoulder = points[self.BODY_PARTS["RShoulder"]]
78
+ r_elbow = points[self.BODY_PARTS["RElbow"]]
79
+ l_shoulder = points[self.BODY_PARTS["LShoulder"]]
80
+ l_elbow = points[self.BODY_PARTS["LElbow"]]
81
+
82
+ # Check if arms are straight (plank position)
83
+ r_arm_angle = self.calculate_angle(r_shoulder, r_elbow)
84
+ l_arm_angle = self.calculate_angle(l_shoulder, l_elbow)
85
+
86
+ if 150 < r_arm_angle < 180 and 150 < l_arm_angle < 180:
87
+ return "Plank"
88
+
89
+ return "Unknown"
90
+
91
+ def calculate_angle(self, point1, point2):
92
+ """Calculate angle between two points"""
93
+ if not point1 or not point2:
94
+ return 0
95
+ return np.degrees(np.arctan2(point2[1] - point1[1], point2[0] - point1[0]))
96
+
97
+ def process_video(self, video_path):
98
+ """Process video and analyze exercises"""
99
+ cap = cv2.VideoCapture(video_path)
100
+ if not cap.isOpened():
101
+ raise ValueError("Could not open video file")
102
+
103
+ while cap.isOpened():
104
+ ret, frame = cap.read()
105
+ if not ret:
106
+ break
107
+
108
+ self.metrics['total_frames'] += 1
109
+
110
+ # Analyze posture
111
+ points = self.analyze_posture(frame)
112
+ if points:
113
+ self.metrics['pose_detected_frames'] += 1
114
+
115
+ # Detect exercise type
116
+ exercise_type = self.detect_exercise_type(points)
117
+ if exercise_type != "Unknown":
118
+ self.metrics['detected_exercises'].append(exercise_type)
119
+
120
+ # Calculate metrics
121
+ self.metrics['movement_consistency'].append(self.calculate_movement_consistency(points))
122
+ self.metrics['balance_scores'].append(self.calculate_balance_score(points))
123
+ self.metrics['posture_alignment'].append(self.calculate_posture_alignment(points))
124
+
125
+ cap.release()
126
+ self.calculate_final_metrics()
127
+
128
+ def calculate_movement_consistency(self, points):
129
+ """Calculate movement consistency score"""
130
+ # Implement movement consistency calculation
131
+ return 0.8 # Placeholder
132
+
133
+ def calculate_balance_score(self, points):
134
+ """Calculate balance score"""
135
+ # Implement balance score calculation
136
+ return 0.7 # Placeholder
137
+
138
+ def calculate_posture_alignment(self, points):
139
+ """Calculate posture alignment score"""
140
+ # Implement posture alignment calculation
141
+ return 0.9 # Placeholder
142
+
143
+ def calculate_final_metrics(self):
144
+ """Calculate final metrics"""
145
+ if self.metrics['total_frames'] > 0:
146
+ self.metrics['video_quality_score'] = (
147
+ self.metrics['pose_detected_frames'] / self.metrics['total_frames']
148
+ ) * 100
149
+
150
+ def generate_report(self, output_path):
151
+ """Generate evaluation report"""
152
+ report = {
153
+ 'timestamp': datetime.now().isoformat(),
154
+ 'metrics': self.metrics,
155
+ 'summary': {
156
+ 'video_quality': f"{self.metrics['video_quality_score']:.2f}%",
157
+ 'detected_exercises': list(set(self.metrics['detected_exercises'])),
158
+ 'average_movement_consistency': np.mean(self.metrics['movement_consistency']),
159
+ 'average_balance_score': np.mean(self.metrics['balance_scores']),
160
+ 'average_posture_alignment': np.mean(self.metrics['posture_alignment'])
161
+ }
162
+ }
163
+
164
+ with open(output_path, 'w') as f:
165
+ json.dump(report, f, indent=4)
166
+
167
+ def visualize_results(self, output_path):
168
+ """Visualize evaluation results"""
169
+ plt.figure(figsize=(12, 8))
170
+
171
+ # Plot metrics over time
172
+ plt.subplot(2, 2, 1)
173
+ plt.plot(self.metrics['movement_consistency'], label='Movement Consistency')
174
+ plt.title('Movement Consistency Over Time')
175
+ plt.legend()
176
+
177
+ plt.subplot(2, 2, 2)
178
+ plt.plot(self.metrics['balance_scores'], label='Balance Score')
179
+ plt.title('Balance Score Over Time')
180
+ plt.legend()
181
+
182
+ plt.subplot(2, 2, 3)
183
+ plt.plot(self.metrics['posture_alignment'], label='Posture Alignment')
184
+ plt.title('Posture Alignment Over Time')
185
+ plt.legend()
186
+
187
+ plt.tight_layout()
188
+ plt.savefig(output_path)
189
+ plt.close()
190
+
191
+ def main():
192
+ """Example usage of the Pilates Video Evaluator"""
193
+ evaluator = PilatesVideoEvaluator()
194
+
195
+ # Replace with your video path
196
+ video_path = "pilates_workout.mp4"
197
+ output_video_path = "analyzed_pilates_workout.mp4"
198
+ report_path = "pilates_evaluation_report.json"
199
+
200
+ try:
201
+ # Process the video
202
+ print("Starting video analysis...")
203
+ evaluator.process_video(video_path)
204
+
205
+ # Print report
206
+ print("\n" + "="*50)
207
+ print("PILATES VIDEO EVALUATION REPORT")
208
+ print("="*50)
209
+
210
+ print(f"Video Quality: {evaluator.metrics['video_quality_score']:.2f}%")
211
+ print(f"Detected Exercises: {', '.join(evaluator.metrics['detected_exercises'])}")
212
+ print(f"Average Movement Consistency: {evaluator.metrics['average_movement_consistency']:.2f}")
213
+ print(f"Average Balance Score: {evaluator.metrics['average_balance_score']:.2f}")
214
+ print(f"Average Posture Alignment: {evaluator.metrics['average_posture_alignment']:.2f}")
215
+
216
+ # Save report and visualization
217
+ evaluator.generate_report(report_path)
218
+ evaluator.visualize_results(output_video_path)
219
+
220
+ except Exception as e:
221
+ print(f"Error processing video: {e}")
222
+ print("Make sure you have the required dependencies installed:")
223
+ print("pip install opencv-python numpy matplotlib")
224
+
225
+ if __name__ == "__main__":
226
+ main()
pyproject.toml ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "Certification_Challenge"
3
+ version = "0.1.0"
4
+ description = "Add your description here"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12,<3.13"
7
+ dependencies = [
8
+ "accelerate>=1.3.0",
9
+ "arxiv>=2.2.0",
10
+ "beautifulsoup4>=4.13.3",
11
+ "chainlit>=2.5.5",
12
+ "datasets>=3.6.0",
13
+ "faiss-cpu>=1.11.0",
14
+ "ipykernel>=6.29.5",
15
+ "ipywidgets>=8.1.5",
16
+ "langchain>=0.3.25",
17
+ "langchain-cohere>=0.4.4",
18
+ "langchain-community>=0.3.24",
19
+ "langchain-core>=0.3.34",
20
+ "langchain-huggingface>=0.1.2",
21
+ "langchain-openai>=0.3.16",
22
+ "langchain-qdrant>=0.2.0",
23
+ "langchain-text-splitters>=0.3.8",
24
+ "langgraph>=0.4.3",
25
+ "libmagic>=1.0",
26
+ "lxml>=5.3.1",
27
+ "nltk==3.9.1",
28
+ "pyarrow>=20.0.0",
29
+ "pymupdf>=1.25.5",
30
+ "python-pptx==1.0.2",
31
+ "ragas>=0.2.15",
32
+ "sentence-transformers>=3.4.1",
33
+ "transformers[torch]>=4.48.3",
34
+ "unstructured>=0.17.2",
35
+ "wandb>=0.19.6",
36
+ "websockets==11.0.3",
37
+ "requests>=2.32.0",
38
+ "openai",
39
+ "torch",
40
+ "opencv-python==4.9.0.80",
41
+ "scikit-image==0.22.0",
42
+ "matplotlib==3.8.4",
43
+ "Pillow==10.3.0",
44
+ "numpy==1.26.4"
45
+ ]
46
+
rag_processor.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from typing import List
3
+ import openai
4
+ from sentence_transformers import SentenceTransformer
5
+ import faiss
6
+ import numpy as np
7
+ from dotenv import load_dotenv
8
+
9
+ load_dotenv()
10
+
11
+ class RAGProcessor:
12
+ def __init__(self, model_name: str = "bsmith3715/legal-ft-demo_final"):
13
+ self.model = SentenceTransformer(model_name)
14
+ self.index = None
15
+ self.documents = []
16
+ self.openai_client = openai.OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
17
+
18
+ def add_documents(self, documents: List[str]):
19
+ """Add documents to the RAG system."""
20
+ self.documents = documents
21
+ embeddings = self.model.encode(documents)
22
+
23
+ # Create FAISS index
24
+ dimension = embeddings.shape[1]
25
+ self.index = faiss.IndexFlatL2(dimension)
26
+ self.index.add(embeddings.astype('float32'))
27
+
28
+ def retrieve_relevant_context(self, query: str, k: int = 3) -> List[str]:
29
+ """Retrieve relevant documents for a given query."""
30
+ if not self.index:
31
+ return []
32
+
33
+ query_embedding = self.model.encode([query])
34
+ distances, indices = self.index.search(query_embedding.astype('float32'), k)
35
+
36
+ return [self.documents[i] for i in indices[0]]
37
+
38
+ def generate_response(self, query: str) -> str:
39
+ """Generate a response using OpenAI API with retrieved context."""
40
+ relevant_docs = self.retrieve_relevant_context(query)
41
+ context = "\n".join(relevant_docs)
42
+
43
+ prompt = f"""Context information is below.
44
+ ---------------------
45
+ {context}
46
+ ---------------------
47
+ Given the context information, please answer the following question. If the context doesn't contain relevant information, say so.
48
+ Question: {query}
49
+ Answer:"""
50
+
51
+ response = self.openai_client.chat.completions.create(
52
+ model="gpt-3.5-turbo",
53
+ messages=[
54
+ {"role": "system", "content": "You are a helpful Pilates instructor assistant. Use the provided context to answer questions accurately."},
55
+ {"role": "user", "content": prompt}
56
+ ],
57
+ temperature=0.7,
58
+ max_tokens=500
59
+ )
60
+
61
+ return response.choices[0].message.content
test_pilates.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ from pilates_evaluator import PilatesVideoEvaluator
2
+
3
+ def test_evaluator():
4
+ evaluator = PilatesVideoEvaluator()
5
+ print("PilatesVideoEvaluator initialized successfully!")
6
+ print("Body parts:", evaluator.BODY_PARTS)
7
+ print("Model loaded:", evaluator.net is not None)
8
+
9
+ if __name__ == "__main__":
10
+ test_evaluator()
uv.lock ADDED
The diff for this file is too large to render. See raw diff