使用OpenCV和卡尔曼滤波器进行实时活体检测

news2024/12/4 16:10:06

引言

在现代计算机视觉应用中,实时检测和跟踪物体是一项重要的任务。本文将详细介绍如何使用OpenCV库和卡尔曼滤波器来实现一个实时的活体检测系统。该系统能够通过摄像头捕捉视频流,并使用YOLOv3模型来检测目标对象(例如人),同时利用卡尔曼滤波器来预测目标的运动轨迹。本文将逐步介绍代码的实现过程,并解释每个部分的功能。

1. 环境准备

在开始编写代码之前,确保已经安装了以下依赖库:

  • OpenCV
  • NumPy
  • FilterPy

可以使用pip命令来安装这些库:

pip install opencv-python numpy filterpy

2. 代码结构

2.1 导入必要的库

import cv2
import numpy as np
from filterpy.kalman import KalmanFilter
from filterpy.common import Q_discrete_white_noise

2.2 初始化卡尔曼滤波器

卡尔曼滤波器是一种用于估计线性动态系统的状态的算法。在这里,我们使用它来预测目标的位置和速度。

def init_kalman_filter():
    kf = KalmanFilter(dim_x=4, dim_z=2)
    kf.x = np.zeros((4, 1))  # 初始状态 [x, y, vx, vy]
    kf.F = np.array([[1, 0, 1, 0],
                     [0, 1, 0, 1],
                     [0, 0, 1, 0],
                     [0, 0, 0, 1]], dtype=float)  # 状态转移矩阵
    kf.H = np.array([[1, 0, 0, 0],
                     [0, 1, 0, 0]])  # 观测矩阵
    kf.P *= 1000  # 初始协方差
    kf.R = np.eye(2) * 5  # 测量噪声
    kf.Q = Q_discrete_white_noise(dim=4, dt=1, var=0.1)  # 过程噪声
    return kf

2.3 更新卡尔曼滤波器

每次获取新的观测值时,我们需要更新卡尔曼滤波器的状态。

def update_kalman_filter(kf, measurement):
    kf.predict()
    kf.update(measurement)
    return kf.x[0, 0], kf.x[1, 0], kf.x[2, 0], kf.x[3, 0]

2.4 实时检测函数

detect_live 函数是整个系统的核心,它负责从摄像头读取视频流,检测目标,并使用卡尔曼滤波器进行预测。

