معرفی
این پروژه یک کیبورد مجازی پیشرفته است که با استفاده از هوش مصنوعی و بینایی کامپیوتر، حرکات دست شما را تشخیص داده و امکان تایپ بدون لمس فیزیکی صفحهکلید را فراهم میکند.
ویژگیهای اصلی:
- تشخیص حرکات دست با استفاده از کتابخانه
MediaPipe - طراحی رابط کاربری زیبا و واکنشگرا
- قابلیت جستجوی مستقیم در گوگل و یوتیوب
- بهینهسازی شده برای عملکرد بالا (FPS)
- پشتیبانی از حرکات لمسی (انگشت شست و اشاره)
- نمایش زنده دوربین با overlay گرافیکی
پیشنیازها (کتابخانهها و ابزارها)
Python 3.8+(توصیهشده 3.10/3.11) کتابخانهMediaPipeروی پایتون 3.13 پشتیبانی نمیشودpip(مدیر بستهٔ پایتون)OpenCV (cv2)پردازش تصویر و ویدئوcvzoneکتابخانه کمکی برای OpenCVMediaPipeتشخیص دست و حرکاتpynputکنترل کیبورد سیستمnumpyمحاسبات عددیwebbrowserباز کردن مرورگر
pip install opencv-python cvzone mediapipe pynput numpy
در ویندوز اگر چند نسخهٔ پایتون نصب دارید، ممکن است لازم باشد از
py -3 -m pip install استفاده کنید.
راهاندازی گامبهگام
1. ایجاد محیط مجازی (Virtual Environment)
# ایجاد محیط مجازی
python -m venv virtual_keyboard_env
# فعال کردن محیط در ویندوز
virtual_keyboard_env\Scripts\activate
# فعال کردن محیط در لینوکس/مک
source virtual_keyboard_env/bin/activate
2. نصب کتابخانههای مورد نیاز
pip install opencv-python cvzone mediapipe pynput numpy
3. تست دوربین
قبل از اجرای برنامه اصلی، مطمئن شوید دوربین شما به درستی کار میکند:
import cv2
cap = cv2.VideoCapture(0)
if cap.isOpened():
print("دوربین فعال است")
cap.release()
else:
print("مشکل در اتصال به دوربین")
چگونه برنامه را اجرا کنیم
- کد کامل را در یک فایل به نام
virtual_keyboard.pyذخیره کنید - محیط مجازی را فعال کنید
- در ترمینال دستور زیر را اجرا کنید:
python virtual_keyboard.py
راهنمای استفاده:
- از انگشت اشاره برای اشاره به دکمهها استفاده کنید
- برای کلیک، انگشت شست و اشاره را به هم نزدیک کنید
- دکمه Google: متن را در گوگل جستجو میکند
- دکمه YouTube: متن را در یوتیوب جستجو میکند
- دکمه Space: فاصله ایجاد میکند
- دکمه Clear: متن را پاک میکند
- دکمه <: کاراکتر آخر را حذف میکند
- کلید q: خروج از برنامه
کد کامل پروژه (قابل کپی)
کد کامل کیبورد مجازی در ادامه آمده است — میتوانید آن را کپی کرده و در فایل virtual_keyboard.py ذخیره کنید.
import cv2
from cvzone.HandTrackingModule import HandDetector
from time import sleep, time
import numpy as np
import cvzone
from pynput.keyboard import Controller
import webbrowser
import urllib.parse
class Button:
def __init__(self, pos, text, size=[60, 60]):
self.pos = pos
self.size = size
self.text = text
class VirtualKeyboard:
def __init__(self):
# Camera setup with optimized settings
self.cap = cv2.VideoCapture(0)
if not self.cap.isOpened():
print("Error: Could not open camera")
return
# Set lower resolution for better FPS
self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
self.cap.set(cv2.CAP_PROP_FPS, 60)
self.cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
# Hand detector with optimized settings
self.detector = HandDetector(detectionCon=0.7, maxHands=1)
self.keyboard = Controller()
self.final_text = ""
# Keyboard layout with Google and YouTube buttons
self.keys = [
["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "<"],
["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"],
["A", "S", "D", "F", "G", "H", "J", "K", "L", ";"],
["Z", "X", "C", "V", "B", "N", "M", ",", ".", "/"],
["Space", "Clear", "Enter", "Google", "YouTube"]
]
# Button properties
self.button_width = 50
self.button_height = 50
self.button_spacing = 6
self.cooldown_time = 0.2
self.button_list = self.create_buttons()
self.last_press_time = 0
self.frame_count = 0
self.fps = 0
self.last_fps_time = time()
def create_buttons(self):
"""Create button objects with proper positioning"""
button_list = []
start_x = 30
start_y = 30
for i, row in enumerate(self.keys):
if i == 0:
for j, key in enumerate(row):
x = start_x + j * (self.button_width + self.button_spacing)
y = start_y + i * (self.button_height + self.button_spacing)
button_list.append(Button([x, y], key))
elif i == 4:
special_widths = [
self.button_width * 2,
self.button_width * 1.5,
self.button_width * 1.5,
self.button_width * 2,
self.button_width * 2
]
x_offset = start_x
for j, key in enumerate(row):
x = x_offset
y = start_y + i * (self.button_height + self.button_spacing)
button_list.append(Button([x, y], key, [int(special_widths[j]), self.button_height]))
x_offset += special_widths[j] + self.button_spacing
else:
for j, key in enumerate(row):
x = start_x + j * (self.button_width + self.button_spacing)
y = start_y + i * (self.button_height + self.button_spacing)
button_list.append(Button([x, y], key))
return button_list
def draw_keyboard(self, img):
"""Draw the virtual keyboard in the top section"""
if img is None:
return None
img_with_keyboard = img.copy()
overlay = img.copy()
cv2.rectangle(overlay, (0, 0), (img.shape[1], 350), (0, 0, 0), -1)
cv2.addWeighted(overlay, 0.5, img_with_keyboard, 0.5, 0, img_with_keyboard)
button_overlay = np.zeros_like(img_with_keyboard)
for button in self.button_list:
x, y = button.pos
w_btn, h_btn = button.size
x, y, w_btn, h_btn = int(x), int(y), int(w_btn), int(h_btn)
if button.text in ["Space", "Clear", "Enter"]:
color = (100, 0, 200)
elif button.text == "<":
color = (200, 100, 0)
elif button.text == "Google":
color = (66, 133, 244)
elif button.text == "YouTube":
color = (255, 0, 0)
else:
color = (200, 0, 200)
cv2.rectangle(button_overlay, (x, y), (x + w_btn, y + h_btn), color, cv2.FILLED)
cv2.rectangle(button_overlay, (x, y), (x + w_btn, y + h_btn), (255, 255, 255), 2)
text_scale = 0.5 if len(button.text) > 5 else 0.6
text_thickness = 2
text_size = cv2.getTextSize(button.text, cv2.FONT_HERSHEY_SIMPLEX, text_scale, text_thickness)[0]
text_x = x + (w_btn - text_size[0]) // 2
text_y = y + (h_btn + text_size[1]) // 2
cv2.putText(button_overlay, button.text, (text_x, text_y),
cv2.FONT_HERSHEY_SIMPLEX, text_scale, (255, 255, 255), text_thickness)
mask = button_overlay.astype(bool)
img_with_keyboard[mask] = cv2.addWeighted(img_with_keyboard, 0.2, button_overlay, 0.8, 0)[mask]
return img_with_keyboard
def draw_text_display(self, img):
"""Draw the text display below the keyboard"""
if img is None:
return
h, w = img.shape[:2]
display_y = 350
cv2.rectangle(img, (30, display_y), (w - 30, display_y + 50), (50, 50, 80), cv2.FILLED)
cv2.rectangle(img, (30, display_y), (w - 30, display_y + 50), (255, 255, 255), 2)
display_text = self.final_text
if len(display_text) > 35:
display_text = "..." + display_text[-35:]
cv2.putText(img, display_text, (40, display_y + 35),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2)
cv2.putText(img, f"Chars: {len(self.final_text)}", (w - 100, display_y + 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (200, 200, 200), 1)
def open_google_search(self, search_text):
"""Open Google with the search text"""
try:
if search_text.strip():
encoded_search = urllib.parse.quote_plus(search_text)
url = f"https://www.google.com/search?q={encoded_search}"
webbrowser.open(url)
print(f"Opening Google search: {search_text}")
else:
webbrowser.open("https://www.google.com")
print("Opening Google homepage")
except Exception as e:
print(f"Error opening Google: {e}")
def open_youtube_search(self, search_text):
"""Open YouTube with the search text"""
try:
if search_text.strip():
encoded_search = urllib.parse.quote_plus(search_text)
url = f"https://www.youtube.com/results?search_query={encoded_search}"
webbrowser.open(url)
print(f"Opening YouTube search: {search_text}")
else:
webbrowser.open("https://www.youtube.com")
print("Opening YouTube homepage")
except Exception as e:
print(f"Error opening YouTube: {e}")
def handle_special_keys(self, key):
"""Handle special key functions"""
try:
if key == "Space":
self.final_text += " "
self.keyboard.press(' ')
self.keyboard.release(' ')
return True
elif key == "Clear":
self.final_text = ""
return True
elif key == "Enter":
self.final_text += "\n"
self.keyboard.press('\n')
self.keyboard.release('\n')
return True
elif key == "<":
if self.final_text:
self.final_text = self.final_text[:-1]
self.keyboard.press('\x08')
self.keyboard.release('\x08')
return True
elif key == "Google":
self.open_google_search(self.final_text)
return True
elif key == "YouTube":
self.open_youtube_search(self.final_text)
return True
return False
except Exception as e:
print(f"Error handling key {key}: {e}")
return False
def process_hand_interaction(self, img, lmList):
"""Process hand interactions with keyboard"""
if not lmList or len(lmList) < 21:
return
current_time = time()
for button in self.button_list:
x, y = button.pos
w_btn, h_btn = button.size
x, y, w_btn, h_btn = int(x), int(y), int(w_btn), int(h_btn)
if x < lmList[8][0] < x + w_btn and y < lmList[8][1] < y + h_btn:
cv2.rectangle(img, (x-3, y-3), (x + w_btn + 3, y + h_btn + 3), (100, 200, 255), 3)
try:
length, info, img = self.detector.findDistance(lmList[8][:2], lmList[4][:2], img)
cv2.line(img,
(int(lmList[8][0]), int(lmList[8][1])),
(int(lmList[4][0]), int(lmList[4][1])),
(0, 255, 255), 3)
if length < 35 and (current_time - self.last_press_time) > self.cooldown_time:
cv2.rectangle(img, (x-5, y-5), (x + w_btn + 5, y + h_btn + 5), (0, 255, 0), cv2.FILLED)
if button.text in ["Google", "YouTube"]:
cv2.rectangle(img, (x-7, y-7), (x + w_btn + 7, y + h_btn + 7), (255, 255, 0), 3)
if not self.handle_special_keys(button.text):
self.final_text += button.text
self.keyboard.press(button.text)
self.keyboard.release(button.text)
self.last_press_time = current_time
except Exception as e:
print(f"Error in hand interaction: {e}")
def calculate_fps(self):
"""Calculate actual FPS"""
self.frame_count += 1
current_time = time()
if current_time - self.last_fps_time >= 1.0:
self.fps = self.frame_count
self.frame_count = 0
self.last_fps_time = current_time
def run(self):
"""Main application loop"""
print("Virtual Keyboard Started! Press 'q' to quit.")
print("Features:")
print("- Type text using hand gestures")
print("- Click 'Google' to open Google search with your text")
print("- Click 'YouTube' to open YouTube search with your text")
print("- Use INDEX FINGER and THUMB to click (bring them together)")
print("- High opacity buttons for better visibility")
print("- Optimized for high FPS performance")
if not self.cap.isOpened():
print("Error: Camera not available")
return
cv2.namedWindow("Virtual Keyboard", cv2.WINDOW_NORMAL)
cv2.resizeWindow("Virtual Keyboard", 800, 600)
print("Optimizing camera...")
for _ in range(10):
self.cap.read()
print("Starting main loop...")
while True:
try:
success, img = self.cap.read()
if not success:
print("Failed to capture image")
break
self.calculate_fps()
img = cv2.flip(img, 1)
hands, img_with_hands = self.detector.findHands(img, flipType=False, draw=True)
lmList = hands[0]["lmList"] if hands else []
img_with_keyboard = self.draw_keyboard(img_with_hands)
if img_with_keyboard is None:
continue
self.draw_text_display(img_with_keyboard)
if lmList:
self.process_hand_interaction(img_with_keyboard, lmList)
h, w = img_with_keyboard.shape[:2]
cv2.line(img_with_keyboard, (0, 350), (w, 350), (255, 255, 255), 2)
cv2.putText(img_with_keyboard, f"FPS: {self.fps}", (w - 80, 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
cv2.putText(img_with_keyboard, "VIRTUAL KEYBOARD", (30, 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
cv2.putText(img_with_keyboard, "Use INDEX + THUMB to click", (200, 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 0), 2)
cv2.putText(img_with_keyboard, "CAMERA FEED", (30, 370),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
cv2.putText(img_with_keyboard, "Gesture: Bring thumb and index finger together to click",
(30, h - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 255), 2)
cv2.imshow("Virtual Keyboard", img_with_keyboard)
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
break
elif key == ord('f'):
cv2.setWindowProperty("Virtual Keyboard", cv2.WND_PROP_FULLSCREEN,
cv2.WINDOW_FULLSCREEN ^ cv2.WINDOW_FULLSCREEN)
except Exception as e:
print(f"Error in main loop: {e}")
break
self.cap.release()
cv2.destroyAllWindows()
print("Virtual Keyboard closed.")
if __name__ == "__main__":
try:
vk = VirtualKeyboard()
vk.run()
except Exception as e:
print(f"Fatal error: {e}")
input("Press Enter to exit...")
شرح قسمتهای مهم کد
کلاس Button
این کلاس برای ایجاد دکمههای مجازی استفاده میشود:
pos: موقعیت دکمه در صفحهtext: متن روی دکمهsize: اندازه دکمه
کلاس VirtualKeyboard
کلاس اصلی که تمام منطق برنامه را مدیریت میکند:
متد __init__
- تنظیمات دوربین و تشخیص دست
- تعریف layout کیبورد
- مشخصات دکمهها و فاصلهها
متد create_buttons
دکمهها را بر اساس layout تعریف شده ایجاد میکند.
متد draw_keyboard
کیبورد مجازی را روی تصویر دوربین رسم میکند.
متد process_hand_interaction
حرکات دست را پردازش کرده و برهمکنش با دکمهها را مدیریت میکند.
متد handle_special_keys
دکمههای خاص مانند Space, Clear, Google, YouTube را مدیریت میکند.
عیبیابی و نکات
- دوربین کار نمیکند: مطمئن شوید دوربین در برنامههای دیگر قفل نیست
- تشخیص دست ضعیف است: نور محیط را افزایش دهید
- FPS پایین: رزولوشن دوربین را کاهش دهید
- دکمهها پاسخ نمیدهند: فاصله دست از دوربین را تنظیم کنید
- خطای کتابخانه: مطمئن شوید همه کتابخانهها نصب شدهاند
نکات بهینهسازی:
- از نور کافی برای تشخیص بهتر دست استفاده کنید
- دست خود را در فاصله مناسب از دوربین نگه دارید (30-60 سانتیمتر)
- حرکات را آرام و دقیق انجام دهید
- برای عملکرد بهتر، برنامههای دیگر را ببندید
منابع
منبع اصلی کد گرفته شده