自学Opencv实战项目01 opencv的AI手势识别--python

一、项目概述

通过摄像头实时捕捉手部动作,实现通过食指与中指捏合手势拖拽屏幕方块的效果。使用以下核心技术:

  • MediaPipe手部关键点检测

  • OpenCV图像处理

  • 几何距离计算

  • 透明物体绘制

二、环境准备

需安装的库

python

pip install opencv-python mediapipe numpy

硬件要求

  • 普通USB摄像头

  • 支持Python3的环境

三、代码结构解析

3.1 初始化模块

# MediaPipe初始化
mp_drawing = mp.solutions.drawing_utils
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(
    model_complexity=1,        # 平衡精度模式
    min_detection_confidence=0.5,
    min_tracking_confidence=0.5
)

# 摄像头初始化
cap = cv2.VideoCapture(0)
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

此处我们可以改变参数会得到不一样效果,一般默认0.5。

model_complexity 作用:控制手部检测模型的复杂度等级 取值范围:0(轻量级)、1(平衡)、2(高精度)。默认值为 1。
min_detection_confidence 作用:手部检测的置信度阈值。 取值范围:[0.0, 1.0],默认值为 0.5。
min_tracking_confidence 作用:手部跟踪的置信度阈值。 取值范围:[0.0, 1.0],默认值为 0.5

3.2 核心逻辑实现

手部关键点检测
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
results = hands.process(frame)

# 绘制关键点连线
mp_drawing.draw_landmarks(
    frame,
    hand_landmarks,
    mp_hands.HAND_CONNECTIONS,
    mp_drawing_styles.get_default_hand_landmarks_style(),
    mp_drawing_styles.get_default_hand_connections_style()
)
手势判定逻辑

利用两点之间距离公式即可计算两指距离

# 计算双指距离
finger_len = math.hypot((index_finger_x-middle_finger_x),
                        (index_finger_y-middle_finger_y))

# 拖拽判定条件
if finger_len < 30:
    if (index_finger_x > square_x and 
        index_finger_x < (square_x+square_width) and
        index_finger_y > square_y and 
        index_finger_y < (square_y+square_width)):
        on_square = True
透明方块实现(为了更好的看见手势放在方块上)
overlay = frame.copy()
cv2.rectangle(overlay, (square_x,square_y), 
             (square_x+square_width, square_y+square_height),
             square_color, -1)
frame = cv2.addWeighted(overlay, 0.5, frame, 0.5, 0)

四、技术原理解析

4.1 MediaPipe工作机制

  • 21个手部关键点:每个手掌检测到21个关键点(0-20号)

  • 关键点索引

    • 8号点:食指指尖

    • 12号点:中指指尖

  • 置信度参数:通过调整检测/跟踪置信度平衡性能与精度

4.2 拖拽算法

  1. 距离计算:使用math.hypot计算欧氏距离

  2. 位置偏移补偿:通过L1/L2记录初始接触偏移量(只要保持相对位置不变,那么方块就可以随着手移动)

  3. 状态机控制:on_square标志位管理拖拽状态

# 记录初始偏移
L1 = abs(index_finger_x - square_x)
L2 = abs(index_finger_y - square_y)

# 更新方块位置
square_x = index_finger_x - L1
square_y = index_finger_y - L2

五、效果演示

特征说明:

  • 双指间距<30时触发抓取

  • 蓝色表示拖拽中状态

  • 半透明视觉效果

  • 实时60FPS+的流畅度

六、优化方向

  1. 手势优化:增加张开/握拳状态识别

  2. 多物体交互:实现多个可拖拽物体

  3. 3D控制:结合深度信息实现Z轴控制

  4. 碰撞检测:添加边界限制与物体碰撞

七、完整代码

import math
import cv2
import  numpy as np
#mp相关参数
import mediapipe as mp
mp_drawing=mp.solutions.drawing_utils
mp_drawing_styles=mp.solutions.drawing_styles
mp_hands=mp.solutions.hands
#model_complexity 作用:控制手部检测模型的复杂度等级 取值范围:0(轻量级)、1(平衡)、2(高精度)。默认值为 1。
#min_detection_confidence 作用:手部检测的置信度阈值。 取值范围:[0.0, 1.0],默认值为 0.5。
#min_tracking_confidence 作用:手部跟踪的置信度阈值。 取值范围:[0.0, 1.0],默认值为 0.5
hands=mp_hands.Hands(
    model_complexity=1,
    min_detection_confidence=0.5,
    min_tracking_confidence=0.5
)
#获取摄像头视频流
cap=cv2.VideoCapture(0)
#获取画面宽度和高度(因为mediapipe框架中对长宽进行了计算)
width=int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height=int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
#方块、坐标相关参数
square_x=100
square_y=100
square_width=100
square_height=100
L1=0
L2=0
on_square=False
square_color=(255,0,255)
while True:
    #获取每一帧
    ret,frame=cap.read()
    #对图像进行处理
    frame=cv2.flip(frame,1)
    #mediapipe处理
    frame.flags.writeable=False
    frame=cv2.cvtColor(frame,cv2.COLOR_BGR2RGB)
    results=hands.process(frame)
    frame.flags.writeable=True
    frame=cv2.cvtColor(frame,cv2.COLOR_RGB2BGR)
    if results.multi_hand_landmarks:
        for hand_landmarks in results.multi_hand_landmarks:
            mp_drawing.draw_landmarks(
                frame,
                hand_landmarks,
                mp_hands.HAND_CONNECTIONS,
                mp_drawing_styles.get_default_hand_landmarks_style(),
                mp_drawing_styles.get_default_hand_connections_style()
            )
        x_list=[]
        y_list=[]
        for landmark in hand_landmarks.landmark:
            x_list.append(landmark.x)
            y_list.append(landmark.y)
        #获取食指坐标
        index_finger_x=int(x_list[8]*width)
        index_finger_y=int(y_list[8]*height)
        #获取中指坐标
        middle_finger_x=int(x_list[12]*width)
        middle_finger_y=int(y_list[12]*height)
        #计算食指与中指指尖距离(两点之间坐标公式)
        finger_len=math.hypot((index_finger_x-middle_finger_x),(index_finger_y-middle_finger_y))
        #画一个圆来验证是否可以显示在屏幕上
        #cv2.circle(frame,(index_finger_x,index_finger_y),20,(255,0,255),-1)
        #如果距离小于30才开始激活
        if finger_len<30:
            #判断食指指尖在不在方块上
            if (index_finger_x > square_x and index_finger_x<(square_x+square_width)
              and index_finger_y>square_y and index_finger_y<(square_y+square_width)):
                if on_square==False:
                    #print("在方块上")
                    L1=abs(index_finger_x-square_x)
                    L2=abs(index_finger_y-square_y)
                    on_square=True
                else:
                    pass
        else:
            on_square=False
            square_color=(255,0,255)
        if on_square:
            square_x=index_finger_x-L1
            square_y=index_finger_y-L2
            square_color=(255,0,0)
    #注意颜色是BGR的顺序,方块参数是对角线上的两点坐标,-1表示实心
    #cv2.rectangle(frame,(square_x,square_y),(square_x+square_width,square_height+square_y),(255,0,0),-1)
    #画一个半透明的方块
    overplay=frame.copy()
    cv2.rectangle(frame,(square_x,square_y),(square_x+
    square_width,square_height+square_y),square_color,-1)
    frame=cv2.addWeighted(overplay,0.5,frame,0.5,0)
    #使鼠标可以调整窗口的大小
    #cv2.namedWindow('window', cv2.WINDOW_NORMAL)
    cv2.imshow('window', frame)
    #当按esc键时退出窗口
    if cv2.waitKey(10) & 0xFF ==27:
        break
#释放并关闭 窗口
cap.release()
cv2.destroyWindow('window')

注意事项

  1. 手指尽量保持竖直状态便于识别

  2. 保证环境光照充足

  3. 可根据摄像头分辨率调整检测阈值

以下是gitee上的源码:creative: 本科大学生个人学习与练习阶段 

注:本人普通双非大学生一名,出于个人兴趣网上学习opencv,听从老师建议决定记录自己学习成果,如有问题欢迎各位点评。同时,如有一些不专业或是其他差错,请谅解! 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值