【多目标跟踪与计数】(三)DeepSORT实战车辆和行人跟踪计数

这篇具有很好参考价值的文章主要介绍了【多目标跟踪与计数】(三)DeepSORT实战车辆和行人跟踪计数。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、DeepSort介绍

论文地址:

https://arxiv.org/pdf/1703.07402.pdf

参考文章:

DeepSort讲解

代码地址:

https://github.com/mikel-brostrom/Yolov5_DeepSort_OSNet(可参考这个源代码,如果需要我这边的源代码可私信)

SORT对比DeepSORT:

虽然SORT是一个非常简单、有效、实用的多目标跟踪算法,但仅仅通过IOU来匹配虽然速度很快,相应的ID Switch次数也多

DeepSORT在原有基础上,通过集成表观信息,使得模型能够处理目标长时间被遮挡的情况,将ID Switch这个指标降低了45%;表观信息是通过一个ReID的模型训练出来的;

思考:SORT算法中ID Switch次数较多的原因?

因为采用的关联矩阵旨在状态预测不确定性较小的时候比较准确(也就是状态估计存在一定问题)

DeepSORT改进了这个状态估计的代价计算方法,用结合运动和外观信息的代价计算来替换原先的;

二、行人ReID特征训练

行人数据集介绍:

Market 1501数据集:在清华大学校园中采集于2015年公开,总共有1501个行人、32668个检测到的行人检测框;

可在该连接下下载:下载地址

训练步骤:

1、由于数据集没有对人进行分类,首先要根据图片名称上的信息,将同一个人存放在一个文件夹下;

deepsort中文版 pdf,实战,目标跟踪,目标检测,计算机视觉,DeepSORT,人工智能

说明:

从上图可以看出,文件夹0002下为同一个人的所有图片,总共有751个这样的文件夹,也就是分为751类;

2、定义网络结构,最终将输出一个类别数量相同的特征向量,在该任务中是751维的向量;

网络结构:

前面通过几层Conv层和ReLU激活函数,对输入特征进行下采样,重点关注最后的分类层;

deepsort中文版 pdf,实战,目标跟踪,目标检测,计算机视觉,DeepSORT,人工智能

最终通过两层全连接层,将特征信息输出为751维度的一个特征向量;

实际上最终的输出类似于一个分类任务,通过输入一个图像,得到该图像的751维度的向量特征;

训练结果:

这里仅仅是训练40个epochs,在该数据集下就能达到一个很好的效果:

deepsort中文版 pdf,实战,目标跟踪,目标检测,计算机视觉,DeepSORT,人工智能

三、工具类代码讲解

首先了解下整个项目的代码结构:

deepsort中文版 pdf,实战,目标跟踪,目标检测,计算机视觉,DeepSORT,人工智能

下面看一下整个实现流程图:

deepsort中文版 pdf,实战,目标跟踪,目标检测,计算机视觉,DeepSORT,人工智能

下面依次讲解一些重要工具类代码:

nn_matching.py

作用:对于每个目标,返回最近邻居的距离度量, 即与到目前为止已观察到的任何样本的最接近距离。

1、欧式距离计算

"""
a :NxM 矩阵,代表 N 个样本,每个样本 M 个数值 
b :LxM 矩阵,代表 L 个样本,每个样本有 M 个数值 
返回的是 NxL 的矩阵,比如 dist[i][j] 代表 a[i] 和 b[j] 之间的平方和距离
"""
def _pdist(a, b):
    a, b = np.asarray(a), np.asarray(b)
    if len(a) == 0 or len(b) == 0:
        return np.zeros((len(a), len(b)))
    a2, b2 = np.square(a).sum(axis=1), np.square(b).sum(axis=1)
    r2 = -2. * np.dot(a, b.T) + a2[:, None] + b2[None, :]
    r2 = np.clip(r2, 0., float(np.inf))			# 将矩阵小于0的值都变为0
    return r2

上述实际上是通过一个公式推到,详情可查看下面的博文介绍:

https://blog.csdn.net/frankzd/article/details/80251042

拓展:

求最近邻的欧氏距离

distances = _pdist(a, b)
return np.maximum(0.0, distances.min(axis=0))	# 实际上就是求第一维度上的最小值

