OpenCV实例(二)手势识别

news2024/11/23 0:20:41

OpenCV实例(二)手势识别

  • 1.手势识别概述
    • 1.1.获取轮廓的凸包
    • 1.2.凸缺陷
    • 1.3.凸缺陷占凸包面积比
  • 2.手势识别过程
    • 2.1.识别流程
  • 3.石头、剪刀、布的识别

作者:Xiou

1.手势识别概述

手势识别的范围很广泛,在不同场景下,有不同类型的手势需要识别,例如:
● 识别手势所表示的数值。
● 识别手势在特定游戏中的含义,如“石头、剪刀、布”等。
● 识别手势在游戏中表示的动作,如前进、跳跃、后退等。
● 识别特定手势的含义,如表示“OK”的手势、表示胜利的手势等。

理论基础

凸包和凸缺陷在图像处理中具有非常重要的意义,被广泛地用于图像识别等领域。

逼近多边形是轮廓的高度近似,但是有时候,我们希望使用一个多边形的凸包来简化它。凸包和逼近多边形很像,只不过凸包是物体最外层的凸多边形。凸包指的是完全包含原有轮廓,并且仅由轮廓上的点构成的多边形。凸包的每一处都是凸的,即连接凸包内任意两点的直线都在凸包内部。在凸包内,任意连续三个点构成的面向内部的角的角度都小于180°。

OpenCV提供的函数cv2.convexHull()用于获取轮廓的凸包,其语法格式为:

hull=cv2.convexHull(points[,clockwise[,returnPoints]])

其中,返回值hull为凸包角点。该函数中的参数如下:
● points表示轮廓。
● clockwise为布尔型值;在该值为True时,凸包角点按顺时针方向排列;在该值为False时,凸包角点按逆时针方向排列。
● returnPoints为布尔型值,默认值是True,此时,函数返回凸包角点的坐标值;当该参数为False时,函数返回轮廓中凸包角点的索引。

1.1.获取轮廓的凸包

代码实例:使用函数cv2.convexHull()获取轮廓的凸包。

# -*- coding: utf-8 -*-
import cv2
# --------------读取并绘制原始图像------------------
o = cv2.imread('hand.bmp')  
cv2.imshow("original",o)
# --------------提取轮廓------------------
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)  
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)  
contours, hierarchy = cv2.findContours(binary,
                                             cv2.RETR_LIST,
                                             cv2.CHAIN_APPROX_SIMPLE)  
# --------------寻找凸包,得到凸包的角点------------------
hull = cv2.convexHull(contours[0])
# --------------绘制凸包------------------
cv2.polylines(o, [hull], True, (0, 255, 0), 2)
# --------------显示凸包------------------
cv2.imshow("result",o)
cv2.waitKey()
cv2.destroyAllWindows()

输出结果:
在这里插入图片描述

1.2.凸缺陷

凸包与轮廓之间的部分称为凸缺陷。凸缺陷示意图如图8-4所示,图中的白色四角星是前景,显然,其边缘就是其轮廓,连接四个顶点构成的四边形是其凸包。

通常情况下,使用如下四个特征值来表示凸缺陷:
● 起点:该特征值用于说明当前凸缺陷的起点位置。需要注意的是,起点值用轮廓索引表示。也就是说,起点一定是轮廓中的一个点,并且用其在轮廓中的序号来表示。例如,图8-4中的点A是凸缺陷1的起点。
● 终点:该特征值用于说明当前凸缺陷的终点位置。该值也是使用轮廓索引表示的。
● 轮廓上距离凸包最远的点。例如,图8-4中的点C是凸缺陷1中的轮廓上距离凸包最远的点。
● 最远点到凸包的近似距离。例如,图8-4中的距离D是凸缺陷1中的最远点到凸包的近似距离。OpenCV提供了函数cv2.convexityDefects()用来获取凸缺陷,其语法格式如下:

convexityDefects=cv2.convexityDefects(contour,convexhull)

需要说明的是,返回结果中[起点,终点,轮廓上距离凸包最远的点,最远点到凸包的近似距离]的前三个值是轮廓点的索引,所以需要从轮廓点集中找它们。
上述函数的参数如下:
● contour是轮廓。
● convexhull是凸包。

值得注意的是,用函数cv2.convexityDefects()计算凸缺陷时,要使用凸包作为参数。在查找该凸包时,函数cv2.convexHull()所使用的参数returnPoints的值必须是False。

