【深度学习】yolov5+deepsort 完成计数和行人行人重识别的追踪

2023-11-04


前言

行人重识别是计算机视觉的基本任务之一,首先要有一个detector(检测器来检测到目标),然后将检测到的目标送入到tracker(追踪器)中,完成对相同目标的判别和追踪。
基于此我们可以将这个技术用于:
1.单摄像头车流量、人流量的计算
2.但摄像头的追踪(徘徊检测)
3.跨摄像头的追踪。
很明显,任务3 要难于任务1, 任务2 是任务1的延续,需要引入一点其它技术。


1、知识体系

1.1 前置说明

DeepSort的前身是sort算法,Sort算法的核心是卡尔曼滤波算法和匈牙利算法。
卡尔曼滤波算法作用:是当前的一系列运动变量去预测下一时刻的运动变量,但是第一次的检测结果用来初始化卡尔曼滤波的运动变量。
匈牙利算法:解决分配问题,就是把一群检测框和卡尔曼预测的框做分配,让卡尔曼预测的框找到和自己最匹配的检测框,达到追踪的效果。本质是维护一个状态矩阵,解决预测框的匹配问题。

1.2 Sort的工作流程

在这里插入图片描述

Detections是通过目标检测到的框。Tracks是轨迹信息。
整个算法的工作流程如下:
(1)将第一帧检测到的结果创建其对应的Tracks。将卡尔曼滤波的运动变量初始化,通过卡尔曼滤波预测其对应的框框。

(2)将该帧目标检测的框框和上一帧通过Tracks预测的框框一一进行IOU匹配,再通过IOU匹配的结果计算其代价矩阵(cost matrix,其计算方式是1-IOU)。

(3)将(2)中得到的所有的代价矩阵作为匈牙利算法的输入,得到线性的匹配的结果,这时候我们得到的结果有三种,第一种是Tracks失配(Unmatched Tracks),我们直接将失配的Tracks删除;第二种是Detections失配(Unmatched Detections),我们将这样的Detections初始化为一个新的Tracks(new Tracks);第三种是检测框和预测的框框配对成功,这说明我们前一帧和后一帧追踪成功,将其对应的Detections通过卡尔曼滤波更新其对应的Tracks变量。

(4)反复循环(2)-(3)步骤,直到视频帧结束。
印象里这里的预测是线性预测,因为预测器很弱容易产生id switch问题,而且只考虑了运动的关联性特征,没考虑外观特征。这些问题会在deepsort中优化。

1.3 deepsort

。而Deepsort算法在sort算法的基础上增加了级联匹配(Matching Cascade)和新轨迹的确认(confirmed)。Tracks分为确认态(confirmed),和不确认态(unconfirmed),新产生的Tracks是不确认态的;不确认态的Tracks必须要和Detections连续匹配一定的次数(默认是3)才可以转化成确认态。确认态的Tracks必须和Detections连续失配一定次数(默认30次),才会被删除。
在这里插入图片描述
整个算法的工作流程如下:

(1)将第一帧次检测到的结果创建其对应的Tracks。将卡尔曼滤波的运动变量初始化,通过卡尔曼滤波预测其对应的框框。这时候的Tracks一定是unconfirmed的。

(2)将该帧目标检测的框框和第上一帧通过Tracks预测的框框一一进行IOU匹配,再通过IOU匹配的结果计算其代价矩阵(cost matrix,其计算方式是1-IOU)。

(3)将(2)中得到的所有的代价矩阵作为匈牙利算法的输入,得到线性的匹配的结果,这时候我们得到的结果有三种,第一种是Tracks失配(Unmatched Tracks),我们直接将失配的Tracks(因为这个Tracks是不确定态了,如果是确定态的话则要连续达到一定的次数(默认30次)才可以删除)删除;第二种是Detections失配(Unmatched Detections),我们将这样的Detections初始化为一个新的Tracks(new Tracks);第三种是检测框和预测的框框配对成功,这说明我们前一帧和后一帧追踪成功,将其对应的Detections通过卡尔曼滤波更新其对应的Tracks变量。

