基于cv2的手势识别-计算机视觉

news2024/11/15 13:02:10

  闲的无聊做的一个小玩意,可以调用你的计算机相机,识别框内的手势(剪刀、石头和布),提供一个判决平台,感兴趣的可以继续完善。
用到的参考小文献:
在这里插入图片描述

具体实现结果如下

在这里插入图片描述
在这里插入图片描述
并且我另写了一个框架平台,可以进行下一步的功能拓展,发在我的资源界面了;
在这里插入图片描述

手势识别与控制系统

简介

  我们系统的手势识别与控制功能主要采用 OpenCV库实现 , OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库, 可以运行在Linux, Windows, Android和Mac-OS操作系统上. 它轻量级而且高效—由一系列 C 函数和少量 C++类构成, 同时提供了Python, Ruby, MATLAB等语言的接口, 实现了图像处理和计算机视觉方面的很多通用算法。
  本系统主要使用了OpenCV的视频采集, 图像色域转换, 颜色通道分割, 高斯滤波, OSTU自动阈值, 凸点检测, 边缘检测, 余弦定理计算手势等功能。

手势检测:#pic_center

水平翻转

设置ROI

   ROI是region of interest首字母的简写,翻译为感性趣的区域。ROI是在一定图片中取一小部分进行重点处理的区域。图片就是一个二维数组,把数组分块求取就可以得到你想要的ROI区域。

定义肤色范围

   人为定义RGB颜色范围,截取红黄蓝三原色上下限选出符合人肤色的区域。

提取肤色

  在ROI选区范围内提取出肤色转化为灰度图像
  在进行颜色空间转换时,RGD各个通道的范围应当根据实际  需要来进行归一化,如RGB转LUV通道时,需要将RGB归一化为32位浮点数,即各通道的值的变化范围为0到1,转化为HSV时也是这样。
  RGB空间下标(R、G、B)代表着相应的颜色空间,且R、G、B∈(0,1).OpenCV中从RGB颜色空间转换为HSV颜色空间按照下式进行:
V = m a x ( R , G , B ) V=max(R,G,B) V=max(R,G,B)
f ( x ) = { V − min ⁡ ( R , G , B ) V , i f V ≠ 0 0 , i f otherwise f(x)=\begin{cases}\dfrac{V-\min(R,G,B)}{V},&\quad if V\neq0\\ 0,&\quad if \textit{otherwise}\end{cases} f(x)= VVmin(R,G,B),0,ifV=0ifotherwise
H = { 60 ( G − B ) V − min ( R , G , B ) , i f V = R 120 + 60 ( B − R ) V − min ( R , G , B ) , i f V = G 250 + 60 ( R − G ) V − min ( R , G , B ) , i f V = B H=\begin{cases}\dfrac{60(G-B)}{V-\text{min}(R,G,B)},ifV=R\\ 120+\dfrac{60(B-R)}{V-\text{min}(R,G,B)},ifV=G\\ 250+\dfrac{60(R-G)}{V-\text{min}(R,G,B)},ifV=B\end{cases} H= Vmin(R,G,B)60(GB),ifV=R120+Vmin(R,G,B)60(BR),ifV=G250+Vmin(R,G,B)60(RG),ifV=B
HSV色表如下:
在这里插入图片描述

图形经过膨胀与腐蚀后进行高斯滤波模糊图像

实现原理
  图像的膨胀(Dilation)和腐蚀(Erosion)是两种基本的形态学运算,主要用来寻找图像中的极大区域和极小区域。其中膨胀类似于“领域扩张”,将图像中的高亮区域或白色部分进行扩张,其运行结果图比原图的高亮区域更大;腐蚀类似于“领域被蚕食”,将图像中的高亮区域或白色部分进行缩减细化,其运行结果图比原图的高亮区域更小。
  膨胀的运算符是”⊕”,其定义如下:
A ⊕ B = { x ∣ ( B ) x ∩ A ≠ Θ } \mathrm{A}\oplus\mathrm{B}=\left\{\mathrm{x}\left|\left(\mathrm{B}\right)_x\cap\mathrm{A}\neq\Theta\right\}\right. AB={x(B)xA=Θ}
  该公式表示用B来对图像A进行膨胀处理,其中B是一个卷积模板或卷积核,其形状可以为正方形或圆形,通过模板B与图像A进行卷积计算,扫描图像中的每一个像素点,用模板元素与二值图像元素做“与”运算,如果都为0,那么目标像素点为0,否则为1。从而计算B覆盖区域的像素点最大值,并用该值替换参考点的像素值实现膨胀。
图像腐蚀
  该公式表示图像A用卷积模板B来进行腐蚀处理,通过模板B与图像A进行卷积计算,得出B覆盖区域的像素点最小值,并用这个最小值来替代参考点的像素值。
  a. 图像二值化处理,将图像的灰度值根据阈值进行0,1处理得到的图像;
  b. 卷积核,对应信号处理中的高低频滤波器。常用numpy函数去设置,np.ones((m,n), np.uint8) 表示指定m*n的卷积核;
  c. 图像的膨胀或腐蚀,cv2.dilate(二值化图像, 卷积核, 迭代次数),完成图片的高斯滤波模糊。

轮廓检测和查找最大面积轮廓

  轮廓处理的话主要用到函数cv2.findContours和这两个函数的使用使用方法很容易搜到就不说了,这部分主要的问题是提取到的轮廓有很多个,但是我们只需要手的轮廓,所以我们要用sorted函数找到最大的轮廓。

估算轮廓然后绘制轮廓曲线

查找手指凸包

  凸包(Convex Hull)是一个计算几何(图形学)中的概念,它的严格的数学定义为:在一个向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包。   
  在图像处理过程中,我们常常需要寻找图像中包围某个物体的凸包。凸包跟多边形逼近很像,只不过它是包围物体最外层的一个凸集,这个凸集是所有能包围这个物体的凸集的交集。
  在opencv中,通过函数convexHulll能很容易的得到一系列点的凸包,比如由点组成的轮廓,通过convexHull函数,我们就能得到轮廓的凸包。寻找图像的凸包,就能够实现手势识别的功能。
对如下手势进行凸包查找:
在这里插入图片描述

手指凹陷查找{求三角形三边长度、点和曲线凸处的距离、余弦定理、去噪、手指轮廓描述曲线}

  利用opencv提供的 convexityDefects 凹点检测函数检测图像凹陷的点, 然后利用, 然后根据凹陷点中的 (开始点, 结束点, 远点)的坐标, 利用余弦定理计算两根手指之间的夹角, 其必为锐角, 根据锐角的个数判别手势.
  其中,锐角个数为0 ,表示 手势是 拳头 或 一,
    锐角个数为0 ,表示 手势是 拳头 或 一,
    锐角个数为1 ,表示 手势是 剪刀
    锐角个数为2 ,表示 手势是 三,
    锐角个数为3 ,表示 手势是 四,
    锐角个数为4 ,表示 手势是 布
  具体原理如下:利用findContours检测图像中的轮廓, 其中返回值contours包含了图像中所有轮廓的坐标点,它们的值分别为起点,终点,最远的点,到最远点的近似距离。根据图像中凹凸点中的 (开始点, 结束点, 远点)的坐标, 利用余弦定理计算两根手指之间的夹角, 其必为锐角, 根据锐角的个数判别手势。

输出手势

划拳(三次训练):

  • 构造3*3的结构元素,得到ROI分析块
  • 将膨胀后的图像和腐蚀相减得到轮廓边(灰度图)
  • 将上面结果二进制化并进行反色
  • 对反色结果进行中值滤波
  • 计算手势面积
  • 设定一定的判定时间,等到倒计时结束的时候将取得的图片进行处理和判定,经过训练三次得到判定的结果。

代码实现过程

import cv2
import time
import os

def judge():
    # 构造一个3×3的结构元素
    # return 0 stone ,1 jiandao, 2 bu
    img = cv2.imread("wif.jpg", 0)

    element = cv2.getStructuringElement(cv2.MORPH_RECT, (11, 11))  # 矩形:MORPH_RECT  交叉形:MORPH_CROSS  椭圆形:MORPH_ELLIPSE

    dilate = cv2.dilate(img, element)
    erode = cv2.erode(img, element)

    # 将两幅图像相减获得边,第一个参数是膨胀后的图像,第二个参数是腐蚀后的图像
    result = cv2.absdiff(dilate, erode);

    # 上面得到的结果是灰度图,将其二值化以便更清楚的观察结果
    retval, result = cv2.threshold(result, 40, 255, cv2.THRESH_BINARY);

    # 反色,即对二值图每个像素取反 图像非运算的效果:一个二值图,将黑色转为白色,白色转为黑色。
    result = cv2.bitwise_not(result);

    # 非线性过滤——中值滤波
    result = cv2.medianBlur(result, 23)

    #  计算手势面积
    a = []
    posi = []
    width = []
    count = 0
    area = 0

    for i in range(result.shape[1]):

        for j in range(result.shape[0]):

            if (result[j][i] == 0):

                area += 1

    for i in range(result.shape[1]):

        if (result[5 * result.shape[0] // 16][i] == 0 and result[5 * result.shape[0] // 16][i - 1] != 0):

            count += 1

            width.append(0)

            posi.append(i)

        if (result[5 * result.shape[0] // 16][i] == 0):

            width[count - 1] += 1  # 如果在这里报错,是因为背景问题,请让手的背景尽量整洁

    print('the pic width is ', result.shape[1], '\n')

    for i in range(count):

        print('the ', i, 'th', ' ', 'is')

        print('width ', width[i])

        print('posi ', posi[i], '\n')

    print(count, '\n')

    print('area is ', area, '\n')

    cv2.line(result, (0, 5 * result.shape[0] // 16), (214, 5 * result.shape[0] // 16), (0, 0, 0))

    cv2.namedWindow("fcuk")

    cv2.imshow("fcuk", result)

    cv2.waitKey(0)

    # 判定时间
    width_length = 0

    width_jiandao = True

    for i in range(count):

        if width[i] > 45:

            return 2;

        if width[i] <= 20 or width[i] >= 40:

            width_jiandao = False

        width_length += width[i]

    if width_jiandao == True and count == 2:

        return 1;

    if (area < 8500):

        print('shi tou')

        return 0;

    print("width_leng", width_length)

    if (width_length < 35):

        # 这个时候说明照片是偏下的,所以需要重新测定。
        a = []
        posi = []
        width = []
        count = 0

        for i in range(result.shape[1]):

            if (result[11 * result.shape[0] // 16][i] == 0 and result[11 * result.shape[0] // 16][i - 1] != 0):

                count += 1

                width.append(0)

                posi.append(i)

            if (result[11 * result.shape[0] // 16][i] == 0):

                width[count - 1] += 1

    width_length = 0

    width_jiandao = True

    for i in range(count):

        if width[i] > 45:

            print('bu1')

            return 2;

        if width[i] <= 20 or width[i] >= 40:

            width_jiandao = False

        width_length += width[i]

    if width_jiandao == True and count == 2:

        return 1;

    if (area > 14000 or count >= 3):

        print('bu2')

        return 2;

    if (width_length < 110):

        print('jian dao')

        return 1;

    else:

        print('bu3')

        return 2;


def show():

    box = []

    box.append("石头")

    box.append("剪刀")

    box.append("布")

    capture = cv2.VideoCapture(0)

    cv2.namedWindow("camera", 1)

    start_time = time.time()

    while (1):

        ha, img = capture.read()

        end_time = time.time()

        cv2.rectangle(img, (426, 0), (640, 250), (0, 255, 0))

        # (426, 0), (640, 250), 分别代表左上角和右下角的两个坐标点 (0, 255, 0)分别代指B G R
        cv2.putText(img, str(int(5 - (end_time - start_time))), (100, 100), cv2.FONT_HERSHEY_TRIPLEX, 2, 255, 0)
        # cv2.putText(图片, 添加的文字,左上角坐标,字体,字体大小,颜色,字体粗细)
        # cv2.FONT_HERSHEY_SIMPLEX 字体效果 https://www.pianshen.com/article/2216678823/

        cv2.imshow("camera", img)

        if (end_time - start_time > 5):

            break

        if (cv2.waitKey(30) >= 0):

            break

    ha, img = capture.read()

    capture.release()

    cv2.imshow("camera", img)

    img = img[0:210, 426:640]

    cv2.imwrite("wif.jpg", img)

    p1 = judge()

    print('你出的是',box[p1],'\n')

    cv2.destroyAllWindows()

def main():

    i=0

    while (i<5):

        i=i+1
        #  清空屏幕
        os.system('cls')

        show()

main()

编写不易,求个点赞!!!!!!!
“你是谁?”

“一个看帖子的人。”

“看帖子不点赞啊?”

“你点赞吗?”

“当然点了。”

“我也会点。”

“谁会把经验写在帖子里。”

“写在帖子里的那能叫经验贴?”

“上流!”
cheer!!!

在这里插入图片描述

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

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

相关文章

风雨30年,电子表格惊人跨越,excel用户:表格都能生成软件了

电子表格&#xff0c;绝不是你看到的样子&#xff01; 你知道电子表格的前世今生吗&#xff1f; 它绝不只是你现在看到Excel或者WPS的模样。 不同的时代&#xff0c;有着不同的样子 DOS时代没有什么“可见可得”。什么都是靠想象力的&#xff0c;就是屏幕上文档的显示和打印…

供应商管理怎么做?供应商管理办法及流程介绍

阅读本文你将了解&#xff1a;1、供应商管理的重要性&#xff08;供应商管理的痛点&#xff09;&#xff1b;2、供应商管理具体流程&#xff1b;供应商管理系统有什么作用。 一、供应商管理的重要性&#xff08;供应商管理的痛点&#xff09; 供应商管理是组织中至关重要的一环…

锂溶液净化和提纯

锂离子电池是一种充电电池&#xff0c;依靠锂离子在正极和负极之间移动来工作&#xff0c;广泛应用在便携式设备、卫星、储备电源、电动汽车等领域&#xff0c;具有替代各种二次电源的潜力。 近年来国家大力提倡和发展的新能源产业&#xff0c;锂离子电池的需求量的不断攀升&a…

三维实景模型在线浏览平台,在线编辑、在线分享各类地理空间数据

很多行业用户在获取了大量的三维实景模型数据后&#xff0c;想要进行在线浏览或者分享给他人查看&#xff0c;以往只能通过人工手动将模型压缩&#xff0c;不但耗时费力&#xff0c;效果还难以达到预期。 其实&#xff0c;只需要通过四维轻云地理空间数据云管理平台就能轻松实…

DataX数据同步工具使用

1.DataX 简介 DataX 是阿里云 DataWorks 数据集成 的开源版本&#xff0c;主要就是用于实现数据间的离线同步。 DataX 致力于实现包括关系型数据库&#xff08;MySQL、Oracle 等&#xff09;、HDFS、Hive、ODPS、HBase、FTP 等 各种异构数据源&#xff08;即不同的数据库&…

# Linux shell终端设置代理服务器的方法

Linux shell终端设置代理服务器的方法 文章目录 Linux shell终端设置代理服务器的方法1 变量列表2 设置方法2.1 设置代理2.2 测试代理 3 软件专用代理3.1 yum专用代理3.2 git专用代理3.3 wget专用代理3.4 curl专用代理3.5 pip专用代理3.6 aria2c专用代理 4 最后 通过设置Linux变…

三个方法教你快速找到LinkedIn领英的潜在客户(置顶收藏)

第三点–最后一点是重点&#xff0c;要看到最后 相信很多外贸业务员都有这样的问题&#xff1a;“为什么你运营的账户询问的人数那么多&#xff0c;我自己运营的账户却没有人问呢&#xff1f;你是不是有什么好的运营技巧啊&#xff0c;可以给我说一下吗&#xff1f;”事实上&a…

Shell编程循环语句for while until(心有所觉,但亦做不解)

一、for 循环 1.用法和特点 读取不同的变量值&#xff0c;用来逐个执行同一组命令 for循环经常使用在已经知道要进行多少次循环的场景 for 变量 in 取值列表 -- 默认取值分割符&#xff08;空格、制表符、换行符&#xff09;do 或 {命令序列 done 或 }2.执行指…

时序预测 | Matlab实现SSA-BiLSTM、BiLSTM麻雀算法优化双向长短期记忆神经网络时间序列预测(含优化前后对比)

时序预测 | Matlab实现SSA-BiLSTM、BiLSTM麻雀算法优化双向长短期记忆神经网络时间序列预测(含优化前后对比) 目录 时序预测 | Matlab实现SSA-BiLSTM、BiLSTM麻雀算法优化双向长短期记忆神经网络时间序列预测(含优化前后对比)预测效果基本介绍程序设计参考资料 预测效果 基本介…

数学中为什么要研究各种各样的变换?

从信号处理角度来说 分析平稳信号所蕴涵的信息, 一般地Fourier 变换就能应付自如。但以不稳定动力系统为特征的物理世界, 信号往往具有如下特点: 非平稳、非线性、非确定、非可积、非连续、非光滑、非周期、非对称等等。使用Fourier 变换分析、解释非平稳信号, 就显得无能为力…

【日志系列】日志框架Log4j2源码解析

初始化 LoggerFactory private static final Logger logger LoggerFactory.getLogger(LogFilter.class);LoggerFactory#getLogger() public static Logger getLogger(Class<?> clazz) {Logger logger getLogger(clazz.getName());if (DETECT_LOGGER_NAME_MISMATCH) {…

设计模式 Map+函数式接口减少if else

参考资料 代码优雅之道——如何干掉过多的if else 目录 一. 前期准备1.1 标记邮箱种类的接口1.2 邮箱类型区分类1.3 入参Form实体类 二. 邮件发送的业务聚合类三. 定义函数式接口&#xff0c;创建邮件发送的Map四. 效果 一. 前期准备 1.1 标记邮箱种类的接口 import java.la…

一文让你真正了解正则表达式

1 正则表达式是什么 正则表达式(Regular Expression)其实就是一门工具&#xff0c;目的是为了字符串模式匹配&#xff0c;从而实现搜索和替换功能。它起源于上个20世纪50年代科学家在数学领域做的一些研究工作&#xff0c;后来才被引入到计算机领域中。从它的命名我们可以知道…

3自由度并联绘图机器人实现写字功能(二)

1. 功能说明 本文示例将实现R305b样机3自由度并联绘图机器人写字的功能。本实验使用的样机是用探索者兼容零件制作的。 2. 电子硬件 在这个示例中&#xff0c;采用了以下硬件&#xff0c;请大家参考&#xff1a; 主控板 Basra主控板&#xff08;兼容Arduino Uno&#xff09; 扩…

Visual Studio C# WinForm开发入门(5):TabControl 控件使用

TabContrl选项卡控件可创建标签化窗口&#xff0c;在实际 编程中经常用到&#xff0c;该控件的作用是将相关的组件组合到一系列选项卡页面上。 比如下面的例子&#xff0c;在tabPage1页面和tabPage2页面各放了2个checkBox控件&#xff0c;通过点击不同page即可切换&#xff1a;…

交叉编译工具

工具链有一个松散的名称约定&#xff0c;如 arch[-vendor][-os]-abi-language . arch 适用于架构&#xff0c;编译器用于哪个目标平台&#xff1a; arm &#xff0c; mips &#xff0c; x86 &#xff0c; i686 ... vendor 是工具链供应商&#xff0c;以厂家名称命名的&#xf…

权威学者、企业CFO荟聚上海国家会计学院,共探「智能会计 价值财务」

4月21日&#xff0c;由用友主办的「智能会计 价值财务」2023企业数智化财务创新峰会在上海国家会计学院圆满举办。学院权威教授、业内专家与来自央国企、行业领先企业的财务先锋&#xff0c;线下云端共聚一堂&#xff0c;数万人共探大型企业财务数智化的全新价值主张。 会议伊始…

WLAN - 五大安全措施

文章目录 1 概述2 五大安全措施2.1 SSID 访问控制2.2 物理地址过滤 MAC2.3 有线等效保密 WEP2.4 WPA&#xff08;IEEE 802.11i 草案&#xff09;2.5 WPA2&#xff08;IEEE 802.11i&#xff09; 3 扩展3.1 网工软考真题 1 概述 无线局域网面临着两个主要问题&#xff0c;一是增…

【Unity入门】19.定时调用Invoke

【Unity入门】定时调用Invoke 大家好&#xff0c;我是Lampard~~ 欢迎来到Unity入门系列博客&#xff0c;所学知识来自B站阿发老师~感谢 &#xff08;一&#xff09;计时器 &#xff08;1&#xff09;Invoke 单词调用 计时器我们并不陌生&#xff0c;在cocos上有着schedule类是…

深度学习 - 45.MMOE Gate 简单实现 By Keras

目录 一.引言 二.MMoE 模型分析 三.MMoE 逻辑实现 • Input • Expert Output • Gate Output • Weighted Sum • Sigmoid Output • 完整代码 四.总结 一.引言 上一篇文章介绍了 MMoE 借鉴 MoE 的思路&#xff0c;为每一类输出构建一个 Gate 并最终加权多个 Exper…