Практика OpenCV: управление жестами для достижения бесконтактной функции перетаскивания

前言: Привет всем, меня зовут Дрим. Сегодня давайте научимся использовать OpenCV для управления жестами и мгновенного улучшения опыта работы! Пересекая границы, появляется функция перетаскивания управления жестами OpenCV.

Вставьте сюда описание изображения

1. Знакомство с основными этапами и функциями библиотеки.

1. Основные шаги

Для реализации данного эксперимента основные этапы заключаются в следующем:

  1. Импортируйте библиотеку OpenCV.
  2. Прочитайте видеопоток камеры через OpenCV.
  3. Используйте алгоритмы определения цвета кожи, такие как преобразование цветового пространства и пороговая сегментация, для идентификации областей рук.
  4. Обнаружение контура выполняется на области руки, чтобы найти контур руки.
  5. По контуру руки получают пиксельные координаты ключевых точек пальца. Для жестов перетаскивания вы можете сосредоточиться на положении указательного и среднего пальцев.
  6. Рассчитайте расстояние между кончиками указательного и среднего пальцев и определите, выполняются ли условия для запуска действия перетаскивания.
  7. Если условия соблюдены, вы можете использовать теорему Пифагора для расчета расстояния и изменить цвет прямоугольной области, чтобы указать, что перетаскивание сработало.
  8. Обновите координаты прямоугольника в соответствии с положением пальца, чтобы прямоугольник повторял движение пальца.
  9. Остановите движение прямоугольника, когда отпустите палец.

2. Импортируйте необходимые модули.

# 导入OpenCV
import cv2
# 导入mediapipe
import mediapipe as mp

# 导入其他依赖包
import time
import math

3. Категория управления блоками

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

1.Инициализируйте менеджер блоков.

Инициализируйте диспетчер блоков, передайте длину блока (rect_width)в качестве параметра и инициализируйте такие свойства, как список блоков, расстояние, статус активации и идентификатор активированного блока .

class SquareManager:
    def __init__(self, rect_width):
        # 方框长度
        self.rect_width = rect_width

        # 方块列表
        self.square_count = 0
        self.rect_left_x_list = []
        self.rect_left_y_list = []
        self.alpha_list = []

        # 中指与矩形左上角点的距离
        self.L1 = 0
        self.L2 = 0

        # 激活移动模式
        self.drag_active = False

        # 激活的方块ID
        self.active_index = -1

2. Создайте блок

Создайте блок и добавьте координаты и прозрачность верхнего левого угла блока в соответствующие списки.

# 创建一个方块,但是没有显示
    def create(self, rect_left_x, rect_left_y, alpha=0.4):
        # 将方块的左上角坐标和透明度添加到相应的列表中
        self.rect_left_x_list.append(rect_left_x)
        self.rect_left_y_list.append(rect_left_y)
        self.alpha_list.append(alpha)
        self.square_count += 1

3. Обновите положение поля отображения.

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

 # 更新显示方块的位置
    def display(self, class_obj):
        # 遍历方块列表
        for i in range(0, self.square_count):
            x = self.rect_left_x_list[i]
            y = self.rect_left_y_list[i]
            alpha = self.alpha_list[i]

            overlay = class_obj.image.copy()
             # 如果方块处于激活状态,绘制紫色方块;否则绘制蓝色方块
            if (i == self.active_index):
                cv2.rectangle(overlay, (x, y), (x + self.rect_width, y + self.rect_width), (255, 0, 255), -1)
            else:
                cv2.rectangle(overlay, (x, y), (x + self.rect_width, y + self.rect_width), (255, 0, 0), -1)

            # 使用透明度将叠加图像叠加到原始图像上
            class_obj.image = cv2.addWeighted(overlay, alpha, class_obj.image, 1 - alpha, 0)

4. Определить точку приземления блока