2、余弦距离计算

"""
a :NxM 矩阵,代表 N 个样本,每个样本 M 个数值 
b :LxM 矩阵,代表 L 个样本,每个样本有 M 个数值 
返回的是 NxL 的矩阵,比如 c[i][j] 代表 a[i] 和 b[j] 之间的余弦距离
"""


# np.linalg.norm 求向量的范式,默认是 L2 范式 
a = np.asarray(a) / np.linalg.norm(a, axis=1, keepdims=True)
b = np.asarray(b) / np.linalg.norm(b, axis=1, keepdims=True)
return 1. - np.dot(a, b.T) # 余弦距离 = 1 - 余弦相似度

参考博文:https://blog.csdn.net/u013749540/article/details/51813922

说明:求最近邻的方式和欧式距离的最近邻求法一致;

3、代价矩阵的计算

"""
计算features和targets之间的距离,返回一个成本矩阵(代价矩阵)
"""
cost_matrix = np.zeros((len(targets), len(features)))
for i, target in enumerate(targets):
	cost_matrix[i, :] = self._metric(self.samples[target], features)	# 默认采用余弦距离
return cost_matrix

个人理解:

nn_matching的主要作用我认为是DeepSORT后引进的,也就是引入了表观特征后,通过REID网络能够得到一个目标的特征信息,将两帧之间的目标得到的特征,进行余弦距离的最近邻求值,也就是上面讲解的方法,最终能得到一个代价矩阵;

linear_assignment.py

作用:通过成本矩阵以及匈牙利算法,也就是级联匹配的功能实现;

# 计算成本矩阵
cost_matrix = distance_metric(
	tracks, detections, track_indices, detection_indices)
cost_matrix[cost_matrix > max_distance] = max_distance + 1e-5

# 执行匈牙利算法,得到指派成功的索引对,行索引为tracks的索引,列索引为detections的索引
"""
官方函数说明:
https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.linear_sum_assignment.html#scipy.optimi#ze.linear_sum_assignment
"""
row_indices, col_indices = linear_assignment(cost_matrix)

matches, unmatched_tracks, unmatched_detections = [], [], []
# 找出未匹配的detections
for col, detection_idx in enumerate(detection_indices):
	if col not in col_indices:
		unmatched_detections.append(detection_idx)
# 找出未匹配的tracks
for row, track_idx in enumerate(track_indices):
	if row not in row_indices:
		unmatched_tracks.append(track_idx)
# 遍历匹配的(track, detection)索引对
for row, col in zip(row_indices, col_indices):
	track_idx = track_indices[row]
	detection_idx = detection_indices[col]
# 如果相应的cost大于阈值max_distance,也视为未匹配成功
	if cost_matrix[row, col] > max_distance:
		unmatched_tracks.append(track_idx)
		unmatched_detections.append(detection_idx)
	else:
		matches.append((track_idx, detection_idx))
return matches, unmatched_tracks, unmatched_detections

四、主代码讲解

首先来看一下检测模块,所用到YOLOV5网络,并且其效果也影响着整个任务的效果;

objdetector.py

作用:用于实现目标检测,将视频帧中的目标检测出来;

# 检测的目标对象(如果不希望检测的目标可以直接去掉)
OBJ_LIST = ['person', 'car', 'bus', 'truck']
# YoloV5模型权重,这里也可以选择其他类型的模型
DETECTOR_PATH = 'weights/yolov5m.pt'    

# 先定义一个基础类,实现初始化和函数原型
class baseDet(object):
    def __init__(self):
        self.img_size = 640		# 缩放的尺寸
        self.threshold = 0.3	# 阈值
        self.stride = 1

    def build_config(self):
        self.frameCounter = 0

    def feedCap(self, im, func_status):
        # 这个字典是最终返回的结果,也就是将模型的输出保存成字典的形式
        retDict = {
            'frame': None,
            'list_of_ids': None,
            'obj_bboxes': []
        }
        self.frameCounter += 1
        # 这里调用了objtracker,会调用ReID模型得到特征,然后进行匹配
        im, obj_bboxes = objtracker.update(self, im)
        retDict['frame'] = im
        retDict['obj_bboxes'] = obj_bboxes

        return retDict
    def init_model(self):
        raise EOFError("Undefined model type.")
    def preprocess(self):
        raise EOFError("Undefined model type.")
    def detect(self):
        raise EOFError("Undefined model type.")

