欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

OpenCV實(shí)現(xiàn)手勢(shì)虛擬拖拽的使用示例(附demo)

 更新時(shí)間:2023年11月09日 16:37:43   作者:是Dream呀  
本文主要介紹了OpenCV實(shí)現(xiàn)手勢(shì)虛擬拖拽的使用示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

一、主要步驟及庫(kù)的功能介紹

1.主要步驟

要實(shí)現(xiàn)本次實(shí)驗(yàn),主要步驟如下:

  • 導(dǎo)入OpenCV庫(kù)。
  • 通過(guò)OpenCV讀取攝像頭的視頻流。
  • 使用膚色檢測(cè)算法(如色彩空間轉(zhuǎn)換和閾值分割)來(lái)識(shí)別手部區(qū)域。
  • 對(duì)手部區(qū)域進(jìn)行輪廓檢測(cè),找到手的輪廓。
  • 根據(jù)手的輪廓,獲取手指關(guān)鍵點(diǎn)的像素坐標(biāo)。對(duì)于拖拽手勢(shì),可以關(guān)注食指和中指的位置。
  • 計(jì)算食指和中指指尖之間的距離并判斷是否滿足條件觸發(fā)拖拽動(dòng)作。
  • 如果滿足條件,可以使用勾股定理計(jì)算距離,并將矩形區(qū)域變色以示觸發(fā)拖拽。
  • 根據(jù)手指的位置更新矩形的坐標(biāo),使矩形跟隨手指運(yùn)動(dòng)。
  • 當(dāng)手指放開(kāi)時(shí)停止矩形的移動(dòng)。

2.需要的庫(kù)介紹

導(dǎo)入的庫(kù)在實(shí)現(xiàn)手勢(shì)虛擬拖拽的代碼中起著重要的作用,下面是對(duì)每個(gè)庫(kù)的簡(jiǎn)要介紹:

  • OpenCV (cv2): OpenCV是一個(gè)開(kāi)源的計(jì)算機(jī)視覺(jué)庫(kù),提供了豐富的圖像和視頻處理功能,使用OpenCV來(lái)讀取攝像頭視頻流、進(jìn)行圖像處理和計(jì)算。

  • mediapipe (mp): mediapipe提供一系列預(yù)訓(xùn)練的機(jī)器學(xué)習(xí)模型和工具,用于實(shí)現(xiàn)計(jì)算機(jī)視覺(jué)和機(jī)器學(xué)習(xí)任務(wù)。我們使用mediapipe來(lái)進(jìn)行手部關(guān)鍵點(diǎn)檢測(cè)和姿勢(shì)估計(jì)。

  • time: 我們使用time庫(kù)來(lái)計(jì)時(shí)和進(jìn)行時(shí)間相關(guān)的操作。

  • math: 在本次實(shí)驗(yàn)我們使用math庫(kù)來(lái)計(jì)算距離和角度

二、導(dǎo)入所需要的模塊

# 導(dǎo)入OpenCV
import cv2
# 導(dǎo)入mediapipe
import mediapipe as mp

# 導(dǎo)入其他依賴包
import time
import math

三、方塊管理類

(SquareManager)是一個(gè)方塊管理器,用于創(chuàng)建、顯示、更新和處理方塊的相關(guān)操作。

1.初始化方塊管理器

初始化方塊管理器,傳入方塊的長(zhǎng)度(rect_width)作為參數(shù),并初始化方塊列表、距離、激活狀態(tài)和激活的方塊ID等屬性。

class SquareManager:
    def __init__(self, rect_width):
        # 方框長(zhǎng)度
        self.rect_width = rect_width

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

        # 中指與矩形左上角點(diǎn)的距離
        self.L1 = 0
        self.L2 = 0

        # 激活移動(dòng)模式
        self.drag_active = False

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

2.創(chuàng)建一個(gè)方塊

創(chuàng)建一個(gè)方塊,將方塊的左上角坐標(biāo)和透明度添加到相應(yīng)的列表中。

# 創(chuàng)建一個(gè)方塊,但是沒(méi)有顯示
    def create(self, rect_left_x, rect_left_y, alpha=0.4):
        # 將方塊的左上角坐標(biāo)和透明度添加到相應(yīng)的列表中
        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.更新顯示方塊的位置

根據(jù)方塊的狀態(tài),在圖像上繪制方塊,并使用透明度將疊加圖像疊加到原始圖像上。

 # 更新顯示方塊的位置
    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()
             # 如果方塊處于激活狀態(tài),繪制紫色方塊;否則繪制藍(lán)色方塊
            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.判斷落點(diǎn)方塊

判斷給定的坐標(biāo)是否在方塊內(nèi),并返回方塊的ID。

    # 判斷落在哪個(gè)方塊上,返回方塊的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]

            # 檢查指定點(diǎn)是否在方塊內(nè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.計(jì)算距離、更新位置

??setLen? 方法:計(jì)算激活方塊與指尖的距離。
??updateSquare? 方法:根據(jù)給定的新坐標(biāo)更新激活方塊的位置。

    # 計(jì)算與指尖的距離
    def setLen(self, check_x, check_y):
        # 計(jì)算距離
        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

三、識(shí)別控制類

1.初始化識(shí)別控制類

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

        # 中指與矩形左上角點(diǎn)的距離
        self.L1 = 0
        self.L2 = 0

        # image實(shí)例,以便另一個(gè)類調(diào)用
        self.image = None

HandControlVolume用于初始化mediapipe以及存儲(chǔ)中指與矩形左上角點(diǎn)的距離和image實(shí)例。

  • __init__ 方法:在初始化對(duì)象時(shí),初始化mediapipe,包括drawing_utils、drawing_styles和hands。此外,還初始化了中指與矩形左上角點(diǎn)的距離和image實(shí)例。

通過(guò)mediapipe,可以進(jìn)行手部關(guān)鍵點(diǎn)檢測(cè)和姿勢(shì)估計(jì),進(jìn)而進(jìn)行手勢(shì)識(shí)別和處理。為了使其他類能夠調(diào)用image實(shí)例,將其作為該類的屬性進(jìn)行存儲(chǔ),方便地處理手勢(shì)識(shí)別和控制操作。

2.主函數(shù)

這部分代碼主要用于初始化和準(zhǔn)備處理視頻流以進(jìn)行手勢(shì)識(shí)別和交互。

    def recognize(self):
        # 計(jì)算刷新率
        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))

        # 畫(huà)面顯示初始化參數(shù)
        rect_percent_text = 0

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

        # 創(chuàng)建多個(gè)方塊
        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:根據(jù)攝像頭分辨率獲取的視頻幀的寬度和高度,并作為后續(xù)處理的圖像尺寸進(jìn)行縮放。

  • rect_percent_text:畫(huà)面顯示初始化參數(shù),可能被用于屏幕上的文本顯示。

  • squareManager:初始化了方塊管理器類的實(shí)例,并設(shè)置方塊的長(zhǎng)度為150。

使用一個(gè)循環(huán),創(chuàng)建了五個(gè)方塊,并通過(guò)create方法將其添加到方塊管理器中。進(jìn)入循環(huán),從視頻流中讀取幀圖像,并將其調(diào)整為指定的尺寸。如果成功讀取幀圖像,則會(huì)進(jìn)一步處理,否則將輸出錯(cuò)誤消息。

3.提高性能和處理圖像

                self.image.flags.writeable = False
                # 轉(zhuǎn)為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:設(shè)置圖像為不可寫(xiě),以提高性能并避免數(shù)據(jù)拷貝。

  • self.image = cv2.cvtColor(self.image, cv2.COLOR_BGR2RGB):將BGR格式的圖像轉(zhuǎn)換為RGB格式,因?yàn)閙ediapipe模型處理的輸入圖像需要是RGB格式。

  • self.image = cv2.flip(self.image, 1):將圖像進(jìn)行鏡像翻轉(zhuǎn),以與mediapipe模型期望的手部位置對(duì)應(yīng)。

  • results = hands.process(self.image):將處理后的圖像傳遞給mediapipe的hands模型,進(jìn)行手勢(shì)識(shí)別和處理。

  • self.image = cv2.cvtColor(self.image, cv2.COLOR_RGB2BGR):將圖像從RGB格式轉(zhuǎn)換回BGR格式,以便后續(xù)的顯示和處理。

4.檢測(cè)手掌,標(biāo)記關(guān)鍵點(diǎn)和連接關(guān)系

                if results.multi_hand_landmarks:
                    # 遍歷每個(gè)手掌
                    for hand_landmarks in results.multi_hand_landmarks:
                        # 在畫(huà)面標(biāo)注手指
                        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::檢查是否檢測(cè)到手掌。如果有檢測(cè)到手掌,則進(jìn)入下一步處理;否則跳過(guò)。

  • for hand_landmarks in results.multi_hand_landmarks::遍歷檢測(cè)到的每個(gè)手掌。

  • self.mp_drawing.draw_landmarks:使用mediapipe的draw_landmarks方法,在圖像上標(biāo)記手指的關(guān)鍵點(diǎn)和連接關(guān)系。

self.image:輸入的圖像。hand_landmarks:手掌的關(guān)鍵點(diǎn)。

self.mp_hands.HAND_CONNECTIONS:手指之間的連接關(guān)系。

5.解析檢測(cè)到的手掌并提取手指的關(guān)鍵點(diǎn)

檢測(cè)到的手掌并提取手指的關(guān)鍵點(diǎn),然后將手指的坐標(biāo)存儲(chǔ)起來(lái)。

                        landmark_list = []

                        # 用來(lái)存儲(chǔ)手掌范圍的矩形坐標(biāo)
                        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)

                            # 設(shè)計(jì)手掌左上角、右下角坐標(biāo)
                            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)])

                            # 給手掌畫(huà)框框
                            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)

                            # 獲取中指指尖坐標(biāo)
                            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])

                            # 獲取食指指尖坐標(biāo)
                            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])
                            # 中間點(diǎn)
                            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:一個(gè)列表,用于存儲(chǔ)手指的關(guān)鍵點(diǎn)信息。

  • paw_x_list 和 paw_y_list:用于存儲(chǔ)手掌范圍的矩形框的橫縱坐標(biāo)。

  • 在循環(huán)中,將每個(gè)手指的關(guān)鍵點(diǎn)的索引、x坐標(biāo)、y坐標(biāo)和z坐標(biāo)存儲(chǔ)在landmark_list中,同時(shí)將手掌范圍的橫縱坐標(biāo)存儲(chǔ)在paw_x_listpaw_y_list中。

如果landmark_list不為空,即有手指的關(guān)鍵點(diǎn)被檢測(cè)到ratio_x_to_pixel 和 ratio_y_to_pixel:兩個(gè)lambda函數(shù),用于將相對(duì)比例轉(zhuǎn)換為像素坐標(biāo)的函數(shù)。根據(jù)手掌范圍的矩形坐標(biāo),計(jì)算手掌區(qū)域的左上角和右下角坐標(biāo),并畫(huà)出方框。使用landmark_list中的信息獲取中指指尖坐標(biāo)和食指指尖坐標(biāo),并將它們轉(zhuǎn)換為像素坐標(biāo)。計(jì)算中指指尖坐標(biāo)和食指指尖坐標(biāo)的中間點(diǎn)。將中指指尖的坐標(biāo)和食指指尖的坐標(biāo)存儲(chǔ)在thumb_finger_pointindex_finger_point中。

解析檢測(cè)到的手掌信息,并提取手指的關(guān)鍵點(diǎn)坐標(biāo),將手指坐標(biāo)轉(zhuǎn)換為像素坐標(biāo),并將中指指尖和食指指尖的位置標(biāo)記在圖像上。

6.繪制指尖圓圈和連接線,計(jì)算距離

                            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)
                            # 畫(huà)2點(diǎn)連線
                            self.image = cv2.line(self.image, thumb_finger_point, index_finger_point, (255, 0, 255), 5)
                            # 勾股定理計(jì)算長(zhǎng)度
                            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函數(shù),在圖像上繪制中指指尖和食指指尖之間的連接線。

  • math.hypot函數(shù)計(jì)算直角三角形斜邊的長(zhǎng)度。

  • 將指尖之間的距離映射到rect_percent_text變量中,用作后續(xù)文本顯示的參數(shù)。

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
                                # 計(jì)算距離
                                squareManager.setLen(between_finger_tip[0], between_finger_tip[1])

