koulsahil commited on
Commit
14e24fa
·
verified ·
1 Parent(s): 1be55a6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +154 -59
app.py CHANGED
@@ -1,74 +1,169 @@
1
  import cv2
2
- import mediapipe as mp
3
  import numpy as np
 
4
  import gradio as gr
 
 
 
5
 
6
- # Initialize Mediapipe Hands
7
  mp_hands = mp.solutions.hands
8
- hands = mp_hands.Hands(min_detection_confidence=0.7, min_tracking_confidence=0.7)
9
-
10
- # Generate simple circle points as target shape
11
- def generate_circle_points(center, radius, num_points=50):
12
- return np.array([
13
- [int(center[0] + radius * np.cos(2 * np.pi * i / num_points)),
14
- int(center[1] + radius * np.sin(2 * np.pi * i / num_points))]
15
- for i in range(num_points)
16
- ])
17
-
18
- target_shape = generate_circle_points(center=(320, 240), radius=100)
19
-
20
- # Calculate shape similarity (simplified)
21
- def compare_shapes(drawn_points, target_points):
22
- if len(drawn_points) < 10:
23
- return 0 # Not enough points to compare
24
- drawn_resampled = np.array(drawn_points[::max(1, len(drawn_points)//50)])
25
- if drawn_resampled.shape[0] != target_points.shape[0]:
26
- return 0
27
- dist = np.linalg.norm(drawn_resampled - target_points, axis=1)
28
- score = max(0, 100 - np.mean(dist) * 0.1) # Scale and clamp score
29
- return round(score, 2)
30
 
31
- # Gradio function
32
- def live_shape_match(video_frame):
33
- global points # Keep trail across frames
34
- h, w, _ = video_frame.shape
35
- rgb_frame = cv2.cvtColor(video_frame, cv2.COLOR_BGR2RGB)
36
- result = hands.process(rgb_frame)
37
 
38
- if result.multi_hand_landmarks:
39
- for hand_landmarks in result.multi_hand_landmarks:
40
- x = int(hand_landmarks.landmark[8].x * w)
41
- y = int(hand_landmarks.landmark[8].y * h)
42
- points.append([x, y])
43
 
44
- # Draw the trail
45
- for i in range(1, len(points)):
46
- cv2.line(video_frame, tuple(points[i - 1]), tuple(points[i]), (0, 255, 0), 3)
 
 
 
 
 
 
 
47
 
48
- # Draw the target shape
49
- for i in range(1, len(target_shape)):
50
- cv2.line(video_frame, tuple(target_shape[i - 1]), tuple(target_shape[i]), (255, 0, 0), 2)
51
 
52
- # Calculate score
53
- score = compare_shapes(points, target_shape)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
 
55
- # Show score
56
- cv2.putText(video_frame, f"Score: {score}", (10, 30),
57
- cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
- return video_frame
 
 
 
 
60
 
61
- # Reset points buffer
62
- points = []
 
63
 
64
- # Build Gradio app
65
- iface = gr.Interface(
66
- fn=live_shape_match,
67
- inputs=gr.Image(source="webcam", streaming=True),
68
- outputs="image",
69
- live=True,
70
- title="Real-Time Shape Drawing Analyzer",
71
- description="Draw a circle in the air with your finger! Watch your score update live."
72
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
- iface.launch()
 
 
 
1
  import cv2
 
2
  import numpy as np
3
+ import mediapipe as mp
4
  import gradio as gr
5
+ import math
6
+ from collections import deque
7
+ import time
8
 
9
+ # Initialize MediaPipe Hand solution
10
  mp_hands = mp.solutions.hands
11
+ mp_drawing = mp.solutions.drawing_utils
12
+ hands = mp_hands.Hands(
13
+ static_image_mode=False,
14
+ max_num_hands=1,
15
+ min_detection_confidence=0.5,
16
+ min_tracking_confidence=0.5
17
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
+ # Initialize deque to store finger path points (with max length to avoid memory issues)
20
+ max_path_length = 100
21
+ finger_path = deque(maxlen=max_path_length)
 
 
 
22
 
23
+ # Flag to start/stop drawing
24
+ is_drawing = False
 
 
 
25
 
26
+ # Perfect shapes for comparison
27
+ def create_perfect_circle(center, radius, num_points=100):
28
+ """Create points for a perfect circle"""
29
+ circle_points = []
30
+ for i in range(num_points):
31
+ angle = 2 * math.pi * i / num_points
32
+ x = int(center[0] + radius * math.cos(angle))
33
+ y = int(center[1] + radius * math.sin(angle))
34
+ circle_points.append((x, y))
35
+ return circle_points
36
 
37
+ # Initial target shape - circle
38
+ target_shape_name = "circle"
39
+ target_shape = None # Will be set based on frame dimensions
40
 
41
+ # Calculate similarity between drawn path and target shape
42
+ def calculate_similarity(path, target_shape):
43
+ """Calculate similarity between drawn path and target shape using shape metrics"""
44
+ if len(path) < 5: # Not enough points to calculate
45
+ return 0
46
+
47
+ # Convert path to numpy array
48
+ path_array = np.array(list(path))
49
+
50
+ # Simple metrics - compare against circle properties
51
+ # 1. Calculate centroid of the drawn shape
52
+ centroid = np.mean(path_array, axis=0)
53
+
54
+ # 2. Calculate distances from centroid to each point
55
+ distances = np.sqrt(np.sum((path_array - centroid)**2, axis=1))
56
+
57
+ # 3. For a perfect circle, the standard deviation of distances should be close to 0
58
+ std_dev = np.std(distances)
59
+ mean_dist = np.mean(distances)
60
+
61
+ # 4. Normalize the standard deviation as a percentage of the mean radius
62
+ if mean_dist > 0:
63
+ # Lower std_dev means more circular
64
+ similarity = max(0, 100 - (std_dev / mean_dist * 100))
65
+ return min(100, similarity) # Cap at 100%
66
+ return 0
67
 
68
+ def process_frame(frame):
69
+ """Process a single frame from the webcam"""
70
+ global finger_path, is_drawing, target_shape
71
+
72
+ # Set target shape if not already set
73
+ if target_shape is None:
74
+ height, width = frame.shape[:2]
75
+ center = (width // 2, height // 2)
76
+ radius = min(width, height) // 4
77
+ target_shape = create_perfect_circle(center, radius)
78
+
79
+ # Convert the BGR image to RGB
80
+ rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
81
+
82
+ # Process the frame with MediaPipe Hands
83
+ results = hands.process(rgb_frame)
84
+
85
+ # Draw target shape (circle for now)
86
+ for point in target_shape:
87
+ cv2.circle(frame, point, 1, (0, 255, 0), -1)
88
+
89
+ # If hands are detected
90
+ if results.multi_hand_landmarks:
91
+ for hand_landmarks in results.multi_hand_landmarks:
92
+ # Draw hand landmarks
93
+ mp_drawing.draw_landmarks(
94
+ frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)
95
+
96
+ # Get index finger tip coordinates
97
+ index_finger_tip = hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_TIP]
98
+ h, w, c = frame.shape
99
+ x, y = int(index_finger_tip.x * w), int(index_finger_tip.y * h)
100
+
101
+ # Check if index finger is raised (simplified)
102
+ index_finger_mcp = hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_MCP]
103
+ if index_finger_tip.y < index_finger_mcp.y: # Index finger is raised
104
+ is_drawing = True
105
+ finger_path.append((x, y))
106
+
107
+ # Draw a filled circle at the finger tip
108
+ cv2.circle(frame, (x, y), 10, (255, 0, 0), -1)
109
+ else:
110
+ is_drawing = False
111
+
112
+ # Draw the finger path
113
+ if len(finger_path) > 1:
114
+ for i in range(1, len(finger_path)):
115
+ cv2.line(frame, finger_path[i-1], finger_path[i], (0, 0, 255), 2)
116
+
117
+ # Calculate and display similarity score
118
+ similarity = calculate_similarity(finger_path, target_shape)
119
+ cv2.putText(
120
+ frame, f"Score: {similarity:.1f}%", (10, 30),
121
+ cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2
122
+ )
123
+
124
+ # Display instructions
125
+ cv2.putText(
126
+ frame, "Draw a circle with your index finger", (10, 70),
127
+ cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2
128
+ )
129
+
130
+ return frame
131
 
132
+ def clear_path():
133
+ """Clear the current drawing path"""
134
+ global finger_path
135
+ finger_path.clear()
136
+ return "Path cleared!"
137
 
138
+ def webcam_feed(video):
139
+ """Process webcam feed and return processed frames"""
140
+ return process_frame(video)
141
 
142
+ # Create Gradio interface
143
+ with gr.Blocks(title="Shape Drawing Game") as demo:
144
+ gr.Markdown("# ✏️ Shape Drawing Game")
145
+ gr.Markdown("Draw shapes in the air using your index finger and see how close you get to the target shape!")
146
+
147
+ with gr.Row():
148
+ webcam = gr.Image(source="webcam", streaming=True, label="Webcam Feed")
149
+ processed = gr.Image(label="Game View")
150
+
151
+ webcam.stream(webcam_feed, webcam, processed)
152
+
153
+ with gr.Row():
154
+ clear_button = gr.Button("Clear Drawing")
155
+
156
+ clear_button.click(fn=clear_path, outputs=gr.Textbox(label="Status"))
157
+
158
+ gr.Markdown("""
159
+ ## How to Play:
160
+ 1. Position your hand in front of the camera
161
+ 2. Raise your index finger to start drawing
162
+ 3. Try to trace the green circle as closely as possible
163
+ 4. See your score update in real-time
164
+ 5. Click 'Clear Drawing' to start over
165
+ """)
166
 
167
+ # Launch the app
168
+ if __name__ == "__main__":
169
+ demo.launch(debug=True)