Drag-and-Drop на Python+OpenCV

от автора

В данной статье расскажу про простой Drag-and-Drop на Python+OpenCV.

Немного теории, ведь в наше время без нее никуда?

Drag-and-drop () — способ оперирования элементами интерфейса в интерфейсах пользователя (как графическим, так и текстовым, где элементы GUI реализованы при помощи псевдографики) при помощи манипулятора «мышь» или сенсорного экрана.

Простыми словами:

Drag-and-Drop - это возможность захватить мышью элемент и перенести его.

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

Тут, я разберу весь код с нуля, но для полной ясности лучше прочитать мои предыдущие статьи?

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

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

pip install opencv-python pip install numpy pip install cvzone

В первую очередь создаем новый файл `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()

Данный класс мы использовали в Управление громкостью звука жестами на Python.

Можем создать новый файл, назовем его main.py и будем писать всю логику.

Импортируем библиотеки:

import cv2 import HandTrackingModule as htm import cvzone import numpy as np

Запускаем камеру и присваиваем глобальным переменным значения:

cap = cv2.VideoCapture(0) cap.set(3, 1280) cap.set(4, 720) detector = htm.handDetector(detectionCon=0.8) colorR = (255, 0, 255)  cx, cy, w, h = 100, 100, 200, 200

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

cx, cy - координаты левой верхней точки нашего четырехугольника w, h - ширина и длина (в нашем случае будет квадрат)

Напишем класс, для более удобной дальнейшей работы с блоками:

class DragAndDrogRectangle():     def __init__(self, posCenter, size=[200, 200]):         self.posCenter = posCenter         self.size = size      def update(self, cursor):         cx, cy = self.posCenter         w, h = self.size          # If the index finger tip is in rectangle region         if cx - w//2 < cursor[0] < cx + w//2 and cy - h//2 < cursor[1] < cy + h//2:             self.posCenter = cursor
rectList = [] for x in range(5):     rectList.append(DragAndDrogRectangle([x*250+150, 150]))

Таким образом у нас будет не один квадрат, а 5. При необходимости можно добавлять или удалять их.

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

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

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

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

if len(lmList) != 0:         length, _, _ = detector.findDistance(8, 12, img, draw=False)         if length < 40:             cursor = lmList[8][1:] # index finger tip landmark             # call the update             for rect in rectList:                 rect.update(cursor)          # Draw     imgNew = np.zeros_like(img, np.uint8)     for rect in rectList:         cx, cy = rect.posCenter         w, h = rect.size         cv2.rectangle(imgNew, (cx - w//2, cy - h//2), (cx + w//2, cy + h//2), colorR, cv2.FILLED)         cvzone.cornerRect(imgNew, (cx - w//2, cy - h//2, w, h), 20, rt=0)      out = img.copy()     alpha = 0.1     mask = imgNew.astype(bool)     out[mask] = cv2.addWeighted(img, alpha, imgNew, 1-alpha, 0)[mask]      cv2.imshow("Image", out)     cv2.waitKey(1)

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

if length < 40

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

Если хотите поменять клик или указатель на другие пальцы, то на изображении «Hand Land Marks» можно выбрать те точки, которые будут за это отвечать. После чего необходимо поменять эти строки:

length, _, _ = detector.findDistance(8, 12, img, draw=False)

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

cursor = lmList[8][1:] # index finger tip landmark

Вместо 8, вписываем ту точку, которая будет служить указателем (представим, что это курсор мыши).

Для перемещения квадратов (блоков) нужно удерживать клик, т.е. два пальца должны быть соединены все время, пока мы хотим перемещать тот или иной объект.

Запускаем программу и тестируем?:

Ура! Все работает!

Вместо квадратов вы сможете добавить необходимые блоки и использовать Drag-and-Drop для работы с ними =)


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


Комментарии

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

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