为了更直观地观察凸缺陷点集,尝试将凸缺陷点集在一幅图内显示出来。实现方式为,将起点和终点用一条线连接,在最远点处绘制一个圆点。下面通过一个例子来展示上述操作。

代码实例:使用函数cv2.convexityDefects()计算凸缺陷。

# -*- coding: utf-8 -*-
import cv2
#----------------原图--------------------------
img = cv2.imread('hand.bmp')
cv2.imshow('original',img)
#----------------构造轮廓--------------------------
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 127, 255,0)
contours, hierarchy = cv2.findContours(binary,
                                             cv2.RETR_TREE,
                                 
            cv2.CHAIN_APPROX_SIMPLE)  
#----------------凸包--------------------------
cnt = contours[0]
hull = cv2.convexHull(cnt,returnPoints = False)
defects = cv2.convexityDefects(cnt,hull)
print("defects=\n",defects)
#----------------构造凸缺陷--------------------------
for i in range(defects.shape[0]):
    s,e,f,d = defects[i,0]
    start = tuple(cnt[s][0])
    end = tuple(cnt[e][0])
    far = tuple(cnt[f][0])
    cv2.line(img,start,end,[0,0,255],2)
    cv2.circle(img,far,5,[255,0,0],-1)
#----------------显示结果,释放图像--------------------------
cv2.imshow('result',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

输出结果:
在这里插入图片描述

1.3.凸缺陷占凸包面积比

当有0个凸缺陷时,手势既可能表示数值1,也可能表示数值0。因此,不能根据凸缺陷的个数判定此时的手势到底表示的是数值0还是数值1,需要寻找二者的其他区别。

代码实例:编写程序,利用表示数值0的手势和表示数值1的手势的凸缺陷面积差异,对二者进行识别。

# -*- coding: utf-8 -*-
import cv2
# 手势识别函数
def reg(x):
    #=================找出轮廓===============
    #查找所有轮廓
    x=cv2.cvtColor(x,cv2.COLOR_BGR2GRAY)
    contours,h = cv2.findContours(x,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
    #从所有轮廓中找到最大的,作为手势的轮廓
    cnt = max(contours,key=lambda x:cv2.contourArea(x))  
    areacnt = cv2.contourArea(cnt)   #获取轮廓面积
    #===========获取轮廓的凸包=============
    hull = cv2.convexHull(cnt)   #获取轮廓的凸包,用于计算面积,返回坐标
    areahull = cv2.contourArea(hull)   #获取凸包的面积
    #===========获取轮廓面积、凸包面积,二者的比值=============
    arearatio = areacnt/areahull  
    #通常情况下,手势0,轮廓和凸包大致相等,该值大于0.9.
    # 手势1,轮廓要比凸包小一些,该值小于等于0.9
    # 需要注意,这个不是特定值,因人而异,有的人手指长,有的人手指短
    # 所以,该值存在一定的差异
    if arearatio>0.9:     #轮廓面积/凸包面积>0.9,二者面积近似,识别为0
            result='fist:0'
    else:
            result='finger:1'  #对应:轮廓面积/凸包面积<=0.9,较大凸缺陷,识别为1
    return result 
# 读取两幅图像识别
x = cv2.imread('zero.jpg')  
y = cv2.imread('one.jpg')  
# 分别识别x和y
xtext=reg(x)
ytext=reg(y)
# 输出识别结果
org=(0,80)
font = cv2.FONT_HERSHEY_SIMPLEX
fontScale=2
color=(0,0,255)
thickness=3
cv2.putText(x,xtext,org,font,fontScale,color,thickness)
cv2.putText(y,ytext,org,font,fontScale,color,thickness)
# 显示识别结果
cv2.imshow('zero',x)
cv2.imshow('one',y)
cv2.waitKey()
cv2.destroyAllWindows()

输出结果:
在这里插入图片描述

由图可知,程序能够准确地识别出表示数值0(fist:0)和表示数值1(finger:1)手势的图像。

2.手势识别过程

2.1.识别流程

手势识别基本流程图如图所示:

在这里插入图片描述

下面,对各个步骤进行程序介绍。

● Step 1:获取图像。本步骤的主要任务是读取摄像头、划定识别区域。划定识别区域的目的在于仅识别特定区域内的手势,简化识别过程。

● Step 2:识别皮肤。本步骤的主要任务是色彩空间转换、在新的色彩空间内根据颜色范围值识别出皮肤所在区域。

色彩空间转换的目的在于将图像从BGR色彩空间转换到HSV色彩空间,以进行皮肤检测。通过皮肤颜色的范围值确定手势所在区域。

● Step 3:图像预处理。图像预处理主要是为了去除图像内的噪声,以便后续处理。这里的图像预处理包含膨胀操作和高斯滤波

● Step 4:获取轮廓。本步骤的主要任务在于获取图像的轮廓信息,并获取其面积。

● Step 5:获取凸包。本步骤的主要任务是获取轮廓的凸包信息,并获取其面积。

● Step 6:计算轮廓和凸包的面积比。本步骤的主要任务是计算轮廓和凸包的面积比。

● Step 7:获取凸缺陷。本步骤的主要任务是获取手势的凸缺陷。

● Step 8:计算并绘制有效凸缺陷。本步骤的主要任务是计算有效凸缺陷的个数,并绘制凸包、凸缺陷的最远点。

● Step 9:使用凸缺陷识别手势。本步骤的主要任务是根据凸缺陷的个数、凸缺陷与凸包的面积比进行手势识别。本步骤先对凸缺陷的个数进行判断,然后根据凸缺陷的个数判定当前手势的形状。有一个特例是,当凸缺陷的个数为0时,需要再对轮廓与凸包面积比进行判断,才能决定具体手势。

● Step 10:显示结果。本步骤的主要任务是将识别结果显示出来。

代码实例:

# -*- coding: utf-8 -*-
import cv2
import numpy as np
import math
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)    
#==============主程序======================
while(cap.isOpened()):
    ret,frame = cap.read() # 读取摄像头图像
    # print(frame.shape)   #获取窗口大小
    frame = cv2.flip(frame,1)   #沿着y轴转换下方向
    #===============设定一个固定区域作为识别区域=============
    roi = frame[10:210,400:600] # 将右上角设置为固定识别区域
    cv2.rectangle(frame,(400,10),(600,210),(0,0,255),0) # 将选定的区域标记出来
    #===========在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)  #设定范围,上限
    mask = cv2.inRange(hsv,lower_skin,upper_skin)   #确定手所在区域
    #===========预处理===============
    kernel = np.ones((2,2),np.uint8)   #构造一个核
    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,key=lambda x:cv2.contourArea(x))  
    areacnt = cv2.contourArea(cnt)   #获取轮廓面积
    #===========获取轮廓的凸包=============
    hull = cv2.convexHull(cnt)   #获取轮廓的凸包,用于计算面积,返回坐标
    # hull = cv2.convexHull(cnt,returnPoints=False)
    areahull = cv2.contourArea(hull)   #获取凸包的面积
    #===========获取轮廓面积、凸包的面积比=============
    arearatio = areacnt/areahull   
    # 轮廓面积/凸包面积 :
    # 大于0.9,表示几乎一致,是手势0
    # 否则,说明凸缺陷较大,是手势1.
    #===========获取凸缺陷=============
    hull = cv2.convexHull(cnt,returnPoints=False) #使用索引,returnPoints=False
    defects = cv2.convexityDefects(cnt,hull)    #获取凸缺陷
    #===========凸缺陷处理==================
    n=0 #定义凹凸点个数初始值为0 
    #-------------遍历凸缺陷,判断是否为指间凸缺陷--------------
    for i in range(defects.shape[0]):
        s,e,f,d, = defects[i,0]
        start = tuple(cnt[s][0])
        end = tuple(cnt[e][0])
        far = tuple(cnt[f][0])
        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)
        #--------计算手指之间的角度----------------
        angle = math.acos((b**2 + c**2 -a**2)/(2*b*c))*57
        #-----------绘制手指间的凸包最远点-------------
        #角度在[20,90]之间的认为是不同手指所构成的凸缺陷
        if angle<=90 and d>20:
            n+=1
            cv2.circle(roi,far,3,[255,0,0],-1)   #用蓝色绘制最远点
        #----------绘制手势的凸包--------------
        cv2.line(roi,start,end,[0,255,0],2) 
    #============通过凸缺陷个数及面积比判断识别结果=================
    if n==0:           #0个凸缺陷,可能为0,也可能为1
        if arearatio>0.9:     #轮廓面积/凸包面积>0.9,判定为拳头,识别为0
            result='0'
        else:
            result='1'   #轮廓面积/凸包面积<=0.9,说明存在很大的凸缺陷,识别为1
    elif n==1:        #1个凸缺陷,对应2根手指,识别为2
        result='2'
    elif n==2:        #2个凸缺陷,对应3根手指,识别为3
        result='3'
    elif n==3:        #3个凸缺陷,对应4根手指,识别为4
        result='4'
    elif n==4:        #4个凸缺陷,对应5根手指,识别为5
        result='5'
    #============设置与显示识别结果相关的参数=================
    org=(400,80)
    font = cv2.FONT_HERSHEY_SIMPLEX
    fontScale=2
    color=(0,0,255)
    thickness=3
    #================显示识别结果===========================
    cv2.putText(frame,result,org,font,fontScale,color,thickness)
    cv2.imshow('frame',frame)
    k = cv2.waitKey(25)& 0xff  
    if k == 27:     # 键盘Esc键退出
        break