# 对YOLOV5检测器的一个封装,使得使用起来更加简便
class Detector(baseDet):
    def __init__(self):
        super(Detector, self).__init__()
        self.init_model()
        self.build_config()
	
    # 加载模型
    def init_model(self):
        self.weights = DETECTOR_PATH
        self.device = '0' if torch.cuda.is_available() else 'cpu'
        self.device = select_device(self.device)
        model = attempt_load(self.weights, map_location=self.device)
        model.to(self.device).eval()
        model.half()
        self.m = model
        self.names = model.module.names if hasattr(
            model, 'module') else model.names
	
    # 对传进来的视频帧进行预处理
    def preprocess(self, img):
        img0 = img.copy()
        img = letterbox(img, new_shape=self.img_size)[0]
        img = img[:, :, ::-1].transpose(2, 0, 1)
        img = np.ascontiguousarray(img)
        img = torch.from_numpy(img).to(self.device)
        img = img.half()  # 半精度
        img /= 255.0  # 图像归一化
        if img.ndimension() == 3:
            img = img.unsqueeze(0)
        return img0, img		# img0是原始的图像,img是处理后的图像

    def detect(self, im):
        im0, img = self.preprocess(im)
        pred = self.m(img, augment=False)[0]		# 将图像传入检测器中,得到推理后的结果
        pred = pred.float()
        pred = non_max_suppression(pred, self.threshold, 0.4)	# 进行非极大值抑制
        pred_boxes = []
        for det in pred:
            if det is not None and len(det):
                det[:, :4] = scale_coords(
                    img.shape[2:], det[:, :4], im0.shape).round()
                for *x, conf, cls_id in det:
                    lbl = self.names[int(cls_id)]
                    if not lbl in OBJ_LIST:			# 这里就是判断类别,不在我们需要检测的类别中就跳过
                        continue
                    x1, y1 = int(x[0]), int(x[1])
                    x2, y2 = int(x[2]), int(x[3])
                    pred_boxes.append(
                        (x1, y1, x2, y2, lbl, conf))
        return im, pred_boxes							# 最后返回原始图像以及检测到的目标框

objtracker.py

作用:一个跟踪器的类,对检测后的目标进行跟踪;

cfg = get_config()
cfg.merge_from_file("deep_sort/configs/deep_sort.yaml")
# 首先需要实例化一个DeepSORT的类,其中封装了一些工具类的实现
deepsort = DeepSort(cfg.DEEPSORT.REID_CKPT,
                    max_dist=cfg.DEEPSORT.MAX_DIST, min_confidence=cfg.DEEPSORT.MIN_CONFIDENCE,
                    nms_max_overlap=cfg.DEEPSORT.NMS_MAX_OVERLAP, max_iou_distance=cfg.DEEPSORT.MAX_IOU_DISTANCE,
                    max_age=cfg.DEEPSORT.MAX_AGE, n_init=cfg.DEEPSORT.N_INIT, nn_budget=cfg.DEEPSORT.NN_BUDGET,
                    use_cuda=True)
                    
# 下面重点看一下update这个函数,也就是更新图像中的检测框
def update(target_detector, image):
		# 这里也就是用之前的检测器得到检测框
		_, bboxes = target_detector.detect(image)
        bbox_xywh = []
        confs = []
        bboxes2draw = []
        if len(bboxes):
            # Adapt detections to deep sort input format(更新检测对象的状态)
            for x1, y1, x2, y2, _, conf in bboxes:
                obj = [
                    int((x1+x2)/2), int((y1+y2)/2),
                    x2-x1, y2-y1
                ]
                bbox_xywh.append(obj)
                confs.append(conf)
            xywhs = torch.Tensor(bbox_xywh)
            confss = torch.Tensor(confs)

            # Pass detections to deepsort(这里就可以得到最终的这一帧的目标框和目标ID)
            outputs = deepsort.update(xywhs, confss, image)
            for value in list(outputs):
                x1,y1,x2,y2,track_id = value
                bboxes2draw.append(
                    (x1, y1, x2, y2, '', track_id)
                )
        # 这里起到一个将检测框和ID信息绘制到图像上的作用
        image = plot_bboxes(image, bboxes2draw)
        return image, bboxes2draw

