打开摄像头,读取视频流
发布时间:2025-06-24 18:19:45 作者:北方职教升学中心 阅读量:576
食指和两者之间的连线,并使用勾股定理计算了两个指尖之间的长度。初始化 HandControlVolume 类
classHandControlVolume:def__init__(self):""" 初始化 HandControlVolume 类的实例 初始化 mediapipe 对象,用于手部关键点检测和手势识别。对每一帧图像进行处理: - 转换图像为RGB格式。
- pycaw:pycaw库,用于控制电脑音量。
遍历results.multi_hand_landmarks
中的每个hand_landmarks,即遍历每个检测到的手掌。
- 打开摄像头,读取视频流。
# 提高性能image.flags.writeable =False# 转为RGBimage =cv2.cvtColor(image,cv2.COLOR_BGR2RGB)# 镜像image =cv2.flip(image,1)# mediapipe模型处理results =hands.process(image)image.flags.writeable =Trueimage =cv2.cvtColor(image,cv2.COLOR_RGB2BGR)
3.判断是否有手掌
判断results.multi_hand_landmarks
是否存在,即是否检测到手掌。
self.volume.SetMasterVolumeLevel方法
将音量值vol设置为电脑的音量。
这些优化操作旨在提高程序的性能和效率。
绘制大拇指和食指之间的连线。
计算大拇指指尖和食指指尖的中间点坐标。
# 解析手指,存入各个手指坐标landmark_list =[]forlandmark_id,finger_axis inenumerate(hand_landmarks.landmark):landmark_list.append([landmark_id,finger_axis.x,finger_axis.y,finger_axis.z ])iflandmark_list:# 获取大拇指指尖坐标thumb_finger_tip =landmark_list[4]thumb_finger_tip_x =math.ceil(thumb_finger_tip[1]*resize_w)thumb_finger_tip_y =math.ceil(thumb_finger_tip[2]*resize_h)# 获取食指指尖坐标index_finger_tip =landmark_list[8]index_finger_tip_x =math.ceil(index_finger_tip[1]*resize_w)index_finger_tip_y =math.ceil(index_finger_tip[2]*resize_h)# 中间点finger_middle_point =(thumb_finger_tip_x +index_finger_tip_x)//2,(thumb_finger_tip_y +index_finger_tip_y)//2# print(thumb_finger_tip_x)thumb_finger_point =(thumb_finger_tip_x,thumb_finger_tip_y)index_finger_point =(index_finger_tip_x,index_finger_tip_y)# 画指尖2点image =cv2.circle(image,thumb_finger_point,10,(255,0,255),-1)image =cv2.circle(image,index_finger_point,10,(255,0,255),-1)image =cv2.circle(image,finger_middle_point,10,(255,0,255),-1)# 画2点连线image =cv2.line(image,thumb_finger_point,index_finger_point,(255,0,255),5)# 勾股定理计算长度line_len =math.hypot((index_finger_tip_x -thumb_finger_tip_x),(index_finger_tip_y -thumb_finger_tip_y))
5.获取电脑最大最小音量
实现获取电脑的最大和最小音量,并将指尖的长度映射到音量范围和矩形显示上,然后将映射后的音量值设置为电脑的音量。
np.interp函数
将指尖的长度line_len映射到从50到300的范围,再映射到从0到100的范围,得到矩形百分比显示的数值rect_percent_text。
对图像进行水平翻转,即镜像操作,以使图像更符合常见的镜像显示。
功能:
- 初始化mediapipe和音量控制模块,获取音量范围。
- 使用mediapipe检测手部关键点。其中,将图像的可写标志设置为False可以减少不必要的内存拷贝,转换图像的格式和镜像操作则是为了符合MediaPipe模型的输入要求和更好地进行手势识别。 """
# 初始化 medialpipeself.mp_drawing =mp.solutions.drawing_utils self.mp_drawing_styles =mp.solutions.drawing_styles self.mp_hands =mp.solutions.hands # 获取电脑音量范围devices =AudioUtilities.GetSpeakers()interface =devices.Activate(IAudioEndpointVolume._iid_,CLSCTX_ALL,None)self.volume =cast(interface,POINTER(IAudioEndpointVolume))self.volume.SetMute(0,None)self.volume_range =self.volume.GetVolumeRange()
- 初始化 mediapipe 对象,用于手部关键点检测和手势识别。
遍历手部关键点的每个元素,将每个关键点的id、使用
cap.read()
函数从视频流中读取一帧图像,返回的success
表示是否读取成功,image
则是读取到的图像。绘制大拇指和食指的指尖点,以及中间点。
使用MediaPipe模型对图像进行处理,得到结果。
将图像的可写标志image.flags.writeable设置为True,以重新启用对图像的写入操作。
六、
# 获取电脑最大最小音量min_volume =self.volume_range[0]max_volume =self.volume_range[1]# 将指尖长度映射到音量上vol =np.interp(line_len,[50,300],[min_volume,max_volume])# 将指尖长度映射到矩形显示上rect_height =np.interp(line_len,[50,300],[0,200])rect_percent_text =np.interp(line_len,[50,300],[0,100])# 设置电脑音量self.volume.SetMasterVolumeLevel(vol,None)
6.显示矩形
cv2.putText函数
来在图像上显示矩形框的百分比值;cv2.rectangle函数
来绘制矩形框并填充颜色;cv2.putText函数
来在图像上显示当前帧的刷新率FPS;cv2.imshow函数
来显示处理后的图像;cv2.waitKey函数
等待按键输入,当按下ESC键或关闭窗口时退出程序;HandControlVolume
类的recognize方法调用了手势识别的功能。
判断landmark_list是否不为空,如果不为空,继续执行下面的代码。如果存在,则继续执行下面的代码。y和z坐标存储在一个列表中,然后将该列表添加到landmark_list中。
np.interp函数
将指尖的长度line_len映射到从50到300的范围,再映射到最小音量和最大音量的范围,得到音量值vol。
cv2.VideoCapture()函数参数问题
这并没有错。
# 主函数defrecognize(self):# 计算刷新率fpsTime =time.time()# OpenCV读取视频流cap =cv2.VideoCapture(0)# 视频分辨率resize_w =640resize_h =480# 画面显示初始化参数rect_height =0rect_percent_text =0withself.mp_hands.Hands(min_detection_confidence=0.7,min_tracking_confidence=0.5,max_num_hands=2)ashands:whilecap.isOpened():success,image =cap.read()image =cv2.resize(image,(resize_w,resize_h))ifnotsuccess:print("空帧.")continue
2.提高性能
将图像的可写标志image.flags.writeable设置为False,以便进行内存优化。但在树莓派上调用时需要更改参数,改为:
cap =cv2.VideoCapture(1)
调用电脑摄像头时:
电脑在用cv2.VideoCapture(0)
时,程序结束后会有报错:[ WARN:0] SourceReaderCB::~SourceReaderCB terminating async callback
需要改为:
cv2.VideoCapture(0,cv2.CAP_DSHOW)
二、最后,将图像转换回BGR格式是为了与OpenCV的显示函数兼容。
将图像从RGB格式转换回BGR格式,以便后续的显示和处理。
使用
OpenCV
打开视频流,此处读取摄像头设备,默认使用设备ID为0。- 显示处理后的图像。
- 将手势距离转换为音量大小,并控制电脑音量。
- 获取电脑音量接口,并获取音量范围。
从landmark_list中获取食指指尖坐标的列表项,然后计算出在图像上的像素坐标。
np.interp函数
将指尖的长度line_len映射到从50到300的范围,再映射到从0到200的范围,得到矩形的高度rect_height。然后,根据手指的坐标计算出大拇指和食指的指尖坐标,以及两者的中间点坐标。如果读取失败,则打印提示信息并继续下一次循环。- 如果检测到手部关键点:
- 在图像中标注手指关键点和手势连线。
创建一个空的landmark_list列表用于存储手指坐标。
导入库:
- cv2:OpenCV库,用于读取摄像头视频流和图像处理。
- 检测到手指关键点时,将索引指为0的关键点作为拇指的指尖,索引指为1的关键点作为食指的指尖。
设置视频流的分辨率为指定的
resize_w和resize_h
大小,并将图像resize为该尺寸。使用勾股定理计算大拇指指尖和食指指尖之间的长度,保存在line_len中。mediapipe、接下来,绘制了大拇指、主函数
1.计算刷新率
初始化刷新率的计算,记录当前时间作为初始时间。
进入循环,判断视频流是否打开。
# 显示矩形cv2.putText(image,str(math.ceil(rect_percent_text))+"%",(10,350),cv2.FONT_HERSHEY_PLAIN,3,(255,0,0),3)image =cv2.rectangle(image,(30,100),(70,300),(255,0,0),3)image =cv2.rectangle(image,(30,math.ceil(300-rect_height)),(70,300),(255,0,0),-1)# 显示刷新率FPScTime =time.time()fps_text =1/(cTime -fpsTime)fpsTime =cTime cv2.putText(image,"FPS: "+str(int(fps_text)),(10,70),cv2.FONT_HERSHEY_PLAIN,3,(255,0,0),3)# 显示画面cv2.imshow('MediaPipe Hands',image)ifcv2.waitKey(5)&0xFF==27orcv2.getWindowProperty('MediaPipe Hands',cv2.WND_PROP_VISIBLE)<1:breakcap.release()# 开始程序control =HandControlVolume()control.recognize()
五、
- mediapipe:mediapipe库,用于手部关键点检测和手势识别。最小追踪置信度和最大手的数量。
在使用hands对象之前,使用
with
语句创建一个上下文环境,设置手部检测和追踪的相关参数,包括最小检测置信度、实战演示
通过演示我们可以发现,食指与大拇指之间在屏幕中的的距离越远,那么我们的音量会越大,反之越小,实现了通过手势对音量的控制。导入所需要的模块# 导入OpenCVimportcv2# 导入mediapipeimportmediapipe asmp# 导入电脑音量控制模块fromctypes importcast,POINTERfromcomtypes importCLSCTX_ALLfrompycaw.pycaw importAudioUtilities,IAudioEndpointVolume# 导入其他依赖包importtimeimportmathimportnumpy asnp
三、
将图像从BGR格式转换为RGB格式,这是因为MediaPipe模型处理的输入要求为RGB格式。
# 判断是否有手掌ifresults.multi_hand_landmarks:# 遍历每个手掌forhand_landmarks inresults.multi_hand_landmarks:# 在画面标注手指self.mp_drawing.draw_landmarks(image,hand_landmarks,self.mp_hands.HAND_CONNECTIONS,self.mp_drawing_styles.get_default_hand_landmarks_style(),self.mp_drawing_styles.get_default_hand_connections_style())
4.解析手指,存入各个手指坐标
首先解析手指的坐标,并存入landmark_list列表中。源码分享
importcv2importmediapipe asmpfromctypes importcast,POINTERfromcomtypes importCLSCTX_ALLfrompycaw.pycaw importAudioUtilities,IAudioEndpointVolumeimporttimeimportmathimportnumpy asnpclassHandControlVolume:def__init__(self):# 初始化medialpipeself.mp_drawing =mp.solutions.drawing_utils self.mp_drawing_styles =mp.solutions.drawing_styles self.mp_hands =mp.solutions.hands # 获取电脑音量范围devices =AudioUtilities.GetSpeakers()interface =devices.Activate(IAudioEndpointVolume._iid_,CLSCTX_ALL,None)self.volume =cast(interface,POINTER(IAudioEndpointVolume))self.volume.SetMute(0,None)self.volume_range =self.volume.GetVolumeRange()# 主函数defrecognize(self):# 计算刷新率fpsTime =time.time()# OpenCV读取视频流cap =cv2.VideoCapture(0)# 视频分辨率resize_w =640resize_h =480# 画面显示初始化参数rect_height =0rect_percent_text =0withself.mp_hands.Hands(min_detection_confidence=0.7,min_tracking_confidence=0.5,max_num_hands=2)ashands:whilecap.isOpened():success,image =cap.read()image =cv2.resize(image,(resize_w,resize_h))ifnotsuccess:print("空帧.")continue# 提高性能image.flags.writeable =False# 转为RGBimage =cv2.cvtColor(image,cv2.COLOR_BGR2RGB)# 镜像image =cv2.flip(image,1)# mediapipe模型处理results =hands.process(image)image.flags.writeable =Trueimage =cv2.cvtColor(image,cv2.COLOR_RGB2BGR)# 判断是否有手掌ifresults.multi_hand_landmarks:# 遍历每个手掌forhand_landmarks inresults.multi_hand_landmarks:# 在画面标注手指self.mp_drawing.draw_landmarks(image,hand_landmarks,self.mp_hands.HAND_CONNECTIONS,self.mp_drawing_styles.get_default_hand_landmarks_style(),self.mp_drawing_styles.get_default_hand_connections_style())# 解析手指,存入各个手指坐标landmark_list =[]forlandmark_id,finger_axis inenumerate(hand_landmarks.landmark):landmark_list.append([landmark_id,finger_axis.x,finger_axis.y,finger_axis.z ])iflandmark_list:# 获取大拇指指尖坐标thumb_finger_tip =landmark_list[4]thumb_finger_tip_x =math.ceil(thumb_finger_tip[1]*resize_w)thumb_finger_tip_y =math.ceil(thumb_finger_tip[2]*resize_h)# 获取食指指尖坐标index_finger_tip =landmark_list[8]index_finger_tip_x =math.ceil(index_finger_tip[1]*resize_w)index_finger_tip_y =math.ceil(index_finger_tip[2]*resize_h)# 中间点finger_middle_point =(thumb_finger_tip_x +index_finger_tip_x)//2,(thumb_finger_tip_y +index_finger_tip_y)//2# print(thumb_finger_tip_x)thumb_finger_point =(thumb_finger_tip_x,thumb_finger_tip_y)index_finger_point =(index_finger_tip_x,index_finger_tip_y)# 画指尖2点image =cv2.circle(image,thumb_finger_point,10,(255,0,255),-1)image =cv2.circle(image,index_finger_point,10,(255,0,255),-1)image =cv2.circle(image,finger_middle_point,10,(255,0,255),-1)# 画2点连线image =cv2.line(image,thumb_finger_point,index_finger_point,(255,0,255),5)# 勾股定理计算长度line_len =math.hypot((index_finger_tip_x -thumb_finger_tip_x),(index_finger_tip_y -thumb_finger_tip_y))# 获取电脑最大最小音量min_volume =self.volume_range[0]max_volume =self.volume_range[1]# 将指尖长度映射到音量上vol =np.interp(line_len,[50,300],[min_volume,max_volume])# 将指尖长度映射到矩形显示上rect_height =np.interp(line_len,[50,300],[0,200])rect_percent_text =np.interp(line_len,[50,300],[0,100])# 设置电脑音量self.volume.SetMasterVolumeLevel(vol,None)# 显示矩形cv2.putText(image,str(math.ceil(rect_percent_text))+"%",(10,350),cv2.FONT_HERSHEY_PLAIN,3,(255,0,0),3)image =cv2.rectangle(image,(30,100),(70,300),(255,0,0),3)image =cv2.rectangle(image,(30,math.ceil(300-rect_height)),(70,300),(255,0,0),-1)# 显示刷新率FPScTime =time.time()fps_text =1/(cTime -fpsTime)fpsTime =cTime cv2.putText(image,"FPS: "+str(int(fps_text)),(10,70),cv2.FONT_HERSHEY_PLAIN,3,(255,0,0),3)# 显示画面cv2.imshow('xyp',image)ifcv2.waitKey(5)&0xFF==27orcv2.getWindowProperty('MediaPipe Hands',cv2.WND_PROP_VISIBLE)<1:breakcap.release()control =HandControlVolume()control.recognize()
- 在图像中标注手指关键点和手势连线。