OpenCV實現(xiàn)手勢虛擬拖拽的使用示例(附demo)
一、主要步驟及庫的功能介紹
1.主要步驟
要實現(xiàn)本次實驗,主要步驟如下:
- 導(dǎo)入OpenCV庫。
- 通過OpenCV讀取攝像頭的視頻流。
- 使用膚色檢測算法(如色彩空間轉(zhuǎn)換和閾值分割)來識別手部區(qū)域。
- 對手部區(qū)域進(jìn)行輪廓檢測,找到手的輪廓。
- 根據(jù)手的輪廓,獲取手指關(guān)鍵點的像素坐標(biāo)。對于拖拽手勢,可以關(guān)注食指和中指的位置。
- 計算食指和中指指尖之間的距離并判斷是否滿足條件觸發(fā)拖拽動作。
- 如果滿足條件,可以使用勾股定理計算距離,并將矩形區(qū)域變色以示觸發(fā)拖拽。
- 根據(jù)手指的位置更新矩形的坐標(biāo),使矩形跟隨手指運動。
- 當(dāng)手指放開時停止矩形的移動。
2.需要的庫介紹
導(dǎo)入的庫在實現(xiàn)手勢虛擬拖拽的代碼中起著重要的作用,下面是對每個庫的簡要介紹:
OpenCV (cv2): OpenCV是一個開源的計算機(jī)視覺庫,提供了豐富的圖像和視頻處理功能,使用OpenCV來讀取攝像頭視頻流、進(jìn)行圖像處理和計算。
mediapipe (mp): mediapipe提供一系列預(yù)訓(xùn)練的機(jī)器學(xué)習(xí)模型和工具,用于實現(xiàn)計算機(jī)視覺和機(jī)器學(xué)習(xí)任務(wù)。我們使用mediapipe來進(jìn)行手部關(guān)鍵點檢測和姿勢估計。
time: 我們使用time庫來計時和進(jìn)行時間相關(guān)的操作。
math: 在本次實驗我們使用math庫來計算距離和角度。
二、導(dǎo)入所需要的模塊
# 導(dǎo)入OpenCV import cv2 # 導(dǎo)入mediapipe import mediapipe as mp # 導(dǎo)入其他依賴包 import time import math
三、方塊管理類
(SquareManager)是一個方塊管理器,用于創(chuàng)建、顯示、更新和處理方塊的相關(guān)操作。
1.初始化方塊管理器
初始化方塊管理器,傳入方塊的長度(rect_width)作為參數(shù),并初始化方塊列表、距離、激活狀態(tài)和激活的方塊ID等屬性。
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.創(chuàng)建一個方塊
創(chuàng)建一個方塊,將方塊的左上角坐標(biāo)和透明度添加到相應(yīng)的列表中。
# 創(chuàng)建一個方塊,但是沒有顯示
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.判斷落點方塊
判斷給定的坐標(biāo)是否在方塊內(nèi),并返回方塊的ID。
# 判斷落在哪個方塊上,返回方塊的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]
# 檢查指定點是否在方塊內(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.計算距離、更新位置
??setLen? 方法:計算激活方塊與指尖的距離。
??updateSquare? 方法:根據(jù)給定的新坐標(biāo)更新激活方塊的位置。
# 計算與指尖的距離
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
三、識別控制類
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實例,以便另一個類調(diào)用
self.image = None
HandControlVolume用于初始化mediapipe以及存儲中指與矩形左上角點的距離和image實例。
__init__方法:在初始化對象時,初始化mediapipe,包括drawing_utils、drawing_styles和hands。此外,還初始化了中指與矩形左上角點的距離和image實例。
通過mediapipe,可以進(jìn)行手部關(guān)鍵點檢測和姿勢估計,進(jìn)而進(jìn)行手勢識別和處理。為了使其他類能夠調(diào)用image實例,將其作為該類的屬性進(jìn)行存儲,方便地處理手勢識別和控制操作。
2.主函數(shù)
這部分代碼主要用于初始化和準(zhǔn)備處理視頻流以進(jìn)行手勢識別和交互。
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))
# 畫面顯示初始化參數(shù)
rect_percent_text = 0
# 初始化方塊管理器
squareManager = SquareManager(150)
# 創(chuàng)建多個方塊
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:畫面顯示初始化參數(shù),可能被用于屏幕上的文本顯示。squareManager:初始化了方塊管理器類的實例,并設(shè)置方塊的長度為150。
使用一個循環(huán),創(chuàng)建了五個方塊,并通過create方法將其添加到方塊管理器中。進(jìn)入循環(huán),從視頻流中讀取幀圖像,并將其調(diào)整為指定的尺寸。如果成功讀取幀圖像,則會進(jìn)一步處理,否則將輸出錯誤消息。
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è)置圖像為不可寫,以提高性能并避免數(shù)據(jù)拷貝。self.image = cv2.cvtColor(self.image, cv2.COLOR_BGR2RGB):將BGR格式的圖像轉(zhuǎn)換為RGB格式,因為mediapipe模型處理的輸入圖像需要是RGB格式。self.image = cv2.flip(self.image, 1):將圖像進(jìn)行鏡像翻轉(zhuǎn),以與mediapipe模型期望的手部位置對應(yīng)。results = hands.process(self.image):將處理后的圖像傳遞給mediapipe的hands模型,進(jìn)行手勢識別和處理。self.image = cv2.cvtColor(self.image, cv2.COLOR_RGB2BGR):將圖像從RGB格式轉(zhuǎn)換回BGR格式,以便后續(xù)的顯示和處理。
4.檢測手掌,標(biāo)記關(guān)鍵點和連接關(guān)系
if results.multi_hand_landmarks:
# 遍歷每個手掌
for hand_landmarks in results.multi_hand_landmarks:
# 在畫面標(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::檢查是否檢測到手掌。如果有檢測到手掌,則進(jìn)入下一步處理;否則跳過。for hand_landmarks in results.multi_hand_landmarks::遍歷檢測到的每個手掌。self.mp_drawing.draw_landmarks:使用mediapipe的draw_landmarks方法,在圖像上標(biāo)記手指的關(guān)鍵點和連接關(guān)系。
self.image:輸入的圖像。hand_landmarks:手掌的關(guān)鍵點。
self.mp_hands.HAND_CONNECTIONS:手指之間的連接關(guān)系。
5.解析檢測到的手掌并提取手指的關(guān)鍵點
檢測到的手掌并提取手指的關(guān)鍵點,然后將手指的坐標(biāo)存儲起來。
landmark_list = []
# 用來存儲手掌范圍的矩形坐標(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è)計手掌左上角、右下角坐標(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)])
# 給手掌畫框框
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])
# 中間點
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:一個列表,用于存儲手指的關(guān)鍵點信息。paw_x_list和paw_y_list:用于存儲手掌范圍的矩形框的橫縱坐標(biāo)。在循環(huán)中,將每個手指的關(guān)鍵點的索引、x坐標(biāo)、y坐標(biāo)和z坐標(biāo)存儲在
landmark_list中,同時將手掌范圍的橫縱坐標(biāo)存儲在paw_x_list和paw_y_list中。
如果landmark_list不為空,即有手指的關(guān)鍵點被檢測到ratio_x_to_pixel 和 ratio_y_to_pixel:兩個lambda函數(shù),用于將相對比例轉(zhuǎn)換為像素坐標(biāo)的函數(shù)。根據(jù)手掌范圍的矩形坐標(biāo),計算手掌區(qū)域的左上角和右下角坐標(biāo),并畫出方框。使用landmark_list中的信息獲取中指指尖坐標(biāo)和食指指尖坐標(biāo),并將它們轉(zhuǎn)換為像素坐標(biāo)。計算中指指尖坐標(biāo)和食指指尖坐標(biāo)的中間點。將中指指尖的坐標(biāo)和食指指尖的坐標(biāo)存儲在thumb_finger_point和index_finger_point中。
解析檢測到的手掌信息,并提取手指的關(guān)鍵點坐標(biāo),將手指坐標(biāo)轉(zhuǎn)換為像素坐標(biāo),并將中指指尖和食指指尖的位置標(biāo)記在圖像上。
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函數(shù),在圖像上繪制中指指尖和食指指尖之間的連接線。math.hypot函數(shù)計算直角三角形斜邊的長度。將指尖之間的距離映射到
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
# 計算距離
squareManager.setLen(between_finger_tip[0], between_finger_tip[1])
如果squareManager的drag_active屬性為True,即矩形的移動模式已經(jīng)激活,使用squareManager.updateSquare方法更新矩形的位置。如果兩個手指之間的距離大于100,即手指之間的距離超過了閾值,取消矩形的激活模式,將drag_active設(shè)置為False,將active_index設(shè)置為-1。
否則,如果兩個手指之間的距離小于100,且手指之間存在重疊的矩形,并且矩形的移動模式未激活。激活矩形的移動模式,將drag_active設(shè)置為True。根據(jù)手指之間的距離,計算并設(shè)置矩形的長度,使用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)
# 顯示畫面
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鍵(對應(yīng)的ASCII碼為27),則退出循環(huán)。
在屏幕上顯示處理后的圖像、矩形的狀態(tài)和刷新率,并等待按鍵響應(yīng)。這樣可以實現(xiàn)交互式的虛擬拖放功能。接下來我們看一下實際的操作效果。
四、實戰(zhàn)演示