cv2.destroyAllWindows()
cap.release()

输出结果:

在这里插入图片描述

3.石头、剪刀、布的识别

“石头、剪刀、布”是一种猜拳游戏,受到全世界人们的喜爱。该游戏如此流行,主要是因为它并非是纯靠运气的游戏,而是一种靠策略和智慧取胜的博弈。

形状匹配OpenCV提供了函数cv2.matchShapes()用来对两个对象的Hu矩进行比较。这两个对象可以是轮廓,也可以是灰度图像。函数cv2.matchShapes()的语法格式为:

retval=cv2.matchShapes(contour1,contour2,method,parameter)

其中,retval是返回值。该函数有如下4个参数。
● contour1:第1个轮廓或者灰度图像。
● contour2:第2个轮廓或者灰度图像。
● method:比较两个对象的Hu矩的方法.
● parameter:应用于method的特定参数,该参数为扩展参数,截至OpenCV 4.5.3-pre版本,暂不支持该参数,因此将该值设置为0。

代码实例:使用函数cv2.matchShapes()识别手势。

# -*- coding: utf-8 -*-

import cv2

def reg(x):
    o1 = cv2.imread('paper.jpg',1)
    o2 = cv2.imread('rock.jpg',1)
    o3 = cv2.imread('scissors.jpg',1)  
    gray1 = cv2.cvtColor(o1,cv2.COLOR_BGR2GRAY) 
    gray2 = cv2.cvtColor(o2,cv2.COLOR_BGR2GRAY) 
    gray3 = cv2.cvtColor(o3,cv2.COLOR_BGR2GRAY) 
    xgray = cv2.cvtColor(x,cv2.COLOR_BGR2GRAY) 
    ret, binary1 = cv2.threshold(gray1,127,255,cv2.THRESH_BINARY) 
    ret, binary2 = cv2.threshold(gray2,127,255,cv2.THRESH_BINARY) 
    ret, binary3 = cv2.threshold(gray3,127,255,cv2.THRESH_BINARY) 
    xret, xbinary = cv2.threshold(xgray,127,255,cv2.THRESH_BINARY) 
    contours1, hierarchy = cv2.findContours(binary1,
                                                  cv2.RETR_LIST,
                                                  cv2.CHAIN_APPROX_SIMPLE)  
    contours2, hierarchy = cv2.findContours(binary2,
                                                  cv2.RETR_LIST,
                                                  cv2.CHAIN_APPROX_SIMPLE)  
    contours3, hierarchy = cv2.findContours(binary3,
                                                  cv2.RETR_LIST,
                                                  cv2.CHAIN_APPROX_SIMPLE)  
    xcontours, hierarchy = cv2.findContours(xbinary,
                                                  cv2.RETR_LIST,
                                                  cv2.CHAIN_APPROX_SIMPLE)  
    cnt1 = contours1[0]
    cnt2 = contours2[0]
    cnt3 = contours3[0]
    x = xcontours[0]
    ret=[]
    ret.append(cv2.matchShapes(x,cnt1,1,0.0))
    ret.append(cv2.matchShapes(x,cnt2,1,0.0))
    ret.append(cv2.matchShapes(x,cnt3,1,0.0))
    max_index = ret.index(min(ret))  #计算最大值索引
    if max_index==0:
        r="paper"
    elif max_index==1:
        r="rock"
    else:
        r="sessiors"
    return r

