OpenCV--项目:虚拟计数器

发布于:2024-12-18 ⋅ 阅读:(69) ⋅ 点赞:(0)

OpenCV--项目:虚拟计数器

代码和笔记

import cv2
import numpy as np
import time
from cvzone.HandTrackingModule import HandDetector

"""
项目:虚拟计数器
cvzone:cv任务开源库
mediapipe:姿态估计开源库(在cvzone里面)
"""

# 打开摄像头,显示每一帧图片 0表示只有一个摄像头
# 如果是读取视频文件,则0改为视频的地址就行
cap = cv2.VideoCapture(0)

# 设置窗口大小 ID+value
# 属性ID 3 和 4 来设置视频的宽度和高度
cap.set(3, 1280)
cap.set(4, 720)


# 定义计数器的每一个按键
# pos坐标,width, height宽度和高度,value按键值
class Button:
    def __init__(self, pos, width, height, value):
        self.pos = pos
        self.width = width
        self.height = height
        self.value = value

    # 在视频上画出计数器的每一个按键
    def draw(self, _img):
        # 绘制一个计数器的一个小格子
        # 先绘制一个实心的灰色矩形
        # 第一个是起始点坐标,第二个是起始点对角的那个坐标
        # 然后是颜色和线宽, -1表示填充
        cv2.rectangle(_img, (self.pos[0], self.pos[1]), (self.pos[0] + self.width, self.pos[1] + self.height), (225, 225, 225), -1)
        # 再画一个黑色的矩形边框
        cv2.rectangle(_img, (self.pos[0], self.pos[1]), (self.pos[0] + self.width, self.pos[1] + self.height), (0, 0, 0), 3)
        # 给矩形内赋值按键的值
        # 第一个是值,第二个是文本左下角坐标,第三个是字体类型
        # 第四个是字体大小,第五个是颜色,最后一个是线宽
        cv2.putText(_img, self.value, (self.pos[0] + 30, self.pos[1] + 70), cv2.FONT_HERSHEY_PLAIN, 2, (50, 50, 50), 2)

    # 判断手的坐标是否在按键的框里,判断是否点击
    def check_click(self, x, y):
        if self.pos[0] < x < self.pos[0] + self.width and self.pos[1] < y < self.pos[1] + self.height:
            # 点的时候触发一个变大的效果
            cv2.rectangle(img, (self.pos[0] + 3, self.pos[1] + 3), (self.pos[0] + self.width - 3, self.pos[1] + self.height - 3),
                          (255, 255, 255), -1)
            cv2.putText(img, self.value, (self.pos[0] + 25, self.pos[1] + 80), cv2.FONT_HERSHEY_PLAIN, 5, (0, 0, 0), 5)
            return True
        else:
            return False

# 因为计数器有多个按键,用循环画出按键
button_values = [['7', '8', '9', '*'],
                 ['4', '5', '6', '-'],
                 ['1', '2', '3', '+'],
                 ['0', '/', '.', '=']]

button_list = []
# 开始画,先画第一列,然后是第二列.....
# 注意OpenCV中纵轴的正方向是往下,先遍历列后遍历行
for x in range(4):
    for y in range(4):
        x_pos = x * 100 + 800
        y_pos = y * 100 + 150
        button = Button((x_pos, y_pos), 100, 100, button_values[y][x])
        button_list.append(button)

# 创建手势识别器
# maxHands:最大能够识别的手数。detectionCon:检测到是手的概率
detector = HandDetector(maxHands=1, detectionCon=0.8)

# 计数器公式显示
equation = ''
# 缓冲器,避免重复点击
delay_count = 0

while True:
    # 读取每一帧图片,返回标记和图片
    flag, img = cap.read()
    # 摄像头和真实画面反了,要调整,图片翻转
    # 大于0,左右翻转;等于0,上下翻转;小于0,左右上下都翻转
    img = cv2.flip(img, 1)

    # 检测手,返回手和检测的图片,返回的手是一个列表(字典类型),在lmList里面,里面是手的坐标
    # 因为我们在摄像头就反转了,这里就不用了
    # 注意要先检测手,后按键
    hands, img = detector.findHands(img, flipType=False)

    # 如果flag为True,显示图片
    if flag:
        # 调用button
        for button in button_list:
            button.draw(img)

        # 创建显示结果的窗口
        cv2.rectangle(img, (800, 70), (800 + 400, 70 + 80), (225, 225, 225), -1)
        cv2.rectangle(img, (800, 70), (800 + 400, 70 + 80), (50, 50, 50), 3)

        if hands:
            # 取出食指和中指的点,并计算两者的距离
            lmList = hands[0]['lmList']
            # lmList[8], lmList[12]代表的是食指和中指的点
            # 返回距离,线的描述,图片
            # # 最新版本的cvzone中,lmList坐标是三个值x,y,z,取出前两个值。
            lmList = [x[:2] for x in lmList]
            length, _, img = detector.findDistance(lmList[8], lmList[12], img)
            # 取出手指的坐标
            x, y = lmList[8]
        # 如果食指和中指之间的距离小于50,我们认为进行了点击操作
        # 为了防止频繁输入,设置延缓delay_count
            if length < 50 and delay_count == 0:
                for i, button in enumerate(button_list):
                    if button.check_click(x, y):
                        # 如果正确点击,把点中的数字显示在窗口上
                        # 我们用枚举得到的i来算values的索引
                        values = button_values[int(i % 4)][int(i / 4)]
                        # 如果是'='说明要计算了
                        # eval可以直接把字符串里面的公式或者变量,通过整合或者计算变成数字
                        # eval计算结果 如:'1 + 2',eval后得到3
                        if values == '=':
                            try:
                                equation = str(eval(equation))
                            except Exception:
                                # 非法数学公式,重新输出
                                equation = ''
                        else:
                            # 字符串拼接
                            equation += values
                            # 不要频繁的输入,睡眠一下
                            # time.sleep(0.5) 但睡眠不能完全解决问题还会导致卡顿
                        delay_count = 1

        # 重置delay_count,避免重复点击
        if delay_count != 0:
            delay_count += 1
            if delay_count > 10:
                delay_count = 0

        # 计数器的计算公式
        cv2.putText(img, equation, (810, 130), cv2.FONT_HERSHEY_PLAIN, 3, (0, 0, 0), 3)
        cv2.imshow('img', img)
        # 1ms,1帧
        key = cv2.waitKey(1)
        if key == ord('q'):
            break
        elif key == ord('c'):
            # 清空输出框
            equation = ''

    else:
        print('摄像头打开失败')
        break

cap.release()
cv2.destroyAllWindows()


网站公告

今日签到

点亮在社区的每一天
去签到