-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvirtual_mouse.py
More file actions
206 lines (168 loc) · 8.63 KB
/
virtual_mouse.py
File metadata and controls
206 lines (168 loc) · 8.63 KB
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
205
206
"""
Virtual Mouse Controller using Hand Gestures
"""
import cv2
import numpy as np
import pyautogui
import time
from collections import deque
from hand_detector import HandDetector
import config
# Disable PyAutoGUI fail-safe
pyautogui.FAILSAFE = False
class VirtualMouse:
def __init__(self):
self.detector = HandDetector()
self.screen_width, self.screen_height = pyautogui.size()
# Smoothing
self.smooth_x = deque(maxlen=config.MOUSE_SMOOTHING)
self.smooth_y = deque(maxlen=config.MOUSE_SMOOTHING)
# Scroll smoothing
self.scroll_buffer = deque(maxlen=config.SCROLL_SMOOTHING)
# State tracking
self.prev_time = 0
self.is_dragging = False
self.prev_scroll_y = 0
self.last_click_time = 0
self.last_right_click_time = 0
def run(self):
"""
Main loop for virtual mouse
"""
cap = cv2.VideoCapture(config.CAMERA_INDEX)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, config.FRAME_WIDTH)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, config.FRAME_HEIGHT)
# Create window with specific properties
window_name = "Virtual Mouse"
cv2.namedWindow(window_name, cv2.WINDOW_NORMAL if config.WINDOW_RESIZABLE else cv2.WINDOW_AUTOSIZE)
# Set window to fullscreen or maximized
if config.WINDOW_FULLSCREEN:
cv2.setWindowProperty(window_name, cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
elif config.WINDOW_MAXIMIZED:
cv2.setWindowProperty(window_name, cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_NORMAL)
cv2.resizeWindow(window_name, 1280, 720)
print("Virtual Mouse Started!")
print("Works with EITHER left or right hand!")
print("\n🔥 EXTREME DETECTION - Detects hand VERY easily!")
print(" - Ultra-low threshold (0.1 - MAXIMUM sensitivity!)")
print(" - Fast image enhancement (no lag)")
print(" - Optimized for smooth performance")
print("\n💡 TIPS:")
print(" - Just raise your hand - it will detect instantly!")
print(" - Works in any condition")
print(" - Spread fingers for best results")
print("\nGestures:")
print("- Index finger up: Move cursor (fast & smooth)")
print("- Thumb + Index pinch: Left click")
print("- Thumb + Middle pinch: Right click")
print("- Index + Middle fingers up: Scroll (smooth)")
print("\nControls:")
print("Press 'F' to toggle fullscreen")
print("Press 'Q' to quit")
fullscreen = config.WINDOW_FULLSCREEN
while True:
success, frame = cap.read()
if not success:
break
frame = cv2.flip(frame, 1)
frame = self.detector.find_hands(frame)
landmark_list = self.detector.get_position(frame)
if len(landmark_list) > 0:
# Get finger states
fingers = self.detector.fingers_up(landmark_list)
# Index finger up - Move cursor (faster, smoother)
if fingers[1] == 1 and fingers[2] == 0:
x1, y1 = landmark_list[8][1], landmark_list[8][2]
# Convert coordinates with speed multiplier
x3 = np.interp(x1, (80, config.FRAME_WIDTH - 80),
(0, self.screen_width)) * config.MOUSE_SPEED
y3 = np.interp(y1, (80, config.FRAME_HEIGHT - 80),
(0, self.screen_height)) * config.MOUSE_SPEED
# Clamp to screen bounds
x3 = max(0, min(self.screen_width - 1, x3))
y3 = max(0, min(self.screen_height - 1, y3))
# Smooth movement
self.smooth_x.append(x3)
self.smooth_y.append(y3)
smooth_x = sum(self.smooth_x) / len(self.smooth_x)
smooth_y = sum(self.smooth_y) / len(self.smooth_y)
# Move cursor instantly
pyautogui.moveTo(smooth_x, smooth_y)
# Draw cursor position
cv2.circle(frame, (x1, y1), 15, config.CURSOR_COLOR, cv2.FILLED)
# Left Click - Thumb and Index pinch (with debounce)
if fingers[0] == 1 and fingers[1] == 1:
length, x1, y1, x2, y2 = self.detector.get_distance(
4, 8, landmark_list
)
current_time = time.time()
if length < config.CLICK_THRESHOLD:
cv2.circle(frame, (x1, y1), 15, config.CLICK_COLOR, cv2.FILLED)
if current_time - self.last_click_time > 0.3:
pyautogui.click()
self.last_click_time = current_time
print("Left Click")
# Right Click - Thumb and Middle pinch (with debounce)
if fingers[0] == 1 and fingers[2] == 1 and fingers[1] == 0:
length, x1, y1, x2, y2 = self.detector.get_distance(
4, 12, landmark_list
)
current_time = time.time()
if length < config.CLICK_THRESHOLD:
cv2.circle(frame, (x1, y1), 15, (255, 0, 255), cv2.FILLED)
if current_time - self.last_right_click_time > 0.3:
pyautogui.rightClick()
self.last_right_click_time = current_time
print("Right Click")
# Enhanced Scroll - Index and Middle finger up (smoother)
if fingers[1] == 1 and fingers[2] == 1 and fingers[0] == 0:
# Use middle point between index and middle finger for stability
y_index = landmark_list[8][2]
y_middle = landmark_list[12][2]
y_pos = (y_index + y_middle) // 2
if self.prev_scroll_y != 0:
scroll_delta = (self.prev_scroll_y - y_pos)
self.scroll_buffer.append(scroll_delta)
# Average scroll for smoothness
avg_scroll = sum(self.scroll_buffer) / len(self.scroll_buffer)
scroll_amount = int(avg_scroll / 8)
if abs(scroll_amount) > 0:
pyautogui.scroll(scroll_amount * config.SCROLL_SPEED)
self.prev_scroll_y = y_pos
# Visual feedback
cv2.putText(frame, "SCROLL MODE", (10, 100),
cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 0), 2)
cv2.circle(frame, (landmark_list[8][1], y_pos), 12, (255, 255, 0), cv2.FILLED)
else:
self.prev_scroll_y = 0
self.scroll_buffer.clear()
# Show instructions
if config.SHOW_INSTRUCTIONS:
cv2.putText(frame, "F: Fullscreen | Q: Quit", (10, frame.shape[0] - 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 1)
# FPS
if config.SHOW_FPS:
curr_time = time.time()
fps = 1 / (curr_time - self.prev_time) if self.prev_time > 0 else 0
self.prev_time = curr_time
cv2.putText(frame, f'FPS: {int(fps)}', (10, 40),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
cv2.imshow(window_name, frame)
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
break
elif key == ord('f'):
# Toggle fullscreen
fullscreen = not fullscreen
if fullscreen:
cv2.setWindowProperty(window_name, cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
print("Fullscreen mode ON")
else:
cv2.setWindowProperty(window_name, cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_NORMAL)
print("Fullscreen mode OFF")
cap.release()
cv2.destroyAllWindows()
self.detector.close()
if __name__ == "__main__":
mouse = VirtualMouse()
mouse.run()