通過演示我們可以實現(xiàn)通過手部對方塊進(jìn)行拖拽,效果可以達(dá)到良好的狀態(tài)。
五、源碼分享
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
# 創(chuàng)建一個方塊,但是沒有顯示
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實例,以便另一個類調(diào)用
self.image = None
# 主函數(shù)
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))
# 畫面顯示初始化參數(shù)
rect_percent_text = 0
# 初始化方塊管理器
squareManager = SquareManager(150)
# 創(chuàng)建多個方塊
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:
# 遍歷每個手掌
for hand_landmarks in results.multi_hand_landmarks:
# 在畫面標(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())
# 解析手指,存入各個手指坐標(biāo)
landmark_list = []
# 用來存儲手掌范圍的矩形坐標(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è)計手掌左上角、右下角坐標(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)])
# 給手掌畫框框
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])
# 中間點
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)
# 顯示當(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)
# 顯示畫面
cv2.imshow('virtual drag and drop', self.image)
if cv2.waitKey(5) & 0xFF == 27:
break
cap.release()
control = HandControlVolume()
control.recognize()到此這篇關(guān)于OpenCV實現(xiàn)手勢虛擬拖拽的使用示例的文章就介紹到這了,更多相關(guān)OpenCV 手勢虛擬拖拽內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python使用sort和class實現(xiàn)的多級排序功能示例
這篇文章主要介紹了Python使用sort和class實現(xiàn)的多級排序功能,涉及Python基于面向?qū)ο蟮脑乇闅v、列表排序、添加等相關(guān)操作技巧,需要的朋友可以參考下2018-08-08
Python?numpy之線性代數(shù)與隨機(jī)漫步
這篇文章主要介紹了Python?numpy之線性代數(shù)與隨機(jī)漫步,線性代數(shù),矩陣計算,優(yōu)化與內(nèi)存;比如矩陣乘法,分解,行列式等數(shù)學(xué)知識,是所有數(shù)組類庫的重要組成部分2022-07-07