Определите, находятся ли данные координаты внутри блока, и верните идентификатор блока.

    # 判断落在哪个方块上,返回方块的ID
    def checkOverlay(self, check_x, check_y):
        # 遍历方块列表
        for i in range(0, self.square_count):
            x = self.rect_left_x_list[i]
            y = self.rect_left_y_list[i]

            # 检查指定点是否在方块内
            if (x < check_x < (x + self.rect_width)) and (y < check_y < (y + self.rect_width)):
                # 保存被激活的方块ID
                self.active_index = i
                return i

        return -1

5. Рассчитайте расстояние и обновите местоположение.

Метод setLen: Рассчитайте расстояние между активированным блоком и кончиком пальца.
Метод updateSquare: обновляет положение активного квадрата на основе заданных новых координат.

    # 计算与指尖的距离
    def setLen(self, check_x, check_y):
        # 计算距离
        self.L1 = check_x - self.rect_left_x_list[self.active_index]
        self.L2 = check_y - self.rect_left_y_list[self.active_index]

    # 更新方块位置
    def updateSquare(self, new_x, new_y):
        self.rect_left_x_list[self.active_index] = new_x - self.L1
        self.rect_left_y_list[self.active_index] = new_y - self.L2

3. Определить категории контроля

1. Инициализируйте класс контроля распознавания.

class HandControlVolume:
    def __init__(self):
        # 初始化mediapipe
        self.mp_drawing = mp.solutions.drawing_utils
        self.mp_drawing_styles = mp.solutions.drawing_styles
        self.mp_hands = mp.solutions.hands

        # 中指与矩形左上角点的距离
        self.L1 = 0
        self.L2 = 0

        # image实例,以便另一个类调用
        self.image = None

HandControlVolumeИспользуется для инициализации mediapipeи 存储中指与矩形左上角点的距离和imageсоздания экземпляров.

  • __init__Метод: при инициализации объекта инициализируйте медиапайп, включая Drawing_utils, Drawing_styles и руки. Кроме того, также инициализируется расстояние между средним пальцем и верхним левым углом прямоугольника и экземпляром изображения.

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

2. Основная функция

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

    def recognize(self):
        # 计算刷新率
        fpsTime = time.time()

        # OpenCV读取视频流
        cap = cv2.VideoCapture(0)
        # 视频分辨率
        resize_w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        resize_h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

        # 画面显示初始化参数
        rect_percent_text = 0

        # 初始化方块管理器
        squareManager = SquareManager(150)

        # 创建多个方块
        for i in range(0, 5):
            squareManager.create(200 * i + 20, 200, 0.6)

        with self.mp_hands.Hands(min_detection_confidence=0.7,
                                 min_tracking_confidence=0.5,
                                 max_num_hands=2) as hands:
            while cap.isOpened():

                # 初始化矩形
                success, self.image = cap.read()
                self.image = cv2.resize(self.image, (resize_w, resize_h))

                if not success:
                    print("空帧.")
                    continue
  • resize_wи resize_h: ширина и высота видеокадра, полученные в соответствии с разрешением камеры и масштабированные как размер изображения для последующей обработки.

  • rect_percent_text: Параметр инициализации экрана, может использоваться для отображения текста на экране.

  • squareManager: Инициализирует экземпляр класса менеджера блоков и устанавливает длину блока равной 150.

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

3. Улучшение производительности и обработки изображений.

                self.image.flags.writeable = False
                # 转为RGB
                self.image = cv2.cvtColor(self.image, cv2.COLOR_BGR2RGB)
                # 镜像
                self.image = cv2.flip(self.image, 1)
                # mediapipe模型处理
                results = hands.process(self.image)

                self.image.flags.writeable = True
                self.image = cv2.cvtColor(self.image, cv2.COLOR_RGB2BGR)
  • self.image.flags.writeable = False: установите для образа запрет на запись, чтобы повысить производительность и избежать копирования данных.

  • self.image = cv2.cvtColor(self.image, cv2.COLOR_BGR2RGB): конвертировать изображения формата BGR в формат RGB, поскольку входное изображение, обрабатываемое моделью медиапайпа, должно быть в формате RGB.

  • self.image = cv2.flip(self.image, 1): зеркально отразить изображение, чтобы оно соответствовало положению руки, ожидаемому моделью медиапайпа.

  • results = hands.process(self.image): передать обработанное изображение в модель рук медиапайпа для распознавания и обработки жестов.

  • self.image = cv2.cvtColor(self.image, cv2.COLOR_RGB2BGR): конвертировать изображение из формата RGB обратно в формат BGR для последующего отображения и обработки.

4. Обнаруживайте ладони, отмечайте ключевые точки и связи.

                if results.multi_hand_landmarks:
                    # 遍历每个手掌
                    for hand_landmarks in results.multi_hand_landmarks:
                        # 在画面标注手指
                        self.mp_drawing.draw_landmarks(
                            self.image,
                            hand_landmarks,
                            self.mp_hands.HAND_CONNECTIONS,
                            self.mp_drawing_styles.get_default_hand_landmarks_style(),
                            self.mp_drawing_styles.get_default_hand_connections_style())
  • if results.multi_hand_landmarks:: проверьте, распознана ли ладонь. Если пальма обнаружена, перейдите к следующему шагу; в противном случае пропустите.

  • for hand_landmarks in results.multi_hand_landmarks:: Пройдите по каждой обнаруженной ладони.

  • self.mp_drawing.draw_landmarks: используйте метод медиапайпа draw_landmarks, чтобы отметить ключевые точки и места соединения пальцев на изображении.

self.image: Введите изображение. hand_landmarks: Ключевые точки ладони.

self.mp_hands.HAND_CONNECTIONS: Соединение между пальцами.

5. Разберите обнаруженную ладонь и извлеките ключевые точки пальцев.

Обнаружьте ладонь и извлеките ключевые точки пальцев, а затем сохраните координаты пальцев.

                        landmark_list = []

                        # 用来存储手掌范围的矩形坐标
                        paw_x_list = []
                        paw_y_list = []
                        for landmark_id, finger_axis in enumerate(
                                hand_landmarks.landmark):
                            landmark_list.append([
                                landmark_id, finger_axis.x, finger_axis.y,
                                finger_axis.z
                            ])
                            paw_x_list.append(finger_axis.x)
                            paw_y_list.append(finger_axis.y)
                        if landmark_list:
                            # 比例缩放到像素
                            ratio_x_to_pixel = lambda x: math.ceil(x * resize_w)
                            ratio_y_to_pixel = lambda y: math.ceil(y * resize_h)

                            # 设计手掌左上角、右下角坐标
                            paw_left_top_x, paw_right_bottom_x = map(ratio_x_to_pixel,
                                                                     [min(paw_x_list), max(paw_x_list)])
                            paw_left_top_y, paw_right_bottom_y = map(ratio_y_to_pixel,
                                                                     [min(paw_y_list), max(paw_y_list)])

                            # 给手掌画框框
                            cv2.rectangle(self.image, (paw_left_top_x - 30, paw_left_top_y - 30),
                                          (paw_right_bottom_x + 30, paw_right_bottom_y + 30), (0, 255, 0), 2)

                            # 获取中指指尖坐标
                            middle_finger_tip = landmark_list[12]
                            middle_finger_tip_x = ratio_x_to_pixel(middle_finger_tip[1])
                            middle_finger_tip_y = ratio_y_to_pixel(middle_finger_tip[2])

                            # 获取食指指尖坐标
                            index_finger_tip = landmark_list[8]
                            index_finger_tip_x = ratio_x_to_pixel(index_finger_tip[1])
                            index_finger_tip_y = ratio_y_to_pixel(index_finger_tip[2])
                            # 中间点
                            between_finger_tip = (middle_finger_tip_x + index_finger_tip_x) // 2, (
                                        middle_finger_tip_y + index_finger_tip_y) // 2

                            thumb_finger_point = (middle_finger_tip_x, middle_finger_tip_y)
                            index_finger_point = (index_finger_tip_x, index_finger_tip_y)

  • landmark_list: список, используемый для хранения информации о ключевых точках пальца.

  • paw_x_listи paw_y_list: горизонтальные и вертикальные координаты прямоугольного поля, используемого для хранения диапазона ладони.

  • В цикле индекс, координата x, координата y и координата z ключевой точки каждого пальца сохраняются в landmark_list, а горизонтальные и вертикальные координаты диапазона ладони сохраняются в paw_x_listи paw_y_list.

Если landmark_listон не пуст, то есть обнаруживается ключевая точка пальца ratio_x_to_pixelи ratio_y_to_pixel: две лямбда-функции, функции, используемые для преобразования относительных масштабов в координаты пикселей. На основании прямоугольных координат области ладони вычислите координаты верхнего левого угла и правого нижнего угла области ладони и нарисуйте прямоугольник. Используйте landmark_listэту информацию для получения координат кончика среднего пальца и координат кончика указательного пальца и преобразуйте их в координаты пикселей. Рассчитайте среднюю точку между координатами кончика среднего пальца и координатами кончика указательного пальца. Сохраните координаты кончика среднего пальца и координаты кончика указательного пальца в thumb_finger_pointсумме index_finger_point.

Анализируйте обнаруженную информацию о ладони, извлекайте координаты ключевых точек пальцев, преобразуйте координаты пальцев в координаты пикселей и отмечайте положения кончиков среднего и указательного пальцев на изображении.

6. Нарисуйте кончиками пальцев круги и соединительные линии, чтобы рассчитать расстояние.

                            circle_func = lambda point: cv2.circle(self.image, point, 10, (255, 0, 255), -1)
                            self.image = circle_func(thumb_finger_point)
                            self.image = circle_func(index_finger_point)
                            self.image = circle_func(between_finger_tip)
                            # 画2点连线
                            self.image = cv2.line(self.image, thumb_finger_point, index_finger_point, (255, 0, 255), 5)
                            # 勾股定理计算长度
                            line_len = math.hypot((index_finger_tip_x - middle_finger_tip_x),
                                                  (index_finger_tip_y - middle_finger_tip_y))
                            # 将指尖距离映射到文字
                            rect_percent_text = math.ceil(line_len)
  • cv2.lineФункция, которая рисует соединительную линию между кончиком среднего и кончиком указательного пальца на изображении.

  • math.hypotФункция вычисляет длину гипотенузы прямоугольного треугольника.

  • Сопоставьте расстояние между кончиками пальцев с rect_percent_textпеременной, которая будет использоваться в качестве параметра для последующего отображения текста.

7. Отслеживайте расстояние между пальцами

                            if squareManager.drag_active:
                                # 更新方块
                                squareManager.updateSquare(between_finger_tip[0], between_finger_tip[1])
                                if (line_len > 100):
                                    # 取消激活
                                    squareManager.drag_active = False
                                    squareManager.active_index = -1

                            elif (line_len < 100) and (squareManager.checkOverlay(between_finger_tip[0],
                                                                                  between_finger_tip[1]) != -1) and (
                                    squareManager.drag_active == False):
                                # 激活
                                squareManager.drag_active = True
                                # 计算距离
                                squareManager.setLen(between_finger_tip[0], between_finger_tip[1])

Если squareManagerсвойство drag_activeимеет значение True, то есть режим перемещения прямоугольника активирован, используйте squareManager.updateSquareметод для обновления положения прямоугольника. Если расстояние между двумя пальцами больше 100, то есть расстояние между пальцами превышает порог, режим активации прямоугольника отменяется, которому будет drag_activeприсвоено значение False и будет active_indexустановлено значение -1.

В противном случае, если расстояние между двумя пальцами меньше 100 и между пальцами имеется перекрывающийся прямоугольник, режим перемещения прямоугольника не активируется. Чтобы активировать режим перемещения прямоугольника, drag_activeустановите значение True. Рассчитайте и установите длину прямоугольника, исходя из расстояния между пальцами, используйте squareManager.setLenметод.

8. Отображение изображения

                squareManager.display(self)

                # 显示距离
                cv2.putText(self.image, "Distance:" + str(rect_percent_text), (10, 120), cv2.FONT_HERSHEY_PLAIN, 3,
                            (255, 0, 0), 3)

                # 显示当前激活
                cv2.putText(self.image, "Active:" + (
                    "None" if squareManager.active_index == -1 else str(squareManager.active_index)), (10, 170),
                            cv2.FONT_HERSHEY_PLAIN, 3, (255, 0, 0), 3)

                # 显示刷新率FPS
                cTime = time.time()
                fps_text = 1 / (cTime - fpsTime)
                fpsTime = cTime
                cv2.putText(self.image, "FPS: " + str(int(fps_text)), (10, 70),
                            cv2.FONT_HERSHEY_PLAIN, 3, (255, 0, 0), 3)
                # 显示画面
                cv2.imshow('virtual drag and drop', self.image)

                if cv2.waitKey(5) & 0xFF == 27:
                    break
            cap.release()

control = HandControlVolume()
control.recognize()

recognizeКонец основной функции ( ) используется для отображения статуса и частоты обновления изображения, прямоугольника и ожидания ответа клавиши. Используйте squareManager.displayметод для отображения прямоугольника. cv2.waitKeyФункция ожидает ввода клавиши. Если нажата клавиша ESC (соответствующий код ASCII — 27), она выходит из цикла.

Отобразите обработанное изображение, состояние прямоугольника и частоту обновления на экране и дождитесь ответа клавиши. Это обеспечивает интерактивную функцию виртуального перетаскивания. Далее давайте посмотрим на фактический эффект операции.

4. Практическая демонстрация

Вставьте сюда описание изображения

Вставьте сюда описание изображения
Вставьте сюда описание изображения

Благодаря демонстрации мы можем перетаскивать блоки руками, и эффекта можно достичь в хорошем состоянии.

5. Совместное использование исходного кода

import cv2
import mediapipe as mp
import time
import math
class SquareManager:
    def __init__(self, rect_width):

        # 方框长度
        self.rect_width = rect_width

        # 方块list
        self.square_count = 0
        self.rect_left_x_list = []
        self.rect_left_y_list = []
        self.alpha_list = []

        # 中指与矩形左上角点的距离
        self.L1 = 0
        self.L2 = 0

        # 激活移动模式
        self.drag_active = False

        # 激活的方块ID
        self.active_index = -1

    # 创建一个方块,但是没有显示
    def create(self, rect_left_x, rect_left_y, alpha=0.4):
        self.rect_left_x_list.append(rect_left_x)
        self.rect_left_y_list.append(rect_left_y)
        self.alpha_list.append(alpha)
        self.square_count += 1

    # 更新位置
    def display(self, class_obj):
        for i in range(0, self.square_count):
            x = self.rect_left_x_list[i]
            y = self.rect_left_y_list[i]
            alpha = self.alpha_list[i]

            overlay = class_obj.image.copy()

            if (i == self.active_index):
                cv2.rectangle(overlay, (x, y), (x + self.rect_width, y + self.rect_width), (255, 0, 255), -1)
            else:
                cv2.rectangle(overlay, (x, y), (x + self.rect_width, y + self.rect_width), (255, 0, 0), -1)

            # Following line overlays transparent rectangle over the self.image
            class_obj.image = cv2.addWeighted(overlay, alpha, class_obj.image, 1 - alpha, 0)

    # 判断落在哪个方块上,返回方块的ID
    def checkOverlay(self, check_x, check_y):
        for i in range(0, self.square_count):
            x = self.rect_left_x_list[i]
            y = self.rect_left_y_list[i]

            if (x < check_x < (x + self.rect_width)) and (y < check_y < (y + self.rect_width)):
                # 保存被激活的方块ID
                self.active_index = i

                return i

        return -1

    # 计算与指尖的距离
    def setLen(self, check_x, check_y):
        # 计算距离
        self.L1 = check_x - self.rect_left_x_list[self.active_index]
        self.L2 = check_y - self.rect_left_y_list[self.active_index]

    # 更新方块    
    def updateSquare(self, new_x, new_y):
        # print(self.rect_left_x_list[self.active_index])
        self.rect_left_x_list[self.active_index] = new_x - self.L1
        self.rect_left_y_list[self.active_index] = new_y - self.L2


