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 |
|