t1=cv2.imread('test1.jpg',1)
t2=cv2.imread('test2.jpg',1)
t3=cv2.imread('test3.jpg',1)
# print(reg(t1))
# print(reg(t2))
# print(reg(t3))
# ===========显示处理结果==================
org=(0,60)
font = cv2.FONT_HERSHEY_SIMPLEX
fontScale=2
color=(255,255,255)
thickness=3
cv2.putText(t1,reg(t1),org,font,fontScale,color,thickness)
cv2.putText(t2,reg(t2),org,font,fontScale,color,thickness)
cv2.putText(t3,reg(t3),org,font,fontScale,color,thickness)
cv2.imshow('test1',t1)
cv2.imshow('test2',t2)
cv2.imshow('test3',t3)
cv2.waitKey()
cv2.destroyAllWindows()

输出结果:

在这里插入图片描述

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

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

相关文章

tp5实现导入excel表到数据库

hello&#xff0c;大家好&#xff0c;好长时间没有更新文章了。最近一直在忙着做项目。所以断更了。 那么好&#xff0c;各位老铁是否想要实现导入导出的功能 请关注我&#xff0c;解密如何实现导入导出&#xff0c; 那么今天先来讲一下用thinkphp5.0 如何实现Excel表格导入数据…

如何实现 Java SpringBoot 自动验证入参数据的有效性