# 识别控制类
class HandControlVolume:
    def __init__(self):
        # 初始化medialpipe
        self.mp_drawing = mp.solutions.drawing_utils
        self.mp_drawing_styles = mp.solutions.drawing_styles
        self.mp_hands = mp.solutions.hands

        # 中指与矩形左上角点的距离
        self.L1 = 0
        self.L2 = 0

        # image实例,以便另一个类调用
        self.image = None

    # 主函数
    def recognize(self):
        # 计算刷新率
        fpsTime = time.time()

        # OpenCV读取视频流
        cap = cv2.VideoCapture(0)
        # 视频分辨率
        resize_w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        resize_h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

        # 画面显示初始化参数
        rect_percent_text = 0

        # 初始化方块管理器
        squareManager = SquareManager(150)

        # 创建多个方块
        for i in range(0, 5):
            squareManager.create(200 * i + 20, 200, 0.6)

        with self.mp_hands.Hands(min_detection_confidence=0.7,
                                 min_tracking_confidence=0.5,
                                 max_num_hands=2) as hands:
            while cap.isOpened():

                # 初始化矩形
                success, self.image = cap.read()
                self.image = cv2.resize(self.image, (resize_w, resize_h))

                if not success:
                    print("空帧.")
                    continue

                # 提高性能
                self.image.flags.writeable = False
                # 转为RGB
                self.image = cv2.cvtColor(self.image, cv2.COLOR_BGR2RGB)
                # 镜像
                self.image = cv2.flip(self.image, 1)
                # mediapipe模型处理
                results = hands.process(self.image)

                self.image.flags.writeable = True
                self.image = cv2.cvtColor(self.image, cv2.COLOR_RGB2BGR)
                # 判断是否有手掌
                if results.multi_hand_landmarks:
                    # 遍历每个手掌
                    for hand_landmarks in results.multi_hand_landmarks:
                        # 在画面标注手指
                        self.mp_drawing.draw_landmarks(
                            self.image,
                            hand_landmarks,
                            self.mp_hands.HAND_CONNECTIONS,
                            self.mp_drawing_styles.get_default_hand_landmarks_style(),
                            self.mp_drawing_styles.get_default_hand_connections_style())

                        # 解析手指,存入各个手指坐标
                        landmark_list = []

                        # 用来存储手掌范围的矩形坐标
                        paw_x_list = []
                        paw_y_list = []
                        for landmark_id, finger_axis in enumerate(
                                hand_landmarks.landmark):
                            landmark_list.append([
                                landmark_id, finger_axis.x, finger_axis.y,
                                finger_axis.z
                            ])
                            paw_x_list.append(finger_axis.x)
                            paw_y_list.append(finger_axis.y)
                        if landmark_list:
                            # 比例缩放到像素
                            ratio_x_to_pixel = lambda x: math.ceil(x * resize_w)
                            ratio_y_to_pixel = lambda y: math.ceil(y * resize_h)

                            # 设计手掌左上角、右下角坐标
                            paw_left_top_x, paw_right_bottom_x = map(ratio_x_to_pixel,
                                                                     [min(paw_x_list), max(paw_x_list)])
                            paw_left_top_y, paw_right_bottom_y = map(ratio_y_to_pixel,
                                                                     [min(paw_y_list), max(paw_y_list)])

                            # 给手掌画框框
                            cv2.rectangle(self.image, (paw_left_top_x - 30, paw_left_top_y - 30),
                                          (paw_right_bottom_x + 30, paw_right_bottom_y + 30), (0, 255, 0), 2)

                            # 获取中指指尖坐标
                            middle_finger_tip = landmark_list[12]
                            middle_finger_tip_x = ratio_x_to_pixel(middle_finger_tip[1])
                            middle_finger_tip_y = ratio_y_to_pixel(middle_finger_tip[2])

                            # 获取食指指尖坐标
                            index_finger_tip = landmark_list[8]
                            index_finger_tip_x = ratio_x_to_pixel(index_finger_tip[1])
                            index_finger_tip_y = ratio_y_to_pixel(index_finger_tip[2])
                            # 中间点
                            between_finger_tip = (middle_finger_tip_x + index_finger_tip_x) // 2, (
                                        middle_finger_tip_y + index_finger_tip_y) // 2
                            # print(middle_finger_tip_x)
                            thumb_finger_point = (middle_finger_tip_x, middle_finger_tip_y)
                            index_finger_point = (index_finger_tip_x, index_finger_tip_y)
                            # 画指尖2点
                            circle_func = lambda point: cv2.circle(self.image, point, 10, (255, 0, 255), -1)
                            self.image = circle_func(thumb_finger_point)
                            self.image = circle_func(index_finger_point)
                            self.image = circle_func(between_finger_tip)
                            # 画2点连线
                            self.image = cv2.line(self.image, thumb_finger_point, index_finger_point, (255, 0, 255), 5)
                            # 勾股定理计算长度
                            line_len = math.hypot((index_finger_tip_x - middle_finger_tip_x),
                                                  (index_finger_tip_y - middle_finger_tip_y))
                            # 将指尖距离映射到文字
                            rect_percent_text = math.ceil(line_len)

                            # 激活模式,需要让矩形跟随移动
                            if squareManager.drag_active:
                                # 更新方块
                                squareManager.updateSquare(between_finger_tip[0], between_finger_tip[1])
                                if (line_len > 100):
                                    # 取消激活
                                    squareManager.drag_active = False
                                    squareManager.active_index = -1

                            elif (line_len < 100) and (squareManager.checkOverlay(between_finger_tip[0],
                                                                                  between_finger_tip[1]) != -1) and (
                                    squareManager.drag_active == False):
                                # 激活
                                squareManager.drag_active = True
                                # 计算距离
                                squareManager.setLen(between_finger_tip[0], between_finger_tip[1])

                # 显示方块,传入本实例,主要为了半透明的处理
                squareManager.display(self)

                # 显示距离
                cv2.putText(self.image, "Distance:" + str(rect_percent_text), (10, 120), cv2.FONT_HERSHEY_PLAIN, 3,
                            (255, 0, 0), 3)

                # 显示当前激活
                cv2.putText(self.image, "Active:" + (
                    "None" if squareManager.active_index == -1 else str(squareManager.active_index)), (10, 170),
                            cv2.FONT_HERSHEY_PLAIN, 3, (255, 0, 0), 3)

                # 显示刷新率FPS
                cTime = time.time()
                fps_text = 1 / (cTime - fpsTime)
                fpsTime = cTime
                cv2.putText(self.image, "FPS: " + str(int(fps_text)), (10, 70),
                            cv2.FONT_HERSHEY_PLAIN, 3, (255, 0, 0), 3)
                # 显示画面
                cv2.imshow('virtual drag and drop', self.image)

                if cv2.waitKey(5) & 0xFF == 27:
                    break
            cap.release()

control = HandControlVolume()
control.recognize()

Supongo que te gusta

Origin blog.csdn.net/weixin_51390582/article/details/135705981
Recomendado
Clasificación