Виртуальная клавиатура на Python+OpenCV

от автора

В данной статье разберемся, как написать виртуальную клавиатуру, используя Python+Opencv.

Идея заключается в создании виртуальной клавиатуры, ее выводе на экран и возможности ее использования. Подразумевается, что клавиатура будет работать с текстовыми редакторами. Для того, чтобы нажать на кнопку, нам потребуется кликнуть на нее, поэтому представим, что кликом будет служить соединение указательного и среднего пальцев. Теперь можно писать код.

Все исходники можно найти на моем Github.

Установим все необходимые библиотеки для Python:

pip install opencv-python pip install numpy pip install cvzone pip install pynput

В прошлых статьях я использовал класс handDetector, поэтому добавим его и сюда. Создаем файл HandTrackingModule.py:

import cv2 import mediapipe as mp import time import math  class handDetector(): def __init__(self, mode=False, maxHands=2, modelComplexity=1, detectionCon=0.5, trackCon=0.5): self.mode = mode self.maxHands = maxHands self.modelComplexity = modelComplexity self.detectionCon = detectionCon self.trackCon = trackCon  self.mpHands = mp.solutions.hands self.hands = self.mpHands.Hands(self.mode, self.maxHands, self.modelComplexity, self.detectionCon, self.trackCon) self.mpDraw = mp.solutions.drawing_utils self.tipIds = [4, 8, 12, 16, 20]   def findHands(self, img, draw=True): imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) self.results = self.hands.process(imgRGB) #print(results.multi_hand_landmarks)  if self.results.multi_hand_landmarks: for handLms in self.results.multi_hand_landmarks: if draw: self.mpDraw.draw_landmarks(img, handLms, self.mpHands.HAND_CONNECTIONS) return img  def findPosition(self, img, handNo=0, draw=True): xList = [] yList = [] bbox = [] self.lmList = [] if self.results.multi_hand_landmarks: myHand = self.results.multi_hand_landmarks[handNo] for id, lm in enumerate(myHand.landmark): #print(id, lm) h, w, c = img.shape cx, cy = int(lm.x*w), int(lm.y*h) xList.append(cx) yList.append(cy) #print(id, cx, cy) self.lmList.append([id, cx, cy]) if draw: cv2.circle(img, (cx, cy), 5, (255,0,255), cv2.FILLED) xmin, xmax = min(xList), max(xList) ymin, ymax = min(yList), max(yList) bbox = xmin, ymin, xmax, ymax  if draw: cv2.rectangle(img, (bbox[0]-20, bbox[1]-20), (bbox[2]+20, bbox[3]+20), (0, 255, 0), 2) return self.lmList, bbox  def findDistance(self, p1, p2, img, draw=True): x1, y1 = self.lmList[p1][1], self.lmList[p1][2] x2, y2 = self.lmList[p2][1], self.lmList[p2][2] cx, cy = (x1+x2)//2, (y1+y2)//2  if draw: cv2.circle(img, (x1,y1), 15, (255,0,255), cv2.FILLED) cv2.circle(img, (x2,y2), 15, (255,0,255), cv2.FILLED) cv2.line(img, (x1,y1), (x2,y2), (255,0,255), 3) cv2.circle(img, (cx,cy), 15, (255,0,255), cv2.FILLED)  length = math.hypot(x2-x1, y2-y1) return length, img, [x1, y1, x2, y2, cx, cy]  def fingersUp(self): fingers = []  # Thumb if self.lmList[self.tipIds[0]][1] < self.lmList[self.tipIds[0]-1][1]: fingers.append(1) else: fingers.append(0)  # 4 Fingers for id in range(1,5): if self.lmList[self.tipIds[id]][2] < self.lmList[self.tipIds[id]-2][2]: fingers.append(1) else: fingers.append(0) return fingers  def main(): pTime = 0 cTime = 0 cap = cv2.VideoCapture(0) detector = handDetector() while True: success, img = cap.read() img = detector.findHands(img) lmList = detector.findPosition(img) if len(lmList) != 0: print(lmList[1])  cTime = time.time() fps = 1. / (cTime - pTime) pTime = cTime  cv2.putText(img, str(int(fps)), (10,70), cv2.FONT_HERSHEY_PLAIN, 3, (255,0,255), 3)  cv2.imshow("Image", img) cv2.waitKey(1)   if __name__ == "__main__": main()

Теперь создадим main.py и подключим библиотеки:

from tkinter import E import cv2 from HandTrackingModule import handDetector from time import sleep import numpy as np import cvzone from pynput.keyboard import Controller

Теперь можем подключить и настроить камеру:

cap = cv2.VideoCapture(0) cap.set(3, 1280) cap.set(4, 720)

При подключении камеры могут возникнуть ошибки, поменяйте 0 из `cap = cv2.VideoCapture(0)` на 1 или 2.

Добавим поиск руки, нашу будущую клавиатуру и клавиатурный контроллер:

detector = handDetector(detectionCon=0.8) keys = [["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", ",", ".", "/"],         ["<", " "]] finalText = "" keyboard = Controller()

По желанию список keys можно расширить, например цифрами. Я не буду добавлять их. т.к. статья несет информационный характер.

<    - будет backspace'ом

Создадим функцию для отображения клавиатуры на экране:

def drawALL(img, buttonList):     imgNew = np.zeros_like(img, np.uint8)     for button in buttonList:         x, y = button.pos         w, h = button.size         cvzone.cornerRect(imgNew, (x, y, w, h), 20, rt=0)         cv2.rectangle(imgNew, button.pos, (x + w, y + h), (255, 0, 255), cv2.FILLED)         cv2.putText(imgNew, button.text, (x + 20, y + 65), cv2.FONT_HERSHEY_PLAIN, 4, (255, 255, 255), 4)     out = img.copy()     alpha = 0.5     mask = imgNew.astype(bool)     out[mask] = cv2.addWeighted(img, alpha, imgNew, 1-alpha, 0)[mask]     return out

Создаем простой класс Button. Этот класс будет отвечать за позицию, размер и текст каждой кнопки.

class Button():     def __init__(self, pos, text, size=[85, 85]):         self.pos = pos         self.size = size         self.text = text

Теперь каждой кнопке присваиваем позицию, ее размеры и текст, который она будет печатать:

buttonList = [] for i in range(len(keys)):     for j, key in enumerate(keys[i]):         buttonList.append(Button([100 * j + 50, 100 * i + 50], key))

Запускаем бесконечный цикл (можно добавить остановку), отображаем клавиатуру и ищем руку в кадре:

while True:     success, img = cap.read()     img = cv2.flip(img, 1)     img = detector.findHands(img)     lmList, bboxInfo = detector.findPosition(img)      img = drawALL(img, buttonList)

Рассмотри теперь изображение, где показаны Hand Land Marks :

Если рука в кадре, то можно считывать положения пальцев, точнее только указательного пальца, который будет служить курсором:

if lmList:         for button in buttonList:             x, y = button.pos             w, h = button.size              if x < lmList[8][1] < x + w and y < lmList[8][2] < y + h:                 cv2.rectangle(img, button.pos, (x + w, y + h), (175, 0, 175), cv2.FILLED)                 cv2.putText(img, button.text, (x + 20, y + 65), cv2.FONT_HERSHEY_PLAIN, 4, (255, 255, 255), 4)                 l, _, _ =  detector.findDistance(8, 12, img, draw=False)

Теперь мы можем определять на какой кнопке находится указательный палец. Осталось реализовать клик и вывод текста.

Если считать, что кликом будет соединение указательного и среднего пальцев, то значит нам нужно считывать расстояние между точками 8 и 12.

if l < 40:                     cv2.rectangle(img, button.pos, (x + w, y + h), (0, 255, 0), cv2.FILLED)                     cv2.putText(img, button.text, (x + 20, y + 65), cv2.FONT_HERSHEY_PLAIN, 4, (255, 255, 255), 4)                     if button.text == "<":                         finalText = finalText[:-1]                         keyboard.press('\010')                     else:                         finalText += button.text                         keyboard.press(button.text)                     sleep(0.15)

Если клик не работает и вы не можете использовать клавиатуру, тогда замените 40 на большее значение, например на 50, и попробуйте снова:

if length < 40

Эта проблема возникает из-за того, что у каждого человека руки разные и расстояние между пальцами отличается?.

Если клик был совершен, то кнопка меняет свой цвет на зеленый (на мгновение). В специальном поле и в любом редакторе появляется символ, указанный на кнопке.

И финальный этап:

cv2.rectangle(img, (50, 710), (700, 610), (175, 0, 175), cv2.FILLED)     cv2.putText(img, finalText, (60, 690), cv2.FONT_HERSHEY_PLAIN, 5, (255, 255, 255), 5)                                cv2.imshow("Keyboard", img)     cv2.waitKey(1)

Здесь мы возвращаем кнопке первоначальный цвет, создаем поле, в котором будет выведен текст.

Можем проверять?:

https://github.com/paveldat/virtual_keyboard/blob/main/img/result_notepad.gif


ссылка на оригинал статьи https://habr.com/ru/articles/680060/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *