opencv实战项目 手势识别-手势控制键盘

手势识别是一种人机交互技术,通过识别人的手势动作,从而实现对计算机、智能手机、智能电视等设备的操作和控制。

1.  opencv实现手部追踪(定位手部关键点)

2.opencv实战项目 实现手势跟踪并返回位置信息(封装调用)

3.opencv实战项目 手势识别-手势控制鼠标

4.opencv实战项目 手势识别-手势控制键盘

未完待续

本专栏记录作者的学习之旅会一直更新下去,欢迎订阅一起学习进步

本项目是使用了谷歌开源的框架mediapipe,里面有非常多的模型提供给我们使用,例如面部检测,身体检测,手部检测等在这里插入图片描述

 代码需要用到opencv   HandTraqckModule模块   mediapipe模块和一个键盘控制模块pynput,cvzone模块

一、HandTraqckModule模块 

前面的文章中有封装手部检测模块的教程,这边简单的介绍一下,有新增加的模块可以简单学习一下

import cv2
import mediapipe as mp
import mathclass HandDetector:"""Finds Hands using the mediapipe library. Exports the landmarksin pixel format. Adds extra functionalities like finding howmany fingers are up or the distance between two fingers. Alsoprovides bounding box info of the hand found."""def __init__(self, mode=False, maxHands=2, detectionCon=0.5, minTrackCon=0.5):""":param mode: In static mode, detection is done on each image: slower:param maxHands: Maximum number of hands to detect:param detectionCon: Minimum Detection Confidence Threshold:param minTrackCon: Minimum Tracking Confidence Threshold"""self.mode = modeself.maxHands = maxHandsself.detectionCon = detectionConself.minTrackCon = minTrackConself.mpHands = mp.solutions.handsself.hands = self.mpHands.Hands(self.mode, self.maxHands,self.detectionCon, self.minTrackCon)self.mpDraw = mp.solutions.drawing_utilsself.tipIds = [4, 8, 12, 16, 20]self.fingers = []self.lmList = []def findHands(self, img, draw=True):"""Finds hands in a BGR image.:param img: Image to find the hands in.:param draw: Flag to draw the output on the image.:return: Image with or without drawings"""imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)self.results = self.hands.process(imgRGB)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 imgdef findPosition(self, img, handNo=0, draw=True):"""Finds landmarks of a single hand and puts them in a listin pixel format. Also finds the bounding box around the hand.:param img: main image to find hand in:param handNo: hand id if more than one hand detected:param draw: Flag to draw the output on the image.:return: list of landmarks in pixel format; bounding box"""xList = []yList = []bbox = []bboxInfo = []self.lmList = []if self.results.multi_hand_landmarks:myHand = self.results.multi_hand_landmarks[handNo]for id, lm in enumerate(myHand.landmark):h, w, c = img.shapepx, py = int(lm.x * w), int(lm.y * h)xList.append(px)yList.append(py)self.lmList.append([px, py])if draw:cv2.circle(img, (px, py), 5, (255, 0, 255), cv2.FILLED)xmin, xmax = min(xList), max(xList)ymin, ymax = min(yList), max(yList)boxW, boxH = xmax - xmin, ymax - yminbbox = xmin, ymin, boxW, boxHcx, cy = bbox[0] + (bbox[2] // 2), \bbox[1] + (bbox[3] // 2)bboxInfo = {"id": id, "bbox": bbox, "center": (cx, cy)}if draw:cv2.rectangle(img, (bbox[0] - 20, bbox[1] - 20),(bbox[0] + bbox[2] + 20, bbox[1] + bbox[3] + 20),(0, 255, 0), 2)return self.lmList, bboxInfodef fingersUp(self):"""Finds how many fingers are open and returns in a list.Considers left and right hands separately:return: List of which fingers are up"""if self.results.multi_hand_landmarks:myHandType = self.handType()fingers = []# Thumbif myHandType == "Right":if self.lmList[self.tipIds[0]][0] > self.lmList[self.tipIds[0] - 1][0]:fingers.append(1)else:fingers.append(0)else:if self.lmList[self.tipIds[0]][0] < self.lmList[self.tipIds[0] - 1][0]:fingers.append(1)else:fingers.append(0)# 4 Fingersfor id in range(1, 5):if self.lmList[self.tipIds[id]][1] < self.lmList[self.tipIds[id] - 2][1]:fingers.append(1)else:fingers.append(0)return fingersdef findDistance(self, p1, p2, img, draw=True):"""Find the distance between two landmarks based on theirindex numbers.:param p1: Point1 - Index of Landmark 1.:param p2: Point2 - Index of Landmark 2.:param img: Image to draw on.:param draw: Flag to draw the output on the image.:return: Distance between the pointsImage with output drawnLine information"""if self.results.multi_hand_landmarks:x1, y1 = self.lmList[p1][0], self.lmList[p1][1]x2, y2 = self.lmList[p2][0], self.lmList[p2][1]cx, cy = (x1 + x2) // 2, (y1 + y2) // 2if 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 handType(self):"""Checks if the hand is left or right:return: "Right" or "Left""""if self.results.multi_hand_landmarks:if self.lmList[17][0] < self.lmList[5][0]:return "Right"else:return "Left"def main():cap = cv2.VideoCapture(0)detector = HandDetector(detectionCon=0.8, maxHands=1)while True:# Get image framesuccess, img = cap.read()# Find the hand and its landmarksimg = detector.findHands(img)lmList, bboxInfo = detector.findPosition(img)print(detector.handType())# Displaycv2.imshow("Image", img)cv2.waitKey(1)if __name__ == "__main__":main()

  1. 导入库:导入了必要的库,包括 OpenCV (cv2) 用于图像处理和显示,Mediapipe (mediapipe) 用于手部检测和跟踪,以及数学库 (math)。

  2. HandDetector 类:这是主要的手势检测器类,提供了多个方法来处理手部检测和分析手势。

    • __init__ 方法:初始化检测器的参数,例如检测模式、最大检测手数、检测和跟踪的置信度阈值等。

    • findHands 方法:在给定的图像中寻找手部,可以选择是否绘制检测结果。

    • findPosition 方法:找到单个手部的关键点位置(landmarks)并将它们存储在像素格式的列表中,同时计算手部的边界框信息。

    • fingersUp 方法:确定手势中有多少个手指打开,将结果以列表形式返回。

    • findDistance 方法:计算两个指定关键点之间的距离,并在图像上绘制结果。

    • handType 方法:确定手的类型是左手还是右手。

具体就不展开讲了

这个函数在有一个专门的包叫做cvzone里有,但是不知道是不是版本的问题,少了一些东西,运行不起来,只能自己手撸检测模块。

下面是主函数的代码

import cv2
from cvzone.HandTrackingModule import HandDetector
from HandTrackingModule import *
from time import sleep
import numpy as np
import cvzone
from pynput.keyboard import Controllercap = cv2.VideoCapture(0)
cap.set(3, 1280)
cap.set(4, 720)detector =HandDetector(detectionCon=0.5)
keys = [["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"],["A", "S", "D", "F", "G", "H", "J", "K", "L", ";"],["Z", "X", "C", "V", "B", "N", "M", ",", ".", "/"]]
finalText = ""keyboard = Controller()def drawAll(img, buttonList):for button in buttonList:x, y = button.posw, h = button.sizecvzone.cornerRect(img, (button.pos[0], button.pos[1], button.size[0], button.size[1]),20, rt=0)cv2.rectangle(img, button.pos, (x + w, y + h), (255, 0, 255), cv2.FILLED)cv2.putText(img, button.text, (x + 20, y + 65),cv2.FONT_HERSHEY_PLAIN, 4, (255, 255, 255), 4)return img#
# def drawAll(img, buttonList):
#     imgNew = np.zeros_like(img, np.uint8)
#     for button in buttonList:
#         x, y = button.pos
#         cvzone.cornerRect(imgNew, (button.pos[0], button.pZXos[1], button.size[0], button.size[1]),
#                           20, rt=0)
#         cv2.rectangle(imgNew, button.pos, (x + button.size[0], y + button.size[1]),
#                       (255, 0, 255), cv2.FILLED)
#         cv2.putText(imgNew, button.text, (x + 40, y + 60),
#                     cv2.FONT_HERSHEY_PLAIN, 2, (255, 255, 255), 3)
#
#     out = img.copy()
#     alpha = 0.5
#     mask = imgNew.astype(bool)
#     print(mask.shape)
#     out[mask] = cv2.addWeighted(img, alpha, imgNew, 1 - alpha, 0)[mask]
#     return outclass Button():def __init__(self, pos, text, size=[85, 85]):self.pos = posself.size = sizeself.text = textbuttonList = []
for i in range(len(keys)):for j, key in enumerate(keys[i]):buttonList.append(Button([100 * j + 50, 100 * i + 50], key))while True:success, img = cap.read()img = detector.findHands(img)lmList, bboxInfo = detector.findPosition(img)img = drawAll(img, buttonList)if lmList:for button in buttonList:x, y = button.posw, h = button.sizeif x < lmList[8][0] < x + w and y < lmList[8][1] < y + h:cv2.rectangle(img, (x - 5, y - 5), (x + w + 5, y + h + 5), (175, 0, 175), cv2.FILLED)cv2.putText(img, button.text, (x + 20, y + 65),cv2.FONT_HERSHEY_PLAIN, 4, (255, 255, 255), 4)l, _, _ = detector.findDistance(8, 12, img, draw=False)print(l)## when clickedif l < 30:keyboard.press(button.text)cv2.rectangle(img, button.pos, (x + w, y + h), (0, 255, 0), cv2.FILLED)cv2.putText(img, button.text, (x + 20, y + 65),cv2.FONT_HERSHEY_PLAIN, 4, (255, 255, 255), 4)finalText += button.textsleep(0.15)cv2.rectangle(img, (50, 350), (700, 450), (175, 0, 175), cv2.FILLED)cv2.putText(img, finalText, (60, 430),cv2.FONT_HERSHEY_PLAIN, 5, (255, 255, 255), 5)cv2.imshow("Image", img)cv2.waitKey(1)
  1. 导入库:导入了需要的库,包括 OpenCV (cv2) 用于图像处理和显示,Mediapipe 中的 HandDetector 用于手部检测,cvzone 用于绘制按钮外观,numpy 用于数组处理,pynput.keyboard 中的 Controller 用于模拟键盘输入,time 用于延时。

  2. 设置摄像头参数:通过 OpenCV 设置摄像头的分辨率为 1280x720。

  3. 创建 HandDetector 实例:使用 HandDetector 类创建一个手势检测器实例,设置检测的置信度阈值为 0.5。

  4. 创建按钮列表:创建了一个包含虚拟键盘按钮信息的列表,按键布局通过嵌套列表 keys 定义。

  5. 创建 Button 类:用于创建虚拟按钮的类,每个按钮包含位置、文本内容和大小。

  6. 主循环:进入一个无限循环,用于处理实时的摄像头捕获图像帧。

    • 读取图像帧:从摄像头捕获图像帧。

    • 手部检测:使用手势检测器找出图像中的手部和关键点。

    • 绘制按钮:调用 drawAll 函数在图像上绘制虚拟按钮。

    • 遍历按钮列表:对每个按钮进行检查,看是否有手指触摸到按钮。

      • 如果手指在按钮范围内,绘制高亮效果。

      • 计算手指触摸点与按钮中心的距离,如果小于一定阈值,则模拟键盘按下并记录输入。

    • 绘制已输入的文本:在图像上绘制已输入的文本。

    • 显示图像:通过 OpenCV 显示处理后的图像。

    • 等待键盘输入:等待 1 毫秒,以便保持图像窗口的响应性。

  7. 运行主程序:执行主循环,处理实时的摄像头捕获和手势识别。

如果有遇到问题可以评论区留言,大家一起相互学习!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/35257.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

虚拟机安装 Ubuntu桌面版,宿主机无法访问虚拟机 ufw 防火墙简单使用

虚拟机安装 Ubuntu桌面版&#xff0c;宿主机无法访问虚拟机 问题处理安装ssh服务ufw防火墙 放行ssh服务ufw 常用命令 问题 本次安装使用的 ubuntu-22.04.2-desktop-amd64 &#xff0c;网络连接使用的是桥接&#xff0c;查看ubuntu的ip是正常的&#xff0c;与宿主机在同一个网段…

力扣的板子

板子 线性筛法求质因子的板子快速幂 线性筛法求质因子的板子 int limit 100000; //修改为题目中的数字的上限 bool isprime[100005] {0}; //保存所有1~limit中的数字是不是质数 int myprime[100005] {0}; //保存2~limit中所有数字的最小质因子 int primes[100000] {0}; …

airflow是什么

Airflow 简介 Airflow是一个基于有向无环图(DAG)的可编程、调度和监控的工作流平台&#xff0c;它可以定义一组有依赖的任务&#xff0c;按照依赖依次执行。airflow提供了丰富的命令行工具用于系统管控&#xff0c;而其web管理界面同样也可以方便的管控调度任务&#xff0c;并…

Lua 闭包

一、Lua 中的函数 Lua 中的函数是第一类值。意味着和其他的常见类型的值&#xff08;例如数值和字符串&#xff09;具有同等权限。 举个例子&#xff0c;函数也可以像其他类型一样存储起来&#xff0c;然后调用 -- 将 a.p 指向 print 函数 a { p print } -- 使用 a.p 函数…

(原创)Flutter与Native页面互相跳转

前言 实际开发混合项目时&#xff0c;常常会有页面跳转的需求 如果是原生界面和flutter界面需要互相跳转 这种情况应该怎么处理呢&#xff1f; 今天这篇博客主要就来介绍下这个情况 其实想一下&#xff0c;这个问题可以拆成四个小的问题来分析&#xff1a; 1&#xff1a;原生界…

什么是全局代理,手机怎么设置全局代理

目录 什么是全局代理 全局代理的优缺点 优点 缺点 手机怎么设置全局代理 注意事项 总结 在计算机网络和信息安全中&#xff0c;全局代理是一种常用的技术手段&#xff0c;用于将网络流量通过代理服务器进行转发和处理。本文将介绍什么是全局代理&#xff0c;探讨全局代理…

pyspark笔记 pyspark.sql.functions

col qqpyspark 笔记 pyspark.sql.function col VS select_UQI-LIUWJ的博客-CSDN博客 取某一列 lit 创建一个包含指定值的列 date_trunc 将日期截取成由第一个参数指定的字符串值 year, yyyy, yy——截取到年month,mon,mm——截取到月day,dd ——截取到天microsecondmillis…

SpringBoot WebSocket配合react 使用消息通信

引入websocket依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>配置websocket import org.springframework.context.annotation.Bean; import org.spr…

Highcharts引入

Highcharts是和jQuery一起使用的&#xff0c;所以需要下载好jQuery jQuery下载方式&#xff1a;访问&#xff1a;http://cdn.staticfile.org/jquery/2.1.4/jquery.min.js&#xff0c;然后全选复制到自己新建的txt文档中&#xff0c;最后把扩展名改为js。 Highcharts下载方式&…

pytest运行时参数说明,pytest详解,pytest.ini详解

一、Pytest简介 1.pytest是一个非常成熟的全功能的Python测试框架&#xff0c;主要有一下几个特点&#xff1a; 简单灵活&#xff0c;容易上手&#xff0c;支持参数化 2.能够支持简单的单元测试和复杂的功能测试&#xff0c;还可以用来做selenium、appium等自动化测试&#xf…

使用sqlplus连接oracle,提示ORA-01034和ORA-27101

具体内容如下 PL/SQL Developer 处 登录时 终端处 登录时 ERROR: ORA-01034: ORACLE not available ORA-27101: shared memory realm does not exist Process ID: 0 Session ID: 0 Serial number: 0 解决方法是执行以下命令 sqlplus /nolog conn / as sysdba startup …

【Hilog】鸿蒙系统日志源码分析

【Hilog】鸿蒙系统日志源码分析 Hilog采用C/S结构&#xff0c;Hilogd作为服务端提供日志功能。Client端通过API调用&#xff08;最终通过socket通讯&#xff09;与HiLogd打交道。简易Block图如下。 这里主要分析一下。Hilog的读、写、压缩落盘&#xff0c;以及higlog与android…

学术论文GPT源码解读:从chatpaper、chatwithpaper到gpt_academic

前言 之前7月中旬&#xff0c;我曾在微博上说准备做“20个LLM大型项目的源码解读” 针对这个事&#xff0c;目前的最新情况是 已经做了的&#xff1a;LLaMA、Alpaca、ChatGLM-6B、deepspeedchat、transformer、langchain、langchain-chatglm知识库准备做的&#xff1a;chatpa…

GitHub上受欢迎的Android UI Library

内容 抽屉菜单ListViewWebViewSwitchButton按钮点赞按钮进度条TabLayout图标下拉刷新ViewPager图表(Chart)菜单(Menu)浮动菜单对话框空白页滑动删除手势操作RecyclerViewCardColorDrawableSpinner布局模糊效果TabBarAppBar选择器(Picker)跑马灯日历时间主题样式ImageView通知聊…

chapter 1 formation of crystal, basic concepts

chapter 1 晶体的形成 1.1 Quantum Mechanics and atomic structure 1.1.1 Old Quantum Theory problems of planetary model: atom would be unstableradiate EM wave of continuous frequency to solve the prablom of planetary model: Bohr: Quantum atomic structureP…

React 实现文件分片上传和下载

React 实现文件分片上传和下载 在开发中&#xff0c;文件的上传和下载是常见的需求。然而&#xff0c;当面对大型文件时&#xff0c;直接的上传和下载方式可能会遇到一些问题&#xff0c;比如网络传输不稳定、文件过大导致传输时间过长等等。为了解决这些问题&#xff0c;我们…

Vue中自定义.js变量

1、定义.js文件 order.js文件内容&#xff1a; // 订单是否报账 const EXPENESS_STATUS_NO0; const EXPENESS_STATUS_YES1; // 状态 0-未发货 1-发货 2-确认收获 const STATUS_NO0; const STATUS_SEND1; const STATUS_DELIVERY2; // 如何不加这个&#xff0c;vue中引…

yolov5、YOLOv7、YOLOv8改进:注意力机制CA

论文题目&#xff1a;《Coordinate Attention for Efficient Mobile NetWork Design》论文地址&#xff1a; https://arxiv.org/pdf/2103.02907.pdf 本文中&#xff0c;作者通过将位置信息嵌入到通道注意力中提出了一种新颖的移动网络注意力机制&#xff0c;将其称为“Coordin…

Nagle算法--网络优化算法

Nagle Nagle算法是一种网络优化算法&#xff0c;旨在减少小数据包的网络传输次数&#xff0c;提高网络传输效率。该算法由John Nagle在1984年提出&#xff0c;并被广泛应用于TCP协议中。 Nagle算法的原理是将较小的数据包进行缓存&#xff0c;在缓存数据包的发送时机到来时&am…

拓扑布局和建立小型网络

练习 2.6.1&#xff1a;拓扑布局和建立小型网络 地址表 本实验不包括地址表。 拓扑图 学习目标 正确识别网络中使用的电缆物理连接点对点交换网络验证每个网络的基本连通性 简介&#xff1a; 许多网络问题都可以在网络的物理层解决。因此&#xff0c;必须清楚了解网络连接…