OpenCV+python实现摄像头简单手势识别--进度条控制亮度

2023-11-16

前言

例如:随着人工智能的不断发展,计算机视觉这门技术也越来越重要,很多人都开启了学习计算机视觉,本文在Opencv基础上实现了摄像头简单手势识别–进度条控制亮度的基础内容,并没有使用深度学习技术,因此准确率并不高。


一、整体框架

∙ \bullet 第一步: 开启摄像头,检测每帧图片;

∙ \bullet 第二步: 设置回调函数,操纵滑动条来调整亮度;

∙ \bullet 第三步: 肤色检测,基于HSV颜色空间H,S,V范围筛选法。在HSV中 7<H<20,28<S<256,50<V<256;

∙ \bullet 第四步: 进行高斯滤波;

∙ \bullet 第五步: 边缘轮廓检测;

∙ \bullet 第六步: 求出手势的凹凸点;

∙ \bullet 第七步: 利用凹凸点个数判断当前手势。例如:0个凹凸点就是拳头,4个凹点就是布。

二、使用步骤;

1.引入库;

import cv2
import numpy as np
import math

2.第一步:打开摄像头;

代码如下(示例):

cap = cv2.VideoCapture(0)
while (cap.isOpened()):  
    ret, frame = cap.read()  # 读取摄像头每帧图片
    frame = cv2.flip(frame, 1)  #镜像调整,将图像左右调换回来正常显示

3.第二步:设置回调函数;

def callback(object):  #注意这里createTrackbar会向其传入参数即滑动条地址(几乎用不到),所以必须写一个参数
    pass
cv2.createTrackbar("change", "frame", 100, 255, callback)

4.第三步:肤色检测;

# 基于hsv的肤色检测,通过HSV颜色空间来筛选所需要的像素
hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
lower_skin = np.array([0, 28, 70], dtype=np.uint8)
upper_skin = np.array([20, 255, 255], dtype=np.uint8)

5.第四步:进行高斯滤波;

# 进行高斯滤波,降低噪声的影响
mask = cv2.inRange(hsv, lower_skin, upper_skin)
mask = cv2.dilate(mask, kernel, iterations=4)
mask = cv2.GaussianBlur(mask, (5, 5), 100)

6.第五步:边缘轮廓检测;

# 找出轮廓,确定手势范围
contours, h = cv2.findContours(
    mask, cv2.RETR_TREE,
    cv2.CHAIN_APPROX_SIMPLE)  #opencv中提供findContours()函数来寻找图像中物体的轮廓
cnt = max(contours, key=lambda x: cv2.contourArea(x))
epsilon = 0.0005 * cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, True)
hull = cv2.convexHull(cnt)
areahull = cv2.contourArea(hull)
areacnt = cv2.contourArea(cnt)
arearatio = ((areahull - areacnt) / areacnt) * 100

7.第六步:求出手势的凹凸点;

# 求出凹凸点
hull = cv2.convexHull(approx, returnPoints=False)  #convexHull能很方便的用于求多边形凸包
defects = cv2.convexityDefects(approx, hull)  #使用convexityDefects计算轮廓凸缺陷

8.第七步: 利用凹凸点个数判断当前手势;

# 定义凹凸点个数初始值为0
l = 0
for i in range(defects.shape[0]):
    s, e, f, d, = defects[i, 0]
    start = tuple(approx[s][0])
    end = tuple(approx[e][0])
    far = tuple(approx[f][0])
    pt = (100, 100)
    a = math.sqrt((end[0] - start[0])**2 + (end[1] - start[1])**2)
    b = math.sqrt((far[0] - start[0])**2 + (far[1] - start[1])**2)
    c = math.sqrt((end[0] - far[0])**2 + (end[1] - far[1])**2)
    s = (a + b + c) / 2
    ar = math.sqrt(s * (s - a) * (s - b) * (s - c))
    # 手指间角度求取
    angle = math.acos((b**2 + c**2 - a**2) / (2 * b * c)) * 57
    if angle <= 90 and d > 20:
        l += 1
        cv2.circle(roi, far, 3, [255, 0, 0], -1)
    cv2.line(roi, start, end, [0, 255, 0], 2)  # 画出包络线
