Spaces:
Runtime error
Runtime error
File size: 6,923 Bytes
c43fe87 14e24fa c43fe87 14e24fa c43fe87 14e24fa c43fe87 14e24fa c43fe87 14e24fa c43fe87 14e24fa c43fe87 14e24fa c43fe87 14e24fa c43fe87 14e24fa c43fe87 14e24fa fe74d40 14e24fa c43fe87 14e24fa c43fe87 fe74d40 c43fe87 14e24fa fe74d40 14e24fa fe74d40 14e24fa fe74d40 14e24fa fe74d40 14e24fa fe74d40 14e24fa c43fe87 fe74d40 14e24fa fe74d40 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
import cv2
import numpy as np
import mediapipe as mp
import gradio as gr
import math
from collections import deque
import time
# Initialize MediaPipe Hand solution
mp_hands = mp.solutions.hands
mp_drawing = mp.solutions.drawing_utils
hands = mp_hands.Hands(
static_image_mode=False,
max_num_hands=1,
min_detection_confidence=0.5,
min_tracking_confidence=0.5
)
# Initialize deque to store finger path points (with max length to avoid memory issues)
max_path_length = 100
finger_path = deque(maxlen=max_path_length)
# Flag to start/stop drawing
is_drawing = False
# Perfect shapes for comparison
def create_perfect_circle(center, radius, num_points=100):
"""Create points for a perfect circle"""
circle_points = []
for i in range(num_points):
angle = 2 * math.pi * i / num_points
x = int(center[0] + radius * math.cos(angle))
y = int(center[1] + radius * math.sin(angle))
circle_points.append((x, y))
return circle_points
# Initial target shape - circle
target_shape_name = "circle"
target_shape = None # Will be set based on frame dimensions
# Calculate similarity between drawn path and target shape
def calculate_similarity(path, target_shape):
"""Calculate similarity between drawn path and target shape using shape metrics"""
if len(path) < 5: # Not enough points to calculate
return 0
# Convert path to numpy array
path_array = np.array(list(path))
# Simple metrics - compare against circle properties
# 1. Calculate centroid of the drawn shape
centroid = np.mean(path_array, axis=0)
# 2. Calculate distances from centroid to each point
distances = np.sqrt(np.sum((path_array - centroid)**2, axis=1))
# 3. For a perfect circle, the standard deviation of distances should be close to 0
std_dev = np.std(distances)
mean_dist = np.mean(distances)
# 4. Normalize the standard deviation as a percentage of the mean radius
if mean_dist > 0:
# Lower std_dev means more circular
similarity = max(0, 100 - (std_dev / mean_dist * 100))
return min(100, similarity) # Cap at 100%
return 0
def process_frame(frame):
"""Process a single frame from the webcam"""
global finger_path, is_drawing, target_shape
if frame is None:
# Return a blank frame if input is None
return np.zeros((480, 640, 3), dtype=np.uint8)
# Set target shape if not already set
if target_shape is None:
height, width = frame.shape[:2]
center = (width // 2, height // 2)
radius = min(width, height) // 4
target_shape = create_perfect_circle(center, radius)
# Convert the BGR image to RGB
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# Process the frame with MediaPipe Hands
results = hands.process(rgb_frame)
# Draw target shape (circle for now)
for point in target_shape:
cv2.circle(frame, point, 1, (0, 255, 0), -1)
# If hands are detected
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
# Draw hand landmarks
mp_drawing.draw_landmarks(
frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)
# Get index finger tip coordinates
index_finger_tip = hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_TIP]
h, w, c = frame.shape
x, y = int(index_finger_tip.x * w), int(index_finger_tip.y * h)
# Check if index finger is raised (simplified)
index_finger_mcp = hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_MCP]
if index_finger_tip.y < index_finger_mcp.y: # Index finger is raised
is_drawing = True
finger_path.append((x, y))
# Draw a filled circle at the finger tip
cv2.circle(frame, (x, y), 10, (255, 0, 0), -1)
else:
is_drawing = False
# Draw the finger path
if len(finger_path) > 1:
for i in range(1, len(finger_path)):
cv2.line(frame, finger_path[i-1], finger_path[i], (0, 0, 255), 2)
# Calculate and display similarity score
similarity = calculate_similarity(finger_path, target_shape)
cv2.putText(
frame, f"Score: {similarity:.1f}%", (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2
)
# Display instructions
cv2.putText(
frame, "Draw a circle with your index finger", (10, 70),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2
)
return frame
def clear_path():
"""Clear the current drawing path"""
global finger_path
finger_path.clear()
return "Path cleared!"
def webcam_feed():
"""For Gradio webcam input"""
return None
# Create Gradio interface
with gr.Blocks(title="Shape Drawing Game") as demo:
gr.Markdown("# ✏️ Shape Drawing Game")
gr.Markdown("Draw shapes in the air using your index finger and see how close you get to the target shape!")
# Using Webcam component for compatibility with older Gradio versions
with gr.Row():
webcam = gr.Webcam(label="Webcam Feed")
processed = gr.Image(label="Game View")
with gr.Row():
clear_button = gr.Button("Clear Drawing")
status = gr.Textbox(label="Status")
# Connect components
webcam.change(process_frame, inputs=webcam, outputs=processed)
clear_button.click(fn=clear_path, outputs=status)
gr.Markdown("""
## How to Play:
1. Position your hand in front of the camera
2. Click the webcam button to capture frames
3. Raise your index finger to start drawing
4. Try to trace the green circle as closely as possible
5. See your score update in real-time
6. Click 'Clear Drawing' to start over
""")
# Requirements for Hugging Face Spaces
requirements = ["opencv-python", "mediapipe", "numpy", "gradio"]
# Create a README.md file for the project
readme = """
# Shape Drawing Game
This interactive app transforms your webcam into a real-time shape drawing game. Using your index finger, you draw shapes in the air, and the app:
- Tracks your hand movements live using Mediapipe's hand tracking
- Builds a trail of your fingertip's path
- Compares your drawn path against a target shape (currently a circle)
- Calculates a live accuracy score and gives you immediate feedback
## How to Play
1. Position your hand in front of the camera
2. Click the webcam button to capture frames
3. Raise your index finger to start drawing
4. Try to trace the green circle as closely as possible
5. See your score update in real-time
6. Click 'Clear Drawing' to start over
## Dependencies
- opencv-python
- mediapipe
- numpy
- gradio
"""
# Launch the app
if __name__ == "__main__":
demo.launch() |