如果squareManagerdrag_active屬性為True,即矩形的移動(dòng)模式已經(jīng)激活,使用squareManager.updateSquare方法更新矩形的位置。如果兩個(gè)手指之間的距離大于100,即手指之間的距離超過(guò)了閾值,取消矩形的激活模式,將drag_active設(shè)置為False,將active_index設(shè)置為-1。

否則,如果兩個(gè)手指之間的距離小于100,且手指之間存在重疊的矩形,并且矩形的移動(dòng)模式未激活。激活矩形的移動(dòng)模式,將drag_active設(shè)置為True。根據(jù)手指之間的距離,計(jì)算并設(shè)置矩形的長(zhǎng)度,使用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)

                # 顯示當(dāng)前激活
                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)
                # 顯示畫(huà)面
                cv2.imshow('virtual drag and drop', self.image)

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

control = HandControlVolume()
control.recognize()

主函數(shù)(recognize)的結(jié)尾部分,用于顯示圖像、矩形的狀態(tài)和刷新率,并等待按鍵響應(yīng)。使用squareManager.display方法顯示矩形。cv2.waitKey函數(shù)等待按鍵輸入,如果按下的鍵是ESC鍵(對(duì)應(yīng)的ASCII碼為27),則退出循環(huán)。

在屏幕上顯示處理后的圖像、矩形的狀態(tài)和刷新率,并等待按鍵響應(yīng)。這樣可以實(shí)現(xiàn)交互式的虛擬拖放功能。接下來(lái)我們看一下實(shí)際的操作效果。

四、實(shí)戰(zhàn)演示

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

通過(guò)演示我們可以實(shí)現(xiàn)通過(guò)手部對(duì)方塊進(jìn)行拖拽,效果可以達(dá)到良好的狀態(tài)。

五、源碼分享

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

        # 方框長(zhǎng)度
        self.rect_width = rect_width

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

        # 中指與矩形左上角點(diǎn)的距離
        self.L1 = 0
        self.L2 = 0

        # 激活移動(dòng)模式
        self.drag_active = False

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

    # 創(chuàng)建一個(gè)方塊,但是沒(méi)有顯示
    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)

    # 判斷落在哪個(gè)方塊上,返回方塊的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

    # 計(jì)算與指尖的距離
    def setLen(self, check_x, check_y):
        # 計(jì)算距離
        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


# 識(shí)別控制類
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

        # 中指與矩形左上角點(diǎn)的距離
        self.L1 = 0
        self.L2 = 0

        # image實(shí)例,以便另一個(gè)類調(diào)用
        self.image = None

    # 主函數(shù)
    def recognize(self):
        # 計(jì)算刷新率
        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))

        # 畫(huà)面顯示初始化參數(shù)
        rect_percent_text = 0

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

        # 創(chuàng)建多個(gè)方塊
        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
                # 轉(zhuǎn)為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:
                    # 遍歷每個(gè)手掌
                    for hand_landmarks in results.multi_hand_landmarks:
                        # 在畫(huà)面標(biāo)注手指
                        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())

                        # 解析手指,存入各個(gè)手指坐標(biāo)
                        landmark_list = []

                        # 用來(lái)存儲(chǔ)手掌范圍的矩形坐標(biāo)
                        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)

                            # 設(shè)計(jì)手掌左上角、右下角坐標(biāo)
                            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)])

                            # 給手掌畫(huà)框框
                            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)

                            # 獲取中指指尖坐標(biāo)
                            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])

                            # 獲取食指指尖坐標(biāo)
                            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])
                            # 中間點(diǎn)
                            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)
                            # 畫(huà)指尖2點(diǎn)
                            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)
                            # 畫(huà)2點(diǎn)連線
                            self.image = cv2.line(self.image, thumb_finger_point, index_finger_point, (255, 0, 255), 5)
                            # 勾股定理計(jì)算長(zhǎng)度
                            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)

                            # 激活模式,需要讓矩形跟隨移動(dòng)
                            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
                                # 計(jì)算距離
                                squareManager.setLen(between_finger_tip[0], between_finger_tip[1])

                # 顯示方塊,傳入本實(shí)例,主要為了半透明的處理
                squareManager.display(self)

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

                # 顯示當(dāng)前激活
                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)
                # 顯示畫(huà)面
                cv2.imshow('virtual drag and drop', self.image)

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