(4)反复循环(2)-(3)步骤,直到出现确认态(confirmed)的Tracks或者视频帧结束。

(5)通过卡尔曼滤波预测其确认态的Tracks和不确认态的Tracks对应的框框。将确认态的Tracks的框框和是Detections进行级联匹配(之前每次只要Tracks匹配上都会保存Detections其的外观特征和运动信息,默认保存前100帧,利用外观特征和运动信息和Detections进行级联匹配,这么做是因为确认态(confirmed)的Tracks和Detections匹配的可能性更大)。

(6)进行级联匹配后有三种可能的结果。第一种,Tracks匹配,这样的Tracks通过卡尔曼滤波更新其对应的Tracks变量。第二第三种是Detections和Tracks失配,这时将之前的不确认状态的Tracks和失配的Tracks一起和Unmatched Detections一一进行IOU匹配,再通过IOU匹配的结果计算其代价矩阵(cost matrix,其计算方式是1-IOU)。

(7)将(6)中得到的所有的代价矩阵作为匈牙利算法的输入,得到线性的匹配的结果,这时候我们得到的结果有三种,第一种是Tracks失配(Unmatched Tracks),我们直接将失配的Tracks(因为这个Tracks是不确定态了,如果是确定态的话则要连续达到一定的次数(默认30次)才可以删除)删除;第二种是Detections失配(Unmatched Detections),我们将这样的Detections初始化为一个新的Tracks(new Tracks);第三种是检测框和预测的框框配对成功,这说明我们前一帧和后一帧追踪成功,将其对应的Detections通过卡尔曼滤波更新其对应的Tracks变量。

(8)反复循环(5)-(7)步骤,直到视频帧结束。

2. 实践应用

代码在我的git仓库:https://github.com/justinge/yolov5-deepsort
里面的readyme要看一下,都可以跑的通。 单摄像头的计数很容易的。
还可以配合我之前的文章:https://blog.csdn.net/weixin_40293999/article/details/127811380

3. 干货补充

下面的代码来解析一下,如何实现计数的原理的, 序号 1,2 只是说明了重识别的过程,但没说明计数的原理。

在这里插入图片描述
蓝色区域全为 1
黄色区域全为 2
黑色区域全为 0
我以count_person.py 为例子说明这个问题:

import numpy as np

import objtracker
from objdetector import Detector
import cv2


VIDEO_PATH = './video/test_person.mp4'