Java SpringBoot 通过javax.validation.constraints下的注解&#xff0c;实现入参数据自动验证 如果碰到 NotEmpty 否则不生效&#xff0c;注意看下 RequestBody 前面是否加上了Valid Validation常用注解汇总 Constraint详细信息Null被注释的元素必须为 nullNotNull被注释的元…

CentOS7.6 磁盘挂载

CentOS7.6 磁盘挂载 目录CentOS7.6 磁盘挂载1.磁盘说明2.磁盘分区步骤1.磁盘说明 1、Linux硬盘分IDE硬盘和SCSI硬盘&#xff0c;目前基本上是SCSI硬盘 2、对于IDE硬盘&#xff0c;驱动器标识符为"hdx"&#xff0c;""代表分区&#xff0c;前四个分区用数字…

selenium1—软件测试

文章目录1.什么是自动化测试2.如何实施自动化测试3.UI自动化测试的好处4.自动化框架5.为什么要选择selenium6.webdrive的原理7.selenium IDE1.什么是自动化测试 在预设的条件下&#xff08;自动化脚本&#xff09;运行系统&#xff0c;预设的条件包括正常的和异常的情况&#…

BGP反射器与联邦实验

基础配置 display ip interface brief Interface IP Address/Mask Physical Protocol GigabitEthernet0/0/0 12.0.0.2/24 up up GigabitEthernet0/0/1 172.16.1.1/29 up up GigabitEthernet0/0/2 172.16.1.9/29 up up LoopBack0 172.16.2.1/24 up up(s) NULL0 unassigned up u…

AI 工具 22个使用场景、500个通用提问技巧说明

✏️ 「提问技巧—重点摘要」 「写作」&#xff1a;AI写作提问技巧 「制作视频」&#xff1a;AI制作视频创意提问相关技巧 【制定商业策略】&#xff1a;AI为各行各业制定商业策略提问技巧 【写邮件】&#xff1a;AI写邮件提问技巧 【框架方法论写内容】&#xff1a;AI利用…

【云原生概念和技术】1.1 云原生的概述

如果想了解或者学习云原生的友友们&#xff0c;欢迎订阅哦&#xff5e;&#x1f917;&#xff0c;目前一周三更&#xff0c;努力码字中&#x1f9d1;‍&#x1f4bb;…目前第一章是一些介绍和概念性的知识&#xff0c;可以先在脑海里有一个知识的轮廓&#xff0c;从第二章开始就…

Caffe

目录 1、简述 2、项目起源 3、架构设计 4、极智AI相关内容 1、简述 Caffe&#xff08;全称Convolutional Architecture for Fast Feature Embedding&#xff09;是一个兼具表达性、速度和思维模块化的深度学习框架&#xff0c;由伯克利人工智能研究小组和伯克…

小红书热词速看 | 古茗有何营销动作?

【导语】 据古茗官方数据&#xff0c;新系列推出当日即售空&#xff0c;单店最高出杯420杯&#xff0c;最快24小时内卖断货&#xff1b;上架3天&#xff0c;销量突破100万杯&#xff1b;10天&#xff0c;就售出了343万杯&#xff0c;其中2款牛油果奶昔用掉了40万斤牛油果&…

Hadoop完全分布式运行模式配置(开发重点)