demo.py

作用:实现对传入的视频文件进行目标跟踪,并将最终结果保存为一个新的视频文件;实际上就是将之前封装好的类进行调用,得到我们想要的信息,最终进行可视化的呈现;

VIDEO_PATH = './video/test_person.mp4'		# 传入视频文件
RESULT_PATH = 'result.mp4'					# 输出视频文件

def main():

    func_status = {}
    func_status['headpose'] = None
    
    name = 'demo'

    det = Detector()
    cap = cv2.VideoCapture(VIDEO_PATH)
    fps = int(cap.get(5))
    print('fps:', fps)			# 得到帧率
    t = int(1000/fps)			# 每一帧的间隔时间

    size = None
    videoWriter = None

    while True:

        # try:
        _, im = cap.read()	# 读入一帧帧数据,也就是视频的全部帧
        if im is None:
            break
        
        result = det.feedCap(im, func_status)	# 这里的im表示所有输入视频帧
        result = result['frame']
        result = imutils.resize(result, height=500)
        # 下面代码也就是保存成视频的一个操作
        if videoWriter is None:
            fourcc = cv2.VideoWriter_fourcc(
                'm', 'p', '4', 'v')  # opencv3.0
            videoWriter = cv2.VideoWriter(
                RESULT_PATH, fourcc, fps, (result.shape[1], result.shape[0]))

        videoWriter.write(result)
        cv2.imshow(name, result)
        cv2.waitKey(t)

        if cv2.getWindowProperty(name, cv2.WND_PROP_AUTOSIZE) < 1:
            # 点x退出
            break

    cap.release()
    videoWriter.release()
    cv2.destroyAllWindows()

效果展示:
deepsort中文版 pdf,实战,目标跟踪,目标检测,计算机视觉,DeepSORT,人工智能

count_person.py

作用:实现计数的功能,可以对检测到的目标进行计数统计;

这部分的代码主要是实现自定义撞线,并且统计经过的目标数量和ID,代码就不在这进行讲解了;

行人计数效果展示:

deepsort中文版 pdf,实战,目标跟踪,目标检测,计算机视觉,DeepSORT,人工智能

当然,像车辆计数也是可以实现的,效果如下:

deepsort中文版 pdf,实战,目标跟踪,目标检测,计算机视觉,DeepSORT,人工智能

五、总结

关键知识点:

  • 理解DeepSORT全流程;
  • 对目标检测,目标重识别有初步认知;
  • 多个模型的任务集成;
  • 距离度量方式及代价矩阵的计算;
  • 将代码封装成类的能力;

待深入知识点:文章来源地址https://www.toymoban.com/news/detail-787724.html

  • 卡尔曼滤波器的作用和实现;
  • 如何改进任务的运行效率;
  • 将该任务进行部署,转换成用C++可调用的动态库;
  • 轻量化其中的流程和模型,优化性能;

