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

11 篇文章 5 订阅
3 篇文章 0 订阅


前言

行人重识别是计算机视觉的基本任务之一,首先要有一个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,详见我的另一篇文章。

  • 11
    点赞
  • 130
    收藏
    觉得还不错? 一键收藏
  • 26
    评论
近年来,深度学习技术的飞速发展,使得视觉识别技术在车辆和行人的监管和管控等领域中逐渐得到广泛应用。其中,由YOLOv5DeepSORT组合实现的车辆行人的检测、追踪计数系统具有出色的性能和灵活性,已经被广泛运用于交通安全、城市智慧管理等领域。 YOLOv5是目前最先进的一种物体检测技术,运用领先的深度学习算法和现代化的硬件设备,实现了毫秒级别的准确物体检测,并能够在大规模数据上进行训练,具有高精度、高效率和高适应性等特点。 DeepSORT算法则是一种基于卡尔曼滤波和匈牙利算法的目标追踪技术,采用多个视觉特征联合匹配的方法,能够实现对多目标的快速准确跟踪,并通过追踪结果进行目标计数。 综合应用YOLOv5DeepSORT技术,可以实现对车辆和行人的实时检测、追踪计数。具体实现方式包括以下步骤: 首先,通过YOLOv5模型对输入图像进行物体检测,根据预设的检测策略和检测器参数,输出每个检测框包含的目标信息和对应的位置。 其次,根据DeepSORT模型进行目标追踪,将已检测到的目标与上一帧已追踪的目标进行特征匹配,根据距离和可信度等特征进行目标关联,以确定目标的轨迹并不断更新。 最后,通过对轨迹进行简单的统计和整理,即可得到对车辆和行人的统计计数结果,实现对目标的全程监控和管理。 总体来说,YOLOv5DeepSORT结合的车辆行人检测、追踪计数系统可以快速准确实现对城市的智慧化管理,为保障城市公众的生命、安全和财产利益提供了关键技术支持。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值