目录 Hadoop是一个海量数据存储和计算的框架 Hadoop优点&#xff1a; 3.2 完全分布式运行模式&#xff08;开发重点&#xff09; 3.2.1 虚拟机准备 3.2.2 编写集群分发脚本xsync 3.2.3 SSH无密登录配置 3.2.4 集群配置 3.2.5 群起集群 3.2.6 配置历史服务器 3.2.7 配…

SpringBoot【运维实用篇】---- SpringBoot程序的打包与运行

SpringBoot【运维实用篇】---- SpringBoot程序的打包与运行程序打包程序运行SpringBoot程序打包失败处理命令行启动常见问题及解决方案刚开始做开发学习的小伙伴可能在有一个知识上面有错误的认知&#xff0c;我们天天写程序是在Idea下写的&#xff0c;运行也是在Idea下运行的。…

【从零开始学Skynet】基础篇(七):Mysql数据库常用API

在上一篇中我们完成了对Mysql数据库的准备工作之后&#xff0c;这一篇我们写一个程序测试一下。 1、Mysql API 在写程序之前&#xff0c;我们先学习一下Mysql数据库常用API的使用&#xff1a; API说明mysql.connet(args)连接数据库&#xff0c;参数args是一个Lua表&#xff0c…

责任链设计模式(Chain of Responsibility Pattern)[论点:概念、组成角色、图示、相关代码、框架中的运用、适用场景]

文章目录概念组成角色相关图示示例代码框架中的应用适用场景&#xff1a;概念 责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;是一种行为设计模式&#xff0c;其主要目的是将请求的发送者和接收者解耦。这种模式创建了一系列处理器对象&#xff0c;每个处…

Shell Script

目录Shell Script的概述Shell的主要版本Shell脚本的建立与执行Shell中的变量Shell中的特殊字符通配符双引号倒引号&#xff08;&#xff09;顺序分隔符&#xff08;&#xff1b;&#xff09;管道符逻辑与&#xff08;&&&#xff09;和逻辑或&#xff08;||&#xff09;…

synchronized原理详解

众所周知&#xff0c;使用多线程可以极大地提升程序的性能&#xff0c;但如果多线程使用不合理&#xff0c;也会带来很多不可控的问题&#xff0c;例如线程安全问题。 什么是线程安全问题呢&#xff1f;如果多个线程同时访问某个方法时&#xff0c;这个方法无法得到我们预期的…

同步FIFO、异步FIFO详细介绍、verilog代码实现、FIFO最小深度计算、简答题

文章目录前言一、多bit数据流跨时钟域传输——FIFO1、FIFO分类2、常见参数3、与普通存储器的区别4、FIFO优缺点二、同步FIFO1、计数器法2、高位扩展法3、单端口和双端口RAM3.1 单端口RAM3.2 双端口RAM4、例化双端口RAM实现同步FIFO三、异步FIFO1、格雷码1.1 二进制和格雷码之间…

spring5(五):AOP操作

spring5&#xff08;五&#xff09;&#xff1a;AOP操作前言一、代理模式1、场景模拟2、代理模式2.1 概念2.2 静态代理2.3 动态代理二、AOP概述1、什么是 AOP?2、相关术语3、作用三、AOP底层原理1、AOP 底层使用动态代理2、AOP&#xff08;JDK 动态代理&#xff09;2.1 编写 J…

VR全景展示,VR全景平台,助理全景展示新模式

引言&#xff1a; VR全景展示是一种新型的展示方式&#xff0c;它利用虚拟现实技术和全景拍摄技术&#xff0c;使参观者可以身临其境地进入虚拟展览空间。这种展示方式不仅能够提供更加沉浸式的参观体验&#xff0c;还可以解决传统展览所面临的时间和地域限制等问题。 VR全景展…

【Java实战篇】Day7.在线教育网课平台

文章目录一、需求&#xff1a;课程审核1、需求分析2、建表与数据模型3、接口定义4、Mapper层开发5、Service层开发6、完善controller层二、需求&#xff1a;课程发布1、需求分析2、建表与数据模型3、技术方案4、接口定义5、消息处理SDK6、Mapper层开发7、Service层开发8、页面静…

unity,射手游戏

文章目录介绍一&#xff0c;制作玩家具体函数脚本PlayerCharacter三、 制作玩家控制脚本 PlayerController&#xff0c;调用上面的函数方法四、 制作子弹脚本 shell五、 给玩家挂载脚本六、 制作坦克脚本七、 给坦克添加组件八、 开始游戏&#xff0c;播放动画九、 下载介绍 3…