到了这里,关于【多目标跟踪与计数】(三)DeepSORT实战车辆和行人跟踪计数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【CV\tracking】多目标跟踪(OpenCV+YOLO+DeepSORT)|| 项目实战

    【start:2022.12.15】 数据集来源的竞赛:细胞跟踪挑战赛 制作数据集的工具:自制VOC2007数据集——train、trainval、val、test文件的生成 【code】结合YOLOv5的DeepSORT的可成功复现的项目:https://github.com/HowieMa/DeepSORT_YOLOv5_Pytorch 本文复现基于上述项目 【YOLOv3】yolov3训练自己的数据集

    2024年02月07日
    浏览(75)
  • 记一次调试YOLOv5+DeepSort车辆跟踪项目的经过

    摘要:学习别人的开源项目是日常的一项必备技能,本文通过一个车辆跟踪(YOLOv5+DeepSort)的例子介绍如何配置和调试GitHub上的开源代码。以第一人称的视角给出本人调试代码的过程,包括项目readme的阅读、python环境配置、代码调试运行等,详细的过程已录制在视频中。完整

    2023年04月12日
    浏览(38)
  • YOLOv5+DeepSort实现目标跟踪

    本文将展示如何将YOLOv5和Deepsort相结合,实现目标跟踪计数等功能。由于水平有限,本文将更多着眼于如何实现,原理部分我会推荐几篇博客供大家学习参考。 推荐下面这篇博客,讲的很细致: Yolov5_DeepSort_Pytorch代码学习与修改记录__helen_520的博客 YOLOv5具备目标检测的功能,

    2024年02月03日
    浏览(48)
  • DeepSORT多目标跟踪——算法流程与源码解析

    1. 目标检测 在目标检测任务中,主要目标是识别图像或视频帧中存在的物体的位置和类别信息。这意味着目标检测算法需要定位物体的边界框(Bounding Box)并确定每个边界框内的物体属于哪个类别(如人、汽车、狗等)。目标检测通常独立地处理每一帧图像,不考虑目标在不

    2024年04月13日
    浏览(50)
  • 经典多目标跟踪算法DeepSORT的基本原理和实现

    点击蓝字 关注我们,让开发变得更有趣 作者| 杨亦诚 排版| 李擎 经典多目标跟踪算法DeepSORT的基本原理和实现 OpenVINO 目标检测 vs 目标跟踪 在开始介绍DeepSORT的原理之前呢,我们先来了解下目标检测,和目标跟踪之间的区别: · 目标检测:在目标检测任务中,我们需要利用A

    2024年02月03日
    浏览(40)
  • 人流目标跟踪pyqt界面_v5_deepsort

    直接上效果图 代码仓库和视频演示b站视频006期: 到此一游7758258的个人空间-到此一游7758258个人主页-哔哩哔哩视频 代码展示: YOLOv5 DeepSORT介绍 YOLOv5 DeepSORT是一个结合了YOLOv5和DeepSORT算法的目标检测与多目标跟踪系统。让我为您详细解释一下这两个部分: YOLOv5 : YOLO(You O

    2024年02月13日
    浏览(45)
  • MAC M2芯片执行yolov8 + deepsort 实现目标跟踪

    步骤过程尝试: 执行mot17 数据集 到coco格式 执行mps发现显存不够用 选择autodl 上的服务器进行训练 安装conda install git 然后重新进行 pycocotools.进行 step 2 安装docker 环境 添加官方秘钥 安装docker环境失败 也是可以运行的 不影响bytetrack训练 这个是使用best权重计算得到的 下面是使

    2024年02月10日
    浏览(45)
  • 基于YOLOv8与DeepSORT实现多目标跟踪——算法与源码解析

    \\\"目标跟踪 (Object Tracking)\\\"是机器视觉领域中的一个重要研究领域。根据跟踪的目标数量,可以将其分为两大类:单目标跟踪 (Single Object Tracking,简称 SOT) 和多目标跟踪 (Multi Object Tracking,简称 MOT)。 多目标跟踪往往面临一些挑战,例如需要同时跟踪多个目标、目标可能频繁遮挡

    2024年02月05日
    浏览(51)
  • AI项目八:yolo5+Deepsort实现目标检测与跟踪(CPU版)

    若该文为原创文章,转载请注明原文出处。    DeepSORT 是一种计算机视觉跟踪算法,用于在为每个对象分配 ID 的同时跟踪对象。DeepSORT 是 SORT(简单在线实时跟踪)算法的扩展。DeepSORT 将深度学习引入到 SORT 算法中,通过添加外观描述符来减少身份切换,从而提高跟踪效率。

    2024年02月07日
    浏览(52)
  • 目标跟踪:Deepsort--卡尔曼滤波、匈牙利匹配、马氏距离、欧氏距离、级联匹配、reid

    本篇文章供自己学习回顾,其中错误希望指出! 先把目标跟踪中涉及到的名词抛出来: 1、卡尔曼滤波、 2、匈牙利匹配:https://blog.csdn.net/DeepCBW/article/details/124740092 3、马氏距离、 4、欧氏距离、 5、级联匹配、 6、代价矩阵 sort过程比较简单,先搬个图: 基于上图展开对sort的

    2024年02月05日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包