def detect_live(
        camera_index=0,
        motion_threshold=10,  # 移动的阈值
        min_confidence=0.5,  # 最小置信度
        debug=False,  # 是否显示调试窗口
        consecutive_motion_frames=5,  # 连续检测到移动的帧数
        target_class="person"  # 目标类别
):
    # 加载 YOLOv3 配置和权重文件
    net = cv2.dnn.readNet("yolov3.weights", "yolov3.cfg")

    # 加载类别名称
    with open("coco.names", "r") as f:
        classes = [line.strip() for line in f.readlines()]

    # 获取输出层名称
    layer_names = net.getLayerNames()
    try:
        output_layers = [layer_names[i[0] - 1] for i in net.getUnconnectedOutLayers()]
    except IndexError:
        output_layers = [layer_names[i - 1] for i in net.getUnconnectedOutLayers()]

    # 打开摄像头
    cap = cv2.VideoCapture(camera_index)

    if not cap.isOpened():
        print("无法打开摄像头。")
        return

    prev_frame = None
    prev_centers = []  # 用于存储前几帧的目标中心点
    consecutive_motion_count = 0  # 连续检测到移动的帧数计数器
    is_target_detected = False  # 标志变量,用于记录当前帧中是否检测到目标类别
    kf = init_kalman_filter()  # 初始化卡尔曼滤波器

    try:
        while True:
            # 读取摄像头帧
            ret, frame = cap.read()

            if not ret:
                print("无法读取摄像头帧。")
                break

            height, width, _ = frame.shape

            # 对图像进行预处理
            blob = cv2.dnn.blobFromImage(frame, 0.00392, (416, 416), (0, 0, 0), True, crop=False)
            net.setInput(blob)
            outs = net.forward(output_layers)

            class_ids = []
            confidences = []
            boxes = []

            for out in outs:
                for detection in out:
                    scores = detection[5:]
                    class_id = np.argmax(scores)
                    confidence = scores[class_id]
                    if confidence > min_confidence:
                        center_x = int(detection[0] * width)
                        center_y = int(detection[1] * height)
                        w = int(detection[2] * width)
                        h = int(detection[3] * height)
                        x = int(center_x - w / 2)
                        y = int(center_y - h / 2)
                        boxes.append([x, y, w, h])
                        confidences.append(float(confidence))
                        class_ids.append(class_id)
                        if classes[class_id] == target_class:
                            is_target_detected = True  # 检测到目标类别

            indexes = cv2.dnn.NMSBoxes(boxes, confidences, min_confidence, 0.4)

            # 将 indexes 转换为 NumPy 数组
            indexes = np.array(indexes)

            current_centers = []
            for i in indexes.flatten():
                x, y, w, h = boxes[i]
                label = str(classes[class_ids[i]])
                confidence = confidences[i]

                # 计算中心点
                center = ((x + x + w) // 2, (y + y + h) // 2)
                current_centers.append(center)

                if len(prev_centers) > 0 and label == target_class:
                    # 如果有前一帧的数据,计算速度和方向
                    prev_center = prev_centers[-1]
                    distance = np.sqrt((center[0] - prev_center[0]) ** 2 + (center[1] - prev_center[1]) ** 2)
                    speed = distance  # 单位是像素/帧
                    direction = (center[0] - prev_center[0], center[1] - prev_center[1])

                    # 更新卡尔曼滤波器
                    x, y, vx, vy = update_kalman_filter(kf, np.array([center[0], center[1]]))

                    # 简单的行为预测
                    if speed > motion_threshold:
                        consecutive_motion_count += 1
                    else:
                        consecutive_motion_count = 0

                    # 如果连续检测到足够的移动,认为是活体
                    if consecutive_motion_count >= consecutive_motion_frames:
                        yield {"is_live": True, "speed": speed, "direction": direction, "predicted_position": (x, y),
                               "predicted_velocity": (vx, vy)}
                        consecutive_motion_count = 0  # 重置计数器
                else:
                    consecutive_motion_count = 0

                # 在调试模式下绘制框
                if debug:
                    color = (0, 255, 0) if label == target_class else (0, 0, 255)  # 绿色表示目标类别,红色表示其他类别
                    # 确保坐标是整数类型
                    x, y, w, h = int(x), int(y), int(w), int(h)
                    cv2.rectangle(frame, (x, y), (x + w, y + h), color, 2)
                    cv2.putText(frame, f"{label}: {confidence:.2f}", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color,
                                2)
                    if label == target_class:
                        cv2.circle(frame, center, 5, (0, 0, 255), -1)
                        cv2.circle(frame, (int(x), int(y)), 5, (0, 255, 0), -1)  # 预测位置

            # 更新前一帧的中心点列表
            prev_centers = current_centers

            # 如果没有检测到目标类别,输出不是活体
            if not is_target_detected:
                yield {"is_live": False}

            # 重置标志变量
            is_target_detected = False

            # 显示当前帧(仅在调试模式下)
            if debug:
                cv2.imshow('Live Detection', frame)

            # 按 'q' 键退出循环
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break

    finally:
        # 释放摄像头并关闭所有窗口
        cap.release()
        cv2.destroyAllWindows()

2.5 主程序

主程序调用 detect_live 函数,并打印出检测结果。

if __name__ == "__main__":
    for result in detect_live(debug=True):
        if result["is_live"]:
            print(
                f"Is live: True, Speed: {result['speed']:.2f} pixels/frame, Direction: {result['direction']}, Predicted Position: {result['predicted_position']}, Predicted Velocity: {result['predicted_velocity']}")
        else:
            print("Is live: False")

3. 代码详解

3.1 初始化卡尔曼滤波器

卡尔曼滤波器初始化时定义了状态向量、状态转移矩阵、观测矩阵、初始协方差矩阵、测量噪声和过程噪声。这些参数决定了卡尔曼滤波器的性能。

3.2 更新卡尔曼滤波器

每次获取新的观测值时,卡尔曼滤波器会先进行预测,然后根据新的观测值更新状态。这样可以得到更准确的目标位置和速度估计。

3.3 实时检测

detect_live 函数首先加载YOLOv3模型,然后打开摄像头并开始读取视频流。对于每一帧,它都会进行以下操作:

  1. 对图像进行预处理。
  2. 使用YOLOv3模型进行目标检测。
  3. 使用非极大值抑制(NMS)去除重复的检测框。
  4. 计算目标的中心点。
  5. 如果检测到目标,计算目标的速度和方向。
  6. 更新卡尔曼滤波器以预测目标的未来位置。
  7. 在调试模式下绘制检测框和预测位置。
  8. 如果连续多帧检测到目标移动,认为是活体。
  9. 显示当前帧(仅在调试模式下)。

4. 结论

本文详细介绍了如何使用OpenCV和卡尔曼滤波器实现一个实时的活体检测系统。通过结合YOLOv3模型的强大检测能力和卡尔曼滤波器的预测能力,我们可以构建一个高效且准确的实时检测系统。这个系统可以应用于各种场景,如安全监控、自动驾驶等。

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

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

相关文章

亚马逊云(AWS)使用root用户登录

最近在AWS新开了服务器(EC2),用于学习,遇到一个问题就是默认是用ec2-user用户登录,也需要密钥对。 既然是学习用的服务器,还是想直接用root登录,下面开始修改: 操作系统是&#xff1…

Android笔记【12】脚手架Scaffold和导航Navigation

一、前言 学习课程时,对于自己不懂的点的记录。 对于cy老师第二节课总结。 二、内容 1、PPT介绍scaffold 2、开始代码实操 先新建一个screen包,写一个Homescreen函数,包括四个页面。 再新建一个compenent包,写一个displayText…

HookVip4.0.3 | 可解锁各大应用会员

HookVip是一款可以解锁会员的模块工具,需要搭配相应框架结合使用。这款插件工具支持多种框架如LSPosed、LSPatch、太极、应用转生等,并且完全免费,占用内存小。支持的软件包括now要想、神奇脑波、塔罗牌占卜、爱剪辑、人人视频、咪萌桌面宠物…

猎板 PCB特殊工艺:铸就电子行业核心竞争力新高度

在当今竞争激烈且技术驱动的电子制造领域,印制电路板(PCB)作为电子产品的关键基石,其特殊工艺的发展水平直接影响着整个行业的创新步伐与产品品质。猎板 PCB 凭借在厚铜板、孔口铺铜、HDI 板、大尺寸板以及高频高速板等特殊工艺方…

【教学类-43-25】20241203 数独3宫格的所有可能-使用模版替换(12套样式,空1格-空8格,每套510张,共6120小图)

前期做数独惨宫格的所有排列,共有12套样式,空1格-空8格,每套510张,共6120小图) 【教学类-43-24】20241127 数独3宫格的所有可能(12套样式,空1格-空8格,每套510张,共6120…

Redis+Caffeine 多级缓存数据一致性解决方案

RedisCaffeine 多级缓存数据一致性解决方案 背景 之前写过一篇文章RedisCaffeine 实现两级缓存实战,文章提到了两级缓存RedisCaffeine可以解决缓存雪等问题也可以提高接口的性能,但是可能会出现缓存一致性问题。如果数据频繁的变更,可能会导…

echarts地图立体效果,echarts地图点击事件,echarts地图自定义自定义tooltip

一.地图立体效果 方法1:两层地图叠加 实现原理:geo数组中放入两个地图对象,通过修改zlevel属性以及top,left,right,bottom形成视觉差 配置项参考如下代码: geo: [{zlevel: 2,top: 96,map: map,itemStyle: {color: #091A51ee,opacity: 1,borderWidth: 2,borderColor: #16BAFA…

D87【python 接口自动化学习】- pytest基础用法

day87 pytest运行参数 -m -k 学习日期:20241203 学习目标:pytest基础用法 -- pytest运行参数-m -k 学习笔记: 常用运行参数 pytest运行参数-m -k pytest -m 执行特定的测试用例,markers最好使用英文 [pytest] testpaths./te…

总结拓展十七:特殊采购业务——委外业务

SAP中委外采购业务,又称供应商分包(或外协、转包、、外包、托外等),是企业将部分生产任务委托给外部供应商/集团其他分子公司完成的一种特殊采购业务模式。 委外业务主要有2大类型,分别是标准委外(委外采购…

ESP8266作为TCP客户端或者服务器使用

ESP8266模块,STA模式(与手机搭建TCP通讯,EPS8266为服务端)_esp8266作为station-CSDN博客 ESP8266模块,STA模式(与电脑搭建TCP通讯,ESP8266 为客户端)_esp8266 sta 连接tcp-CSDN博客…

ATTCK红队评估实战靶场(四)

靶机链接:http://vulnstack.qiyuanxuetang.net/vuln/detail/6/ 环境搭建 新建两张仅主机网卡,一张192.168.183.0网段(内网网卡),一张192.168.157.0网段(模拟外网网段),然后按照拓补…

C 语言 “神秘魔杖”—— 指针初相识,解锁编程魔法大门(一)

文章目录 一、概念1、取地址操作符(&)2、解引用操作符(*)3、指针变量1、 声明和初始化2、 用途 二、内存和地址三、指针变量类型的意义1、 指针变量类型的基本含义2、 举例说明不同类型指针变量的意义 四、const修饰指针1、co…

封装loding加载动画的请求

图片 /*** Loading 状态管理类*/ export class Loading {constructor(timer300) {this.value falsethis.timer timer}/*** 执行异步操作并自动管理 loading 状态* param {Promise|Function|any} target - Promise、函数或其他值* returns {Promise} - 返回请求结果*/async r…

人形机器人训练、机器臂远程操控、VR游戏交互、影视动画制作,一副手套全部解决!

广州虚拟动力基于自研技术推出了多节点mHand Pro动捕数据手套,其最大的特点就是功能集成与高精度捕捉,可以用于人形机器人训练、机器臂远程操控、VR游戏交互、影视动画制作等多种场景。 一、人形机器人训练 mHand Pro动捕数据手套双手共装配16个9轴惯性…

Nginx Web服务器管理、均衡负载、访问控制与跨域问题

Nginx Web 服务器的均衡负载、访问控制与跨域问题 Nginx 的配置 1. 安装Nginx 首先安装Nginx apt install nginx -ycaccpurgatory-v:~$ sudo apt install nginx [sudo] password for cacc: Reading package lists... Done Building dependency tree... Done Reading state i…

Bert+CRF的NER实战

CRF(条件随机场-Conditional Random Field) 原始本文:我在北京吃炸酱面 标注示例(采用BIO标注方式): 我O在O北B-PLA京I-PLA吃O炸B-FOOD酱I-FOOD面I-FOOD CRF: 目的:提出一些不可能…

C++语法·识

人生建议:请手机反省一下,为什么总拉着我熬夜。 目录 STL简介 string类容器一 auto(自动声明类型) 简介: 特点 范围for(语法糖) 简介 特点 string string类的常见接口 1.构造 2.容…

蓝桥杯准备训练(lesson1,c++方向)

前言 报名参加了蓝桥杯(c)方向的宝子们,今天我将与大家一起努力参赛,后序会与大家分享我的学习情况,我将从最基础的内容开始学习,带大家打好基础,在每节课后都会有练习题,刚开始的练…

【开源】A059-基于SpringBoot的社区养老服务系统的设计与实现

🙊作者简介:在校研究生,拥有计算机专业的研究生开发团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看项目链接获取⬇️,记得注明来意哦~🌹 赠送计算机毕业设计600个选题ex…

winform跨线程更新界面

前言: 大家好,我是上位机马工,硕士毕业4年年入40万,目前在一家自动化公司担任软件经理,从事C#上位机软件开发8年以上!我们在开发C#程序的时候,有时候需要在非Ui主线程更新界面,为了…