l += 1
font = cv2.FONT_HERSHEY_SIMPLEX

成果展示

可通过滑动条来调整亮度,提高识别率。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
由于肤色检测的时候是用色调来提取特征,因此会被黄色调的影响,因此准确率并不算高,只能实现基本的功能,要想准确率高还得上深度学习算法。

完整代码

# -*- coding: utf-8 -*-
"""
Created on Thu Apr  7 18:42:02 2022

@author: He Zekai
"""
import cv2
import numpy as np
import math

def callback(object):  #注意这里createTrackbar会向其传入参数即滑动条地址(几乎用不到),所以必须写一个参数
    pass

cap = cv2.VideoCapture(0)

cv2.namedWindow('frame')
cv2.resizeWindow('frame',600,800)
cv2.createTrackbar("change", "frame", 100, 255, callback)

while(cap.isOpened()):        
    ret,image = cap.read() # 读取摄像头每帧图片
    
    image = cv2.flip(image,1)

    cv2.rectangle(image,(100,100),(300,300),(0,0,255),0) # 用红线画出手势识别框
    
    #滑动条控制颜色    
    value = cv2.getTrackbarPos('change', 'frame')
    image_dst = np.uint8(image/100*value)


    roi = image_dst[100:300,100:300]# 选取图片中固定位置作为手势输入
    kernel = np.ones((2,2),np.uint8)

    # 进行高斯滤波
    lower_skin = np.array([0,28,70],dtype=np.uint8)
    upper_skin = np.array([20, 255, 255],dtype=np.uint8)
    
    mask = cv2.inRange(roi,lower_skin,upper_skin)
    mask = cv2.dilate(mask,kernel,iterations=4)
    mask = cv2.GaussianBlur(mask,(3,3),100)
    
    # 基于hsv的肤色检测
    hsv = cv2.cvtColor(roi,cv2.COLOR_BGR2HSV)

    # 进行高斯滤波
    mask = cv2.inRange(hsv,lower_skin,upper_skin)
    mask = cv2.dilate(mask,kernel,iterations=4)
    mask = cv2.GaussianBlur(mask,(5,5),100)

    # 找出轮廓
    contours,h = cv2.findContours(mask,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
    cnt = max(contours,default=0,key=lambda x:cv2.contourArea(x))
    epsilon = 0.0005*cv2.arcLength(cnt,True)
    approx = cv2.approxPolyDP(cnt,0.05,True)
    hull = cv2.convexHull(cnt)
    areahull = cv2.contourArea(hull)
    areacnt = cv2.contourArea(cnt)
    arearatio = ((areahull-areacnt)/areacnt)*100

    # 求出凹凸点
    hull = cv2.convexHull(approx,returnPoints=False)
    defects = cv2.convexityDefects(approx,hull)

    # 定义凹凸点个数初始值为0
    l=0
    try:
        for i in range(defects.shape[0]):
            s,e,f,d, = defects[i,0]
            start = tuple(approx[s][0])
            end = tuple(approx[e][0])
            far = tuple(approx[f][0])
            pt = (100,100)
        
            a = math.sqrt((end[0]-start[0])**2+(end[1]-start[1])**2)
            b = math.sqrt((far[0] - start[0]) ** 2 + (far[1] - start[1]) ** 2)
            c = math.sqrt((end[0]-far[0])**2+(end[1]-far[1])**2)
            s = (a+b+c)/2
            ar = math.sqrt(s*(s-a)*(s-b)*(s-c))
        
        	# 手指间角度求取
            angle = math.acos((b**2 + c**2 -a**2)/(2*b*c))*57
        
            if angle<=90 and d>20:
                l+=1
                cv2.circle(roi,far,3,[255,0,0],-1)
            cv2.line(roi,start,end,[0,255,0],2) # 画出包络线
        l+=1
        font = cv2.FONT_HERSHEY_SIMPLEX
        
        # 条件判断,知道手势后想实现的功能
        if l==1:
            if areacnt<2000:
                cv2.putText(image_dst,"Please put hand in the window",(0,50),font,2,(0,0,255),3,cv2.LINE_AA)
            else:
                if arearatio<12:
                    cv2.putText(image_dst,'0',(0,50),font,2,(0,0,255),3,cv2.LINE_AA)
                elif arearatio<17.5:
                    cv2.putText(image_dst,"1",(0,50),font,2,(0,0,255),3,cv2.LINE_AA)
                else:
                    cv2.putText(image_dst,'1',(0,50),font,2,(0,0,255),3,cv2.LINE_AA)
        elif l==2:
            cv2.putText(image_dst,'2',(0,50),font,2,(0,0,255),3,cv2.LINE_AA)
        elif l==3:
            if arearatio<27:
                cv2.putText(image_dst,'3',(0,50),font,2,(0,0,255),3,cv2.LINE_AA)
            else:
                cv2.putText(image_dst,'3',(0,50),font,2,(0,0,255),3,cv2.LINE_AA)
        elif l==4:
            cv2.putText(image_dst,'4',(0,50),font,2,(0,0,255),3,cv2.LINE_AA)
        elif l==5:
            cv2.putText(image_dst,'5',(0,50),font,2,(0,0,255),3,cv2.LINE_AA)
        # cv2.imshow('frame',frame)
        cv2.imshow('mask', mask)
        cv2.imshow('frame', image_dst)
        
        key = cv2.waitKey(25)& 0xFF
        if key == ord('q'):     # 键盘q键退出
            break
    except:
        pass

cv2.destroyAllWindows()
cap.release()

总结

例如:本次基于摄像头的简单手势识别实验,让我对以往的知识掌握更加深刻。虽然现在手势识别都是通过深度学习中的CNN等实现的,但仅使用opencv的传统方法来实现这个功能对我来说还挺新颖的,让我对opencv更加充满了兴趣。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OpenCV+python实现摄像头简单手势识别--进度条控制亮度 的相关文章

随机推荐

  • 什么是JDBC,JDBC的主要功能是什么?

    JDBC Java Data Base Connectivity 是Java连接数据库的一门技术 是一种执行SQL的API 可以为多种关系型数据库提供统一的访问功能 它是由一组用java语言编写的类和接口组成 是Java访问数据库的标准规范
  • Vue Element Select选择器自定义验证规则

    前言 在我们在做增删改查的时候 一般会遇到添加和修改的from表单需要表单验证 一般常规的from表单是有自己的验证规则 from加一个 rules rules 但他需要v model值和prop值要一样 这是常规情况 我遇到的是 绑定多个
  • 5.监听器(Listener)

    1 监听器简介 监听器主要用来监听对象的创建 属性的变化 是一个实现特定接口的普通Java类 Listener接口与事件对应表 与 ServletContext 有关 ServletContextListener ServletContex
  • 前端Ant Design Pro搭建流程

    全局安装tyarn npm i g tyarn 全局安装ant design pro脚手架 npm i ant design pro cli g 使用脚手架创建项目 pro create myapp gt 选择umi 3 gt 选择simp
  • 14-3 编写第一个 GTK 程序

    1 Hello World GTK 是事件驱动的工具包 从数据处理角度 当一个事件发生时 如点击一次鼠标 所按的构件便会发出信号 所以使用 GTK 编写 hello world 时 构件需要与信号进行绑定 信号和构件的绑定函数有两种 g s
  • armbian安装图形桌面_WSL安装图形界面并通过xrdp/X-Launch访问

    前言 虽然说直接使用WSL确实看着也挺酷的 但是总会有用到图形界面的时候吧 通过尝试 安装过gnome xfce4 lxde三款桌面 通过对比 gnome有明显的卡顿 lxde则是偏重于轻量级图形服务 本文主要通过安装xfce4来介绍流程
  • 云服务器挂载磁盘

    1 df h 查看磁盘并没有被挂载上 2 查看磁盘情况 fdisk l发现并没有分区 3 为磁盘分区 fdisk dev vdb 4 输入n 开始创建分区 5 输入p创建主分区 6 选择分区号 7 保存退出 8 fdisk l 检查是否分区
  • 【记录】数控程序的指令代码---标准G代码与标准M代码

    原文地址 http blog 163 com gaochengyi 521 blog static 51831416200793024555647 数控机床的运动是由程序控制的 而准备功能和辅助功能是程序段的重要组成部分 也是程序编制过程中
  • Ubantu搭建NFS服务器共享文件

    Ubantu搭建NFS服务器共享文件 搭建一台NFS服务器为局域网中的用户提供文件共享 NFS服务器要求如下 1 将本地文件系统的 home share目录共享 192 168 61 100与192 168 61 200两个客户机对该目录具
  • Vue 导入文件import、路径@和.的区别

    import html文件中 通过script标签引入js文件 而vue中 通过import xxx from xxx路径的方式导入文件 不光可以导入js文件 xxx 指的是为导入的文件起一个名称 不是指导入的文件的名称 相当于变量名 xx
  • for循环跳过某循环变量值的方法(非循环体内方法)

    今天QQ群里的一位群友问了个问题 一个 for int i 0 i
  • pytorch notes

    DataLoader torch utils data DataLoader 参数worker init fn 创建DataLoader需要传入Dataset对象 如果在Dataset中实现了worker init fn成员函数 则把这个函
  • linux中的shebang

    1 读作shebang或者sha bang 2 通常在unix系统的脚本的第一行开头使用 3 指明执行这个脚本文件的解释程序 4 步骤 使用which查询python3的解释器所在的路径 which python3 修改要运行的主pytho
  • 系统架构设计方法-4-数据架构设计篇

    数据架构设计工作内容 数据分布 数据实体和应用的对应关系 个人感觉 这里面写数据流转不太妥 流传应该都是应用层面来完成的 应用和数据之间的读写关系的组合 工作内容 1 确定数据域 系统架构设计模板和示例 工作内容 2 确定数据主题 系统设计
  • GitHub官方App正式推出了,小伙伴们确定不下载一个吗?

    小伙伴们 大家好 今天给大家推荐的是GitHub软件 微软旗下的 Github 也正式发布了 GitHub 移动版 它是 iOS 和 Android上对 GitHub 网页桌面版的完全体验版 现在 我们可以随时随地在移动设备上与我们的团队保
  • 尚硅谷Java零基础全套视频教程(宋红康2023版,java入门自学必备)

    尚硅谷Java零基础全套视频教程 宋红康2023版 java入门自学必备 开发环境 jdk17 idea2022 第一阶段 Java基本语法 一 Java语言概述 01 Java新版视频教程简介 02 课程目录说明 03 Java基础全栈学
  • 报表开发组件FastReport Mono v2023.1 - 支持与My Reports Cloud集成

    FastReport Mono v2023 1现已推出 最新版中更新了与 My Reports Cloud 的部分集成 来自 JasperReports 的模板转换器等功能 同时修复了10余处问题 点击下方免费试用哦 FastReport
  • URI中的 “//” 有什么用

    前言 很多时候互联网很多东西都是很有意思的 比如 http 这个双斜杠 解释这个东西就需要翻墙去国外了 入口 在2009年10月 BBC中的一篇新闻讲述了 的用途 截图如下所示 文章翻译 互联网地址开头的大幅删减长期以来一直困扰着网民 现在
  • JavaScript奇淫技巧:反调试

    JavaScript奇淫技巧 反调试 本文 将分享几种JS代码反调试技巧 目标是 实现防止他人调试 动态分析自己的代码 检测调试 方法一 用console log检测 代码 var c new RegExp 1 c toString fun
  • OpenCV+python实现摄像头简单手势识别--进度条控制亮度

    文章目录 前言 一 整体框架 二 使用步骤 1 引入库 2 第一步 打开摄像头 3 第二步 设置回调函数 4 第三步 肤色检测 5 第四步 进行高斯滤波 6 第五步 边缘轮廓检测 7 第六步 求出手势的凹凸点 8 第七步 利用凹凸点个数判断