control = HandControlVolume()
control.recognize()

到此這篇關(guān)于OpenCV實(shí)現(xiàn)手勢(shì)虛擬拖拽的使用示例的文章就介紹到這了,更多相關(guān)OpenCV 手勢(shì)虛擬拖拽內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Python腳本破解壓縮文件口令實(shí)例教程(zipfile)

    Python腳本破解壓縮文件口令實(shí)例教程(zipfile)

    這篇文章主要給大家介紹了關(guān)于Python腳本破解壓縮文件口令(zipfile)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Python具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • 在Python 字典中一鍵對(duì)應(yīng)多個(gè)值的實(shí)例

    在Python 字典中一鍵對(duì)應(yīng)多個(gè)值的實(shí)例

    今天小編就為大家分享一篇在Python 字典中一鍵對(duì)應(yīng)多個(gè)值的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-02-02
  • python+opencv實(shí)現(xiàn)堆疊圖片

    python+opencv實(shí)現(xiàn)堆疊圖片

    這篇文章主要為大家詳細(xì)介紹了python+opencv實(shí)現(xiàn)堆疊圖片,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • Python爬取12306車次信息代碼詳解

    Python爬取12306車次信息代碼詳解

    這篇文章主要介紹了Python爬取12306車次信息代碼詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • Python使用sort和class實(shí)現(xiàn)的多級(jí)排序功能示例

    Python使用sort和class實(shí)現(xiàn)的多級(jí)排序功能示例

    這篇文章主要介紹了Python使用sort和class實(shí)現(xiàn)的多級(jí)排序功能,涉及Python基于面向?qū)ο蟮脑乇闅v、列表排序、添加等相關(guān)操作技巧,需要的朋友可以參考下
    2018-08-08
  • Python?numpy之線性代數(shù)與隨機(jī)漫步

    Python?numpy之線性代數(shù)與隨機(jī)漫步

    這篇文章主要介紹了Python?numpy之線性代數(shù)與隨機(jī)漫步,線性代數(shù),矩陣計(jì)算,優(yōu)化與內(nèi)存;比如矩陣乘法,分解,行列式等數(shù)學(xué)知識(shí),是所有數(shù)組類庫(kù)的重要組成部分
    2022-07-07
  • Python將視頻轉(zhuǎn)換為圖片介紹

    Python將視頻轉(zhuǎn)換為圖片介紹

    大家好,本篇文章主要講的是Python將視頻轉(zhuǎn)換為圖片介紹,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽
    2022-01-01
  • 如何運(yùn)行.ipynb文件的圖文講解

    如何運(yùn)行.ipynb文件的圖文講解

    今天小編大家分享一篇如何運(yùn)行.ipynb文件的圖文講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-06-06
  • 利用django如何解析用戶上傳的excel文件

    利用django如何解析用戶上傳的excel文件

    這篇文章主要給大家介紹了關(guān)于利用django如何解析用戶上傳的excel文件的相關(guān)資料,這是最近在工作中遇到的一個(gè)問(wèn)題,覺(jué)著有必要分享出給大家,需要的朋友可以參考借鑒,下面來(lái)一起看看詳細(xì)的介紹吧。
    2017-07-07
  • Python隊(duì)列的定義與使用方法示例

    Python隊(duì)列的定義與使用方法示例

    這篇文章主要介紹了Python隊(duì)列的定義與使用方法,結(jié)合具體實(shí)例形式分析了Python定義及使用隊(duì)列的具體操作技巧與注意事項(xiàng),需要的朋友可以參考下
    2017-06-06

最新評(píng)論