Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -209,12 +209,13 @@
|
|
| 209 |
|
| 210 |
|
| 211 |
|
|
|
|
|
|
|
| 212 |
import streamlit as st
|
| 213 |
import mediapipe as mp
|
| 214 |
import cv2
|
|
|
|
| 215 |
import os
|
| 216 |
-
import time
|
| 217 |
-
from queue import Queue
|
| 218 |
from utils import display_gesture_chart
|
| 219 |
|
| 220 |
# Initialize MediaPipe Hands for hand landmark detection
|
|
@@ -236,27 +237,6 @@ model_path = 'gesture_recognizer.task'
|
|
| 236 |
if not os.path.exists(model_path):
|
| 237 |
raise FileNotFoundError(f"Model file not found at {model_path}")
|
| 238 |
|
| 239 |
-
# Queue to share gesture results between the callback and the main thread
|
| 240 |
-
gesture_queue = Queue()
|
| 241 |
-
|
| 242 |
-
# Callback function to process gesture results and add them to the queue
|
| 243 |
-
def print_result(result: GestureRecognizerResult, output_image: mp.Image, timestamp_ms: int):
|
| 244 |
-
results = []
|
| 245 |
-
if result.gestures:
|
| 246 |
-
for hand_gestures in result.gestures:
|
| 247 |
-
for gesture in hand_gestures:
|
| 248 |
-
results.append(f"{gesture.category_name} (Confidence: {gesture.score:.2f})")
|
| 249 |
-
else:
|
| 250 |
-
results.append("No gestures detected.")
|
| 251 |
-
gesture_queue.put(results)
|
| 252 |
-
|
| 253 |
-
# Configure the Gesture Recognizer options
|
| 254 |
-
options = GestureRecognizerOptions(
|
| 255 |
-
base_options=BaseOptions(model_asset_path=model_path),
|
| 256 |
-
running_mode=VisionRunningMode.LIVE_STREAM,
|
| 257 |
-
result_callback=print_result
|
| 258 |
-
)
|
| 259 |
-
|
| 260 |
# Initialize session state for saving recognized gestures
|
| 261 |
if "recognized_gestures" not in st.session_state:
|
| 262 |
st.session_state.recognized_gestures = []
|
|
@@ -282,104 +262,40 @@ st.sidebar.markdown("<hr>", unsafe_allow_html=True)
|
|
| 282 |
gesture_chart_path = "./gestureReference.png" # Update this with the actual path to the image
|
| 283 |
display_gesture_chart(gesture_chart_path)
|
| 284 |
|
| 285 |
-
# User-configurable options
|
| 286 |
-
max_num_hands = st.sidebar.slider("Max Number of Hands", 1, 2, 1)
|
| 287 |
-
skip_frames = st.sidebar.slider("Process Every Nth Frame", 1, 10, 5)
|
| 288 |
-
resolution = st.sidebar.selectbox("Frame Resolution", ["320x240", "640x480"], index=0)
|
| 289 |
-
|
| 290 |
-
st.sidebar.markdown("<hr>", unsafe_allow_html=True)
|
| 291 |
-
|
| 292 |
-
# Start and Stop buttons
|
| 293 |
-
if "run_app" not in st.session_state:
|
| 294 |
-
st.session_state.run_app = False
|
| 295 |
-
|
| 296 |
-
col1, col2 = st.sidebar.columns(2)
|
| 297 |
-
if col1.button("▶ Start"):
|
| 298 |
-
st.session_state.run_app = True
|
| 299 |
-
|
| 300 |
-
if col2.button("⏹ Stop"):
|
| 301 |
-
st.session_state.run_app = False
|
| 302 |
-
|
| 303 |
# Clear gesture history button
|
| 304 |
if st.sidebar.button("🚲 Clear History"):
|
| 305 |
st.session_state.recognized_gestures = []
|
| 306 |
|
| 307 |
-
# Layout with columns:
|
| 308 |
col_feed, col_log = st.columns([5, 2])
|
| 309 |
|
| 310 |
with col_feed:
|
| 311 |
-
st.markdown("###
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
cap = cv2.VideoCapture(0)
|
| 334 |
-
|
| 335 |
-
# Parse resolution to width and height
|
| 336 |
-
res_width, res_height = map(int, resolution.split("x"))
|
| 337 |
-
|
| 338 |
-
# Start a timestamp for gesture recognition
|
| 339 |
-
start_time = time.time()
|
| 340 |
-
|
| 341 |
-
# Initialize MediaPipe components
|
| 342 |
-
with GestureRecognizer.create_from_options(options) as recognizer, mp_hands.Hands(
|
| 343 |
-
max_num_hands=max_num_hands,
|
| 344 |
-
model_complexity=1, # Use simplified model for better performance
|
| 345 |
-
min_detection_confidence=0.5,
|
| 346 |
-
min_tracking_confidence=0.5
|
| 347 |
-
) as hands:
|
| 348 |
-
frame_count = 0 # Counter to track frames processed
|
| 349 |
-
|
| 350 |
-
while st.session_state.run_app and cap.isOpened():
|
| 351 |
-
success, frame = cap.read()
|
| 352 |
-
if not success:
|
| 353 |
-
st.error("Unable to access the camera. Please check your camera connection.")
|
| 354 |
-
st.session_state.run_app = False
|
| 355 |
-
break
|
| 356 |
-
|
| 357 |
-
frame_count += 1
|
| 358 |
-
|
| 359 |
-
# Skip frames based on the user-configured interval
|
| 360 |
-
if frame_count % skip_frames != 0:
|
| 361 |
-
continue
|
| 362 |
-
|
| 363 |
-
# Flip the frame horizontally for a mirror-like view
|
| 364 |
-
frame = cv2.flip(frame, 1)
|
| 365 |
-
|
| 366 |
-
# Resize the frame to the selected resolution
|
| 367 |
-
frame = cv2.resize(frame, (res_width, res_height))
|
| 368 |
-
|
| 369 |
-
# Convert the frame to RGB for processing by MediaPipe
|
| 370 |
-
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
| 371 |
-
|
| 372 |
# Perform hand landmark detection
|
| 373 |
hand_results = hands.process(frame_rgb)
|
| 374 |
|
| 375 |
-
#
|
| 376 |
-
mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=frame)
|
| 377 |
-
current_time_ms = int((time.time() - start_time) * 1000)
|
| 378 |
-
|
| 379 |
-
# Perform asynchronous gesture recognition
|
| 380 |
-
recognizer.recognize_async(mp_image, current_time_ms)
|
| 381 |
-
|
| 382 |
-
# Draw hand landmarks on the frame
|
| 383 |
if hand_results.multi_hand_landmarks:
|
| 384 |
for hand_landmarks in hand_results.multi_hand_landmarks:
|
| 385 |
mp_drawing.draw_landmarks(
|
|
@@ -389,45 +305,41 @@ if st.session_state.run_app:
|
|
| 389 |
mp_drawing_styles.get_default_hand_landmarks_style(),
|
| 390 |
mp_drawing_styles.get_default_hand_connections_style(),
|
| 391 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 392 |
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
|
| 396 |
-
if results:
|
| 397 |
-
new_gesture = results[-1]
|
| 398 |
-
|
| 399 |
-
# Extract gesture label and confidence
|
| 400 |
-
if " (Confidence: " in new_gesture:
|
| 401 |
-
label, confidence = new_gesture.split(" (Confidence: ")
|
| 402 |
-
confidence = confidence.rstrip(")")
|
| 403 |
-
else:
|
| 404 |
-
label = new_gesture
|
| 405 |
-
confidence = "N/A"
|
| 406 |
-
|
| 407 |
-
# Add gesture to history if not already logged
|
| 408 |
-
if label.isalpha() and new_gesture not in st.session_state.recognized_gestures:
|
| 409 |
-
st.session_state.recognized_gestures.append(new_gesture)
|
| 410 |
-
|
| 411 |
-
# Update current gesture display
|
| 412 |
-
current_gesture_box.markdown(
|
| 413 |
-
f"<h4 style='text-align: center; color: #4CAF50;'>Gesture: {label}<br>Confidence: {confidence}</h4>",
|
| 414 |
-
unsafe_allow_html=True,
|
| 415 |
-
)
|
| 416 |
-
|
| 417 |
-
# Update gesture history display
|
| 418 |
-
gesture_history_box.text_area(
|
| 419 |
-
"Gesture History:",
|
| 420 |
-
value="\n".join(reversed(st.session_state.recognized_gestures)),
|
| 421 |
-
height=300,
|
| 422 |
-
disabled=True,
|
| 423 |
-
)
|
| 424 |
-
|
| 425 |
-
# Display the processed frame with landmarks and gesture details
|
| 426 |
-
video_placeholder.image(frame, channels="BGR", caption="Gesture & Hand Landmark Detection", use_column_width=True)
|
| 427 |
-
|
| 428 |
-
# Release the video capture resource
|
| 429 |
-
cap.release()
|
| 430 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 431 |
|
| 432 |
|
| 433 |
|
|
|
|
| 209 |
|
| 210 |
|
| 211 |
|
| 212 |
+
|
| 213 |
+
|
| 214 |
import streamlit as st
|
| 215 |
import mediapipe as mp
|
| 216 |
import cv2
|
| 217 |
+
import numpy as np
|
| 218 |
import os
|
|
|
|
|
|
|
| 219 |
from utils import display_gesture_chart
|
| 220 |
|
| 221 |
# Initialize MediaPipe Hands for hand landmark detection
|
|
|
|
| 237 |
if not os.path.exists(model_path):
|
| 238 |
raise FileNotFoundError(f"Model file not found at {model_path}")
|
| 239 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 240 |
# Initialize session state for saving recognized gestures
|
| 241 |
if "recognized_gestures" not in st.session_state:
|
| 242 |
st.session_state.recognized_gestures = []
|
|
|
|
| 262 |
gesture_chart_path = "./gestureReference.png" # Update this with the actual path to the image
|
| 263 |
display_gesture_chart(gesture_chart_path)
|
| 264 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 265 |
# Clear gesture history button
|
| 266 |
if st.sidebar.button("🚲 Clear History"):
|
| 267 |
st.session_state.recognized_gestures = []
|
| 268 |
|
| 269 |
+
# Layout with columns: Webcam input on the left, gesture log box on the right
|
| 270 |
col_feed, col_log = st.columns([5, 2])
|
| 271 |
|
| 272 |
with col_feed:
|
| 273 |
+
st.markdown("### Webcam Input")
|
| 274 |
+
# Use st.camera_input() to capture an image from the browser-based webcam
|
| 275 |
+
image_data = st.camera_input("Take a picture using your webcam")
|
| 276 |
+
|
| 277 |
+
if image_data:
|
| 278 |
+
# Read the image as a numpy array
|
| 279 |
+
file_bytes = np.asarray(bytearray(image_data.read()), dtype=np.uint8)
|
| 280 |
+
frame = cv2.imdecode(file_bytes, 1)
|
| 281 |
+
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
| 282 |
+
|
| 283 |
+
# Configure Gesture Recognizer
|
| 284 |
+
options = GestureRecognizerOptions(
|
| 285 |
+
base_options=BaseOptions(model_asset_path=model_path),
|
| 286 |
+
running_mode=VisionRunningMode.IMAGE # Use IMAGE mode for single-frame processing
|
| 287 |
+
)
|
| 288 |
+
|
| 289 |
+
# Initialize MediaPipe Gesture Recognizer
|
| 290 |
+
with GestureRecognizer.create_from_options(options) as recognizer, mp_hands.Hands(
|
| 291 |
+
model_complexity=1,
|
| 292 |
+
min_detection_confidence=0.5,
|
| 293 |
+
min_tracking_confidence=0.5,
|
| 294 |
+
) as hands:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 295 |
# Perform hand landmark detection
|
| 296 |
hand_results = hands.process(frame_rgb)
|
| 297 |
|
| 298 |
+
# Recognize gestures if landmarks are detected
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 299 |
if hand_results.multi_hand_landmarks:
|
| 300 |
for hand_landmarks in hand_results.multi_hand_landmarks:
|
| 301 |
mp_drawing.draw_landmarks(
|
|
|
|
| 305 |
mp_drawing_styles.get_default_hand_landmarks_style(),
|
| 306 |
mp_drawing_styles.get_default_hand_connections_style(),
|
| 307 |
)
|
| 308 |
+
st.image(frame, channels="BGR", caption="Processed Image with Landmarks")
|
| 309 |
+
|
| 310 |
+
# Prepare frame for gesture recognition
|
| 311 |
+
mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=frame_rgb)
|
| 312 |
+
recognizer_result = recognizer.recognize(mp_image)
|
| 313 |
+
|
| 314 |
+
if recognizer_result.gestures:
|
| 315 |
+
for hand_gestures in recognizer_result.gestures:
|
| 316 |
+
for gesture in hand_gestures:
|
| 317 |
+
label = gesture.category_name
|
| 318 |
+
confidence = f"{gesture.score:.2f}"
|
| 319 |
|
| 320 |
+
# Add gesture to history
|
| 321 |
+
if label not in st.session_state.recognized_gestures:
|
| 322 |
+
st.session_state.recognized_gestures.append(f"{label} (Confidence: {confidence})")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 323 |
|
| 324 |
+
with col_log:
|
| 325 |
+
st.markdown("### Gesture History")
|
| 326 |
+
gesture_history_box = st.text_area(
|
| 327 |
+
"Recognized Gestures:",
|
| 328 |
+
value="\n".join(reversed(st.session_state.recognized_gestures)),
|
| 329 |
+
height=300,
|
| 330 |
+
disabled=True,
|
| 331 |
+
)
|
| 332 |
+
|
| 333 |
+
# Footer with branding
|
| 334 |
+
st.sidebar.markdown(
|
| 335 |
+
"""
|
| 336 |
+
<style>
|
| 337 |
+
.footer {text-align: center; font-size: 12px; color: grey; margin-top: 20px;}
|
| 338 |
+
</style>
|
| 339 |
+
<p class="footer">Made by Marco Chen, William Taka, Rigoberto Ponce using Streamlit, MediaPipe & OpenCV</p>
|
| 340 |
+
""",
|
| 341 |
+
unsafe_allow_html=True,
|
| 342 |
+
)
|
| 343 |
|
| 344 |
|
| 345 |
|