if __name__ == '__main__':

    # 根据视频尺寸,填充供撞线计算使用的polygon
    width = 1920
    height = 1080
    mask_image_temp = np.zeros((height, width), dtype=np.uint8)

    # 填充第一个撞线polygon(蓝色)
    list_pts_blue = [[204, 305], [227, 431], [605, 522], [1101, 464], [1900, 601], [1902, 495], [1125, 379], [604, 437],
                     [299, 375], [267, 289]]
    ndarray_pts_blue = np.array(list_pts_blue, np.int32)
    polygon_blue_value_1 = cv2.fillPoly(mask_image_temp, [ndarray_pts_blue], color=1)
    # 增加一个新维度 [height, width, 1]
    polygon_blue_value_1 = polygon_blue_value_1[:, :, np.newaxis]

    # 填充第二个撞线polygon(黄色)
    mask_image_temp = np.zeros((height, width), dtype=np.uint8)
    list_pts_yellow = [[181, 305], [207, 442], [603, 544], [1107, 485], [1898, 625], [1893, 701], [1101, 568],
                       [594, 637], [118, 483], [109, 303]]
    ndarray_pts_yellow = np.array(list_pts_yellow, np.int32)
    polygon_yellow_value_2 = cv2.fillPoly(mask_image_temp, [ndarray_pts_yellow], color=2)
    polygon_yellow_value_2 = polygon_yellow_value_2[:, :, np.newaxis]

    # 撞线检测用的mask,包含2个polygon,(值范围 0、1、2),供撞线计算使用
    polygon_mask_blue_and_yellow = polygon_blue_value_1 + polygon_yellow_value_2




    # 蓝 色盘 b,g,r
    blue_color_plate = [255, 0, 0]
    # 蓝 polygon图片
    blue_image = np.array(polygon_blue_value_1 * blue_color_plate, np.uint8)

    # 黄 色盘
    yellow_color_plate = [0, 255, 255]
    # 黄 polygon图片
    yellow_image = np.array(polygon_yellow_value_2 * yellow_color_plate, np.uint8)

    # 彩色图片(值范围 0-255)
    color_polygons_image = blue_image + yellow_image
    
    # 缩小尺寸,1920x1080->960x540
    color_polygons_image = cv2.resize(color_polygons_image, (width//2, height//2))

    # list 与蓝色polygon重叠
    list_overlapping_blue_polygon = []

    # list 与黄色polygon重叠
    list_overlapping_yellow_polygon = []

    # 下行数量
    down_count = 0
    # 上行数量
    up_count = 0

    font_draw_number = cv2.FONT_HERSHEY_SIMPLEX
    draw_text_postion = (int((width/2) * 0.01), int((height/2) * 0.05))

    # 实例化yolov5检测器
    detector = Detector()

    # 打开视频
    capture = cv2.VideoCapture(VIDEO_PATH)
    count_num = 0
    while True:
        # 读取每帧图片
        _, im = capture.read()
        if im is None:
            break

        # 缩小尺寸,1920x1080->960x540
        im = cv2.resize(im, (width//2, height//2))

        list_bboxs = []
        # 更新跟踪器
        output_image_frame, list_bboxs = objtracker.update(detector, im)
        # 输出图片
        output_image_frame = cv2.add(output_image_frame, color_polygons_image)

        if len(list_bboxs) > 0:
            # ----------------------判断撞线----------------------
            for item_bbox in list_bboxs:
                x1, y1, x2, y2, _, track_id = item_bbox
                # 撞线检测点,(x1,y1),y方向偏移比例 0.0~1.0
                y1_offset = int(y1 + ((y2 - y1) * 0.6))
                # 撞线的点
                y = y1_offset
                x = x1
                if polygon_mask_blue_and_yellow[y, x] == 1:
                    # 如果撞 蓝polygon, 
                    if track_id not in list_overlapping_blue_polygon:
                        list_overlapping_blue_polygon.append(track_id)
                    # 判断 黄polygon list里是否有此 track_id
                    # 有此track_id,则认为是 UP (上行)方向
                    if track_id in list_overlapping_yellow_polygon:
                        # 上行+1
                        up_count += 1
                        print('up count:', up_count, ', up id:', list_overlapping_yellow_polygon)
                        # 删除 黄polygon list 中的此id
                        list_overlapping_yellow_polygon.remove(track_id)

                elif polygon_mask_blue_and_yellow[y, x] == 2:
                    # 如果撞 黄polygon
                    if track_id not in list_overlapping_yellow_polygon:
                        list_overlapping_yellow_polygon.append(track_id)
                    # 判断 蓝polygon list 里是否有此 track_id
                    # 有此 track_id,则 认为是 DOWN(下行)方向
                    if track_id in list_overlapping_blue_polygon:
                        # 下行+1
                        down_count += 1
                        print('down count:', down_count, ', down id:', list_overlapping_blue_polygon)
                        # 删除 蓝polygon list 中的此id
                        list_overlapping_blue_polygon.remove(track_id)
            # ----------------------清除无用id----------------------
            list_overlapping_all = list_overlapping_yellow_polygon + list_overlapping_blue_polygon
            for id1 in list_overlapping_all:
                is_found = False
                for _, _, _, _, _, bbox_id in list_bboxs:
                    if bbox_id == id1:
                        is_found = True
                if not is_found:
                    # 如果没找到,删除id
                    if id1 in list_overlapping_yellow_polygon:
                        list_overlapping_yellow_polygon.remove(id1)

                    if id1 in list_overlapping_blue_polygon:
                        list_overlapping_blue_polygon.remove(id1)
            list_overlapping_all.clear()
            # 清空list
            list_bboxs.clear()
        else:
            # 如果图像中没有任何的bbox,则清空list
            list_overlapping_blue_polygon.clear()
            list_overlapping_yellow_polygon.clear()
            
        # 输出计数信息
        text_draw = 'DOWN: ' + str(down_count) + \
                    ' , UP: ' + str(up_count)
        print(text_draw)
        output_image_frame = cv2.putText(img=output_image_frame, text=text_draw,
                                         org=draw_text_postion,
                                         fontFace=font_draw_number,
                                         fontScale=0.75, color=(0, 0, 255), thickness=2)
        
        if count_num % 100 == 0:
            cv2.imwrite('output_image{}.jpg'.format(count_num), output_image_frame)
        count_num += 1
        #cv2.imshow('Counting Demo', output_image_frame)
        #cv2.waitKey(1)
        

    #capture.release()
    #cv2.destroyAllWindows()

while True 之前就是定义了那么三个颜色的mask,
关键是 While True里面

# 更新跟踪器
output_image_frame, list_bboxs = objtracker.update(detector, im)

list_bboxs 这个输出结果要关注一下,是一个列表:

[[x1,y1,x2,y2,‘’,trace_id],..........]

x1,y1, x2,y2 是矩形框的左上和右下角点, trace_id 就是追踪行人的id

在一个大的循环 While True中 对每个人的位置进行遍历:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
希望经过上面的分析后,在看下面的图,就容易理解很多了吧
在这里插入图片描述

总结

关于跨摄像头的追踪,我还没有想出好办法来,先留个坑。

跨摄像头的追踪 cross domain还是啥,有个专有的名词,是不太行的,是行业不行。搞不定。
通常解决方案是,跨摄像头追踪,追不住。
转成360全景摄像头追的话,太慢。。。一秒拼接不出一张。

又有了新的ByteTrack,检测器需要一个yolov或者ssd之类,然后再进行ByTrack,详见我的另一篇文章。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【深度学习】yolov5+deepsort 完成计数和行人行人重识别的追踪 的相关文章

  • windows连网疑问

    如果我的笔记本连接一台无线AP 当我不更改SSID 而只去更改密码的时候 为什么会连不上 当我把电脑里关于这个AP的连接信息删掉之后 就能够连接上了 所以 是跟之前保存的信息有关系的 但其中的作用原理不是很清楚 不过 我想 这必定是wind
  • ubuntu16.04下teamviewer启动不显示界面

    导读 在Ubuntu下使用teamviewer的时候 通过命令行输入 teamviewer 不会出现界面 就像这样 没有显示teamviewer的界面 adminuser adminuser pc teamviewer Init Check

随机推荐

  • linux 系统调用

    5 1 5 如何使用系统调用 如图5 2所示 用户应用可以通过两种方式使用系统调用 第一种方式是通过C库函数 包括系统调用在C库中的封装函数和其他普通函数 图5 2 使用系统调用的两种方式 第二种方式是使用 syscall宏 2 6 18版
  • VUE路由的hash模式与history模式的区别

    hash模式url带 号 history模式不带 号 通过history api 我们丢掉了丑陋的 但是它也有个问题 不怕前进 不怕后退 就怕刷新 f5 如果后端没有准备的话 因为刷新是实实在在地去请求服务器的 不玩虚的 在hash模式下
  • 使用fio测试磁盘I/O性能

    测试准备 工具 fio Flexible IO Tester 官方网站 http freecode com projects fio http brick kernel dk snaps 注意 性能测试建议直接通过写裸盘的方式进行测试 会得
  • JavaScript之js对象终极序列化(可序列化函数)

    案例官方地址 http www rainx org 2017 01 04 javascript js E5 AF B9 E8 B1 A1 E7 BB 88 E6 9E 81 E5 BA 8F E5 88 97 E5 8C 96 你是否遇到了
  • PTA 习题3.6 一元多项式的乘法与加法运算

    文章目录 题目 1 加法多项式 2 乘法多项式 代码 题目 设计函数分别求两个一元多项式的乘积与和 输入格式 输入分2行 每行分别先给出多项式非零项的个数 再以指数递降方式输入一个多项式非零项系数和指数 绝对值均为不超过1000的整数 数字
  • win11安装linux子系统配置C++开发环境的系列教程

    本期主要记录在Win11系统下安装ubuntu20 04LTS并完成相关配置 在整个过程中 博主也踩过了不少坑 参考了许多优秀作者的资源 将配置过程整理总结如下 第一步 配置安装ubuntu 其实主要做的事情就是 调整 Win 配置 下载配
  • nginx参数tcp_nopush和tcp_nodelay

    参数说明 你的数据传输并不需要总是准确地遵守某一选项或者其它选择 在那种情况下 你可能想要采取更为灵活的措施来控制网络连接 在发送一系列当作单一消息的数据之前设置TCP CORK 而且在发送应立即发出的短消息之前设置TCP NODELAY
  • Mybatis 个人总结

    目录 MyBatis 框架 第一章 框架的概述 1 三层架构 2 三层架构处理的流程 3 为什么使用三层架构 4 三层框架模式和框架 5 框架 6 框架解决的问题 7 jdbc访问数据库的优缺点 8 Mybatis框架 第二章 Mybati
  • H3C配置ssh密码认证登录

    注 在用户使用SSH登录交换机时 交换机对所要登录的用户使用密码对其进行身份验证 生成RSA和DSA密钥对 H3C rsa local key pair create H3C public key local create dsa 设置用户
  • MinGW-w64 C/C++编译器下载和安装

    目录 1 安装包下载 方法一 下载Installer在线安装 费时 方法二 下载离线包 较快 2 环境变量配置 3 小试牛刀 如果电脑没有安装MinGW w64 C C 编译器 在Windows的命令窗口键入gcc会提示 gcc 不是内部或
  • 单元测试_接口测试用例设计思路

    下图是 单元测试之道Java版 的脑图笔记 加上在工作中的总结
  • 重置标准输出

    import org apache log4j Appender import org apache log4j Logger import org apache log4j WriterAppender import java io Bu
  • 配置IDEA下springboot项目显示Run Dashboard面板

    在 idea workspace xml 中搜索
  • Websocket协议解析与QT代码示例

    文章目录 1 Websocket是什么 2 Websocket诞生的原因 3 Websocket与HTTP的相同点与不同点 相同点 不同点 4 Websocket实现原理 5 Websocket代码示例 效果图 核心代码 6 Wiresha
  • openldap 2.3 安装配置详解

    安装逻辑 1 安装配置master 初始化数据 将系统账户导入ldap 详细见openldap自我整理下的 openldap安装注意 配置master slapd conf文件 replogfile var lib ldap openlda
  • 如何完全卸载VS2010

    1 首先用360卸载 当卸载完成后 提示有残余的话 就强力清除2 接着 下载IobitUninstaller工具3 按照下面进行卸载1 Microsoft NET Framework 4 框架 建议保留 很多东西会用到 1 1 Micros
  • 电子学会2022年12月青少年软件编程(图形化)等级考试试卷(四级)答案解析

    目录 一 单选题 共15题 共30分 二 判断题 共10题 共20分 三 编程题 共3题 共50分
  • Cannot resolve plugin org.apache.maven.plugins:maven-install-plugin:2.5.2

    项目场景 今天在创建springboot项目的时候 遇到一个与maven相关的报错 仅此记录一下 防止以后继续踩坑 问题描述 在idea gt settings设置maven的目录信息完后 就遇到了以下报错 原因分析 经排查后发现是自己的本
  • vs code 用户代码片段失效问题

    例如 vue1 scope javascript typescript prefix v1 body new Vue el data description Log output to console 注意 1 如果没有scope的话 试着
  • 【深度学习】yolov5+deepsort 完成计数和行人行人重识别的追踪

    文章目录 前言 1 知识体系 1 1 前置说明 1 2 Sort的工作流程 1 3 deepsort 2 实践应用 3 干货补充 总结 前言 行人重识别是计算机视觉的基本任务之一 首先要有一个detector 检测器来检测到目标 然后将检测