- 名词n
- 1.种类,品种,类型
- 外文名
- sort
- 及物动词vt
- .把...分类
- 中文名
- 种类
- C++头文件
- algorithm
-
2022-01-19 21:34:37
一、参考资料
项目源码
pytorch yolo5+Deepsort实现目标检测和跟踪工程落地
YoloV5 + deepsort + Fast-ReID 完整行人重识别系统(三)
yolov5-deepsort-pedestrian-counting
Yolov5-Deepsort-Fastreid二、相关介绍
Deepsort是实现目标跟踪的算法,从sort(simple online and realtime tracking)演变而来。其使用卡尔慢滤波器预测所检测对象的运动轨迹,匈牙利算法将它们与新检测的目标匹配。Deepsort易于使用,且速度快,成为AI目标检测跟踪的热门算法。
三、重要说明
-
yolov5可检测多种类型的目标,而Deepsort目标跟踪只能跟踪一种类型目标,例如person、car。所以,跟踪需要把yolov5的目标检测类型数量限制成单个类型检测。coco数据集定义:person=0,car=2。
# 行人跟踪 python track.py --classes 0 --source demo_person.mp4 # 小汽车跟踪 python track.py --classes 2 --source demo_car.mp4
-
yolov5提供不同检测精度的权重文件,yolov5x.pt比yolov5s.pt精度高。应用跟踪时,当两个目标重叠后再分离,yolov5s.pt会出现标注数改变。比如,目标10和目标20发生重叠分离,目标10变成了目标15,而目标20不变(目标20遮挡目标10)。此种情况,用yolov5x.pt会好很多,维持目标10不变。
-
yolov5限定单个类型,不需要重新训练。faster rcnn、ResNet限定单个类型,单需要重新训练。
-
yolov5的速度明显优于FastRCNN,且消耗GPU资源少。用FastRCNN,还没用到Deepsort,只看逐帧检测,速度比yolov5+Deepsort逐帧目标检测还要慢,且GPU使用率达到95%。
-
yolov5的训练速度比Faster RCNN、ResNet50、FPN快。
四、实验环境
4.1 系统环境
Environment Operating System + Version: Ubuntu + 16.04 GPU Type: GeForce GTX1650,4GB Nvidia Driver Version: 470.63.01 CUDA Version: 10.2.300 CUDNN Version: 7.6.5 Python Version (if applicable): 3.6.14 virtualenv:20.13.0 gcc:7.5.0 g++:7.5.0
4.2 requirements-gpu.txt
absl-py==1.0.0 cached-property==1.5.2 cachetools==4.2.4 certifi==2021.10.8 charset-normalizer==2.0.10 cycler==0.11.0 Cython==0.29.26 dataclasses==0.8 distlib==0.3.4 easydict==1.9 filelock==3.4.1 flake8==4.0.1 future==0.18.2 gdown==3.10.1 google-auth==2.3.3 google-auth-oauthlib==0.4.6 grpcio==1.43.0 h5py==3.1.0 idna==3.3 imageio==2.13.5 importlib-metadata==4.2.0 importlib-resources==5.4.0 isort==4.3.21 kiwisolver==1.3.1 Markdown==3.3.5 matplotlib==3.3.4 mccabe==0.6.1 numpy==1.19.5 oauthlib==3.1.1 opencv-python==4.5.5.62 pandas==1.1.5 Pillow==8.4.0 platformdirs==2.4.0 protobuf==3.19.3 pyasn1==0.4.8 pyasn1-modules==0.2.8 pycodestyle==2.8.0 pyflakes==2.4.0 pyparsing==3.0.6 PySocks==1.7.1 python-dateutil==2.8.2 pytz==2021.3 PyYAML==6.0 requests==2.27.1 requests-oauthlib==1.3.0 rsa==4.8 scipy==1.5.4 seaborn==0.11.2 six==1.16.0 tb-nightly==2.8.0a20220117 tensorboard-data-server==0.6.1 tensorboard-plugin-wit==1.8.1 torch==1.9.0+cu102 torchvision=0.10.0+cu102 tqdm==4.62.3 typing_extensions==4.0.1 urllib3==1.26.8 virtualenv==20.13.0 Werkzeug==2.0.2 yacs==0.1.8 yapf==0.32.0 zipp==3.6.0
五、关键步骤
5.1 下载 github 仓库代码
5.1.1 下载 Yolov5_DeepSort_Pytorch
git clone https://github.com/mikel-brostrom/Yolov5_DeepSort_Pytorch.git
5.1.2 项目目录
. ├── deep_sort │ ├── configs │ ├── deep │ ├── deep_sort.py │ ├── __init__.py │ ├── LICENSE │ ├── __pycache__ │ ├── README.md │ ├── sort │ └── utils ├── inference # infer 推理的结果 │ └── output ├── LICENSE ├── MOT16_eval │ ├── eval.sh │ ├── track_all.gif │ └── track_pedestrians.gif ├── README.md ├── requirementes-gpu.txt ├── requirements.txt ├── runs │ └── track ├── track.py ├── venv # virtualenv 创建的虚拟环境 │ ├── bin │ ├── lib │ └── pyvenv.cfg ├── yolov5 # clone yolov5 to this path │ ├── CONTRIBUTING.md │ ├── data │ ├── detect.py │ ├── Dockerfile │ ├── export.py │ ├── hubconf.py │ ├── LICENSE │ ├── models │ ├── README.md │ ├── requirements.txt │ ├── setup.cfg │ ├── train.py │ ├── tutorial.ipynb │ ├── utils │ ├── val.py │ └── weights
5.1.3 下载 yolov5
下载到
Yolov5_DeepSort_Pytorch
根目录下,删除之前的yolov5文件夹。git clone https://github.com/ultralytics/yolov5.git
5.1.4 修改文件夹名称
deep-person-reid 改为 reid
5.2 环境配置
5.2.1 (可选)创建virtualenv虚拟环境
# 进入项目路径 cd Yolov5_DeepSort_Pytorch # 创建虚拟环境 virtualenv --system-site-packages -p /usr/bin/python venv # 激活虚拟环境 source ./venv/bin/activate
5.2.2 安装依赖包
# 安装依赖包 pip install -r requirements.txt
5.3 下载预训练模型
选择目标检测模型:yolov5;
选择DeepSort模型:ReID;5.3.1 下载yolov5预训练模型
下载地址,并放入目录
Yolov5_DeepSort_Pytorchyolo5/weights
比如, yolov5s.pt
python track.py --source 0 --yolo_model yolov5/weights/yolov5n.pt --img 640 yolov5/weights/yolov5s.pt yolov5/weights/yolov5m.pt yolov5/weights/yolov5l.pt yolov5/weights/yolov5x.pt --img 1280 ...
5.3.2 下载Deepsort预训练模型
下载地址,放入目录
Yolov5_DeepSort_Pytorch/deep_sort_pytorch/deep_sort/deep/checkpoint
比如,osnet_x1_0
python track.py --source 0 --deep_sort_model osnet_x1_0 nasnsetmobile resnext101_32x8d
5.4 infer推理测试
python track.py --source 0 --yolo_model yolov5/weights/yolov5n.pt --deep_sort_model osnet_x1_0 --img 640
(venv) yichao@yichao:~/MyDocuments/Yolov5_DeepSort_Pytorch$ python track.py --source 0 --yolo_model yolov5/weights/yolov5s.pt --deep_sort_model osnet_x1_0 --img 640 deep_sort/deep/reid/torchreid/metrics/rank.py:12: UserWarning: Cython evaluation (very fast so highly recommended) is unavailable, now use python evaluation. 'Cython evaluation (very fast so highly recommended) is ' Successfully loaded imagenet pretrained weights from "/home/yichao/MyDocuments/Yolov5_DeepSort_Pytorch/deep_sort/deep/checkpoint/osnet_x1_0_imagenet.pth" Selected model type: osnet_x1_0 YOLOv5 🚀 v6.0-193-gdb1f83b torch 1.9.0+cu102 CUDA:0 (NVIDIA GeForce GTX 1650, 3904MiB) YOLOv5 🚀 v6.0-193-gdb1f83b torch 1.9.0+cu102 CUDA:0 (NVIDIA GeForce GTX 1650, 3904MiB) weight_path: yolov5/weights/yolov5s.pt weight_path: yolov5/weights/yolov5s.pt Fusing layers... Model Summary: 224 layers, 7266973 parameters, 0 gradients 1/1: 0... Success (inf frames 640x480 at 30.00 FPS) 0: 480x640 1 person, Done. YOLO:(0.428s), DeepSort:(0.220s) 0: 480x640 1 person, Done. YOLO:(0.023s), DeepSort:(0.020s) 0: 480x640 1 person, Done. YOLO:(0.010s), DeepSort:(0.013s) 0: 480x640 1 person, Done. YOLO:(0.011s), DeepSort:(0.013s) 0: 480x640 1 person, Done. YOLO:(0.011s), DeepSort:(0.013s) 0: 480x640 1 person, Done. YOLO:(0.011s), DeepSort:(0.013s) 0: 480x640 1 person, Done. YOLO:(0.011s), DeepSort:(0.013s) 0: 480x640 1 person, Done. YOLO:(0.011s), DeepSort:(0.013s) 0: 480x640 1 person, Done. YOLO:(0.011s), DeepSort:(0.013s) 0: 480x640 1 person, Done. YOLO:(0.011s), DeepSort:(0.013s) 0: 480x640 1 person, Done. YOLO:(0.011s), DeepSort:(0.013s) 0: 480x640 1 person, Done. YOLO:(0.011s), DeepSort:(0.013s) 0: 480x640 1 person, Done. YOLO:(0.011s), DeepSort:(0.013s) 0: 480x640 1 person, Done. YOLO:(0.011s), DeepSort:(0.013s) 0: 480x640 1 person, Done. YOLO:(0.011s), DeepSort:(0.013s) 0: 480x640 1 person, Done. YOLO:(0.011s), DeepSort:(0.013s) ... ... ...
5.5 修改源码
由于预训练模型是手动下载的,所以需要修改源码中的路径。
修改
osnet_ain.py
源码# 源码路径 Yolov5_DeepSort_Pytorch/deep_sort/deep/reid/torchreid/models/osnet.py cached_file = os.path.join(model_dir, filename) 改为 cached_file = "/home/yichao/MyDocuments/Yolov5_DeepSort_Pytorch/deep_sort/deep/checkpoint/osnet_x1_0_imagenet.pth"
5.6 infer推理速度
0: 480x640 1 person, Done. YOLO:(0.011s), DeepSort:(0.013s) 0: 480x640 1 person, Done. YOLO:(0.010s), DeepSort:(0.013s) 0: 480x640 1 person, Done. YOLO:(0.011s), DeepSort:(0.013s) 0: 480x640 1 person, Done. YOLO:(0.010s), DeepSort:(0.013s) 0: 480x640 1 person, Done. YOLO:(0.011s), DeepSort:(0.013s) 0: 480x640 1 person, Done. YOLO:(0.010s), DeepSort:(0.013s) # yolov5+Deepsort,大约24ms,即41FPS # DeepSort 的速度取决于画面中目标的数目,上述数据是在单目标的情况下进行统计的。
5.7 显存占用情况
Tue Jan 18 16:06:27 2022 +-----------------------------------------------------------------------------+ | NVIDIA-SMI 470.63.01 Driver Version: 470.63.01 CUDA Version: 11.4 | |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | | | | MIG M. | |===============================+======================+======================| | 0 NVIDIA GeForce ... Off | 00000000:01:00.0 On | N/A | | 27% 43C P0 44W / 75W | 1895MiB / 3903MiB | 79% Default | | | | N/A | +-------------------------------+----------------------+----------------------+ +-----------------------------------------------------------------------------+ | Processes: | | GPU GI CI PID Type Process name GPU Memory | | ID ID Usage | |=============================================================================| | 0 N/A N/A 1529 G /usr/lib/xorg/Xorg 218MiB | | 0 N/A N/A 5928 C python 1673MiB | +-----------------------------------------------------------------------------+
六、TensorRT加速目标跟踪
YoloV5+DeepSort+TensorRT 目标检测、跟踪
yolov5_deepsort_tensorrt_cpp七、可能存在的问题
Q1: 缺少
_bz2
文件File "/usr/local/lib/python3.6/bz2.py", line 23, in <module> from _bz2 import BZ2Compressor, BZ2Decompressor ModuleNotFoundError: No module named '_bz2'
错误原因: 缺少 _bz2.cpython-36m-x86_64-linux-gnu.so 文件 解决办法: 把系统自带Python3.6的“_bz2.cpython-36m-x86_64-linux-gnu.so”文件,放到Python3.8的文件夹中。 如果是Python3.8版本,也可以把文件改名后,放到Python3.6的文件夹中。 sudo cp /home/yichao/miniconda3/envs/compress_model/lib/python3.6/lib-dynload/_bz2.cpython-36m-x86_64-linux-gnu.so /usr/local/lib/python3.6/lib-dynload/
Q2: 缺少
DetectMultiBackend
Traceback (most recent call last): File "track.py", line 24, in <module> from yolov5.models.common import DetectMultiBackend ImportError: cannot import name 'DetectMultiBackend'
错误原因: 博主使用的不是最新版本的yolov5,且使用的分支是v5.0,yolov5/models/common文件中暂不支持DetectMultiBackend。 解决办法: 下载最新版本的yolov5,切换到最新的分支v6.0。 git clone https://github.com/ultralytics/yolov5.git git checkout -b 新分支名称(创建) tag_name
Q3: CUDA错误
File "./yolov5/models/yolo.py", line 222, in fuse m.conv = fuse_conv_and_bn(m.conv, m.bn) # update conv File "./yolov5/utils/torch_utils.py", line 207, in fuse_conv_and_bn fusedconv.weight.copy_(torch.mm(w_bn, w_conv).view(fusedconv.weight.shape)) RuntimeError: CUDA error: CUBLAS_STATUS_INTERNAL_ERROR when calling `cublasCreate(handle)`
错误原因: pytorch的版本问题,博主由于粗心,安装了CPU版本的Pytorch和pytorchvision。 torch-1.8.0-cp36-cp36m-linux_x86_64.whl torchvision-0.9.0-cp36-cp36m-linux_x86_64.whl 解决办法: 安装GPU版本的pytorch和pytorchvison。 torch-1.9.0+cu102-cp36-cp36m-linux_x86_64.whl torchvision-0.10.0+cu102-cp36-cp36m-linux_x86_64.whl pytorch与pytorchvision版本对齐,参考 [Pytorch安装教程](https://blog.csdn.net/m0_37605642/article/details/117855911)
Q4: pytorch版本问题
File "/home/yichao/MyDocuments/Yolov5_DeepSort_Pytorch/venv/lib/python3.6/site-packages/torchvision/extension.py", line 63, in _assert_has_ops "Couldn't load custom C++ ops. This can happen if your PyTorch and " RuntimeError: Couldn't load custom C++ ops. This can happen if your PyTorch and torchvision versions are incompatible, or if you had errors while compiling torchvision from source. For further information on the compatible versions, check https://github.com/pytorch/vision#installation for the compatibility matrix. Please check your PyTorch version with torch.__version__ and your torchvision version with torchvision.__version__ and verify if they are compatible, and if not please reinstall torchvision so that it matches your PyTorch install.
错误原因: 博主由于粗心,安装了cpu版本的torchvision,与GPU版本的pytorch不匹配。 torchvision-0.10.0-cp36-cp36m-linux_x86_64.whl 解决办法: 卸载pytorchvision,安装GPU版本的pytorchvison。 torchvision-0.10.0+cu102-cp36-cp36m-linux_x86_64.whl
更多相关内容 -
-
C++ sort()排序详解
2020-05-05 22:00:36本文对C++中的sort()做了一个比较详细的说明,包括为什么选择使用sort()、sort()函数的实现原理、sort()的使用方法等等。sort()简介
为什么选择使用sort()
在刷题的时候我们经常会碰到排序的问题,如果我们不使用一些排序的方法那我们只能手撕排序,这样就会浪费一些时间。而且我们还需要根据需要去选择相关的排序方法:
冒泡排序、快速排序、插入排序、希尔排序、归并排序、选择排序、堆排序、基数排序、桶排序
。在选择的过程中也需要我们花费一些时间,所以在明白这些经典排序的情况下再一遍一遍的手写就有点浪费时间啦!
如果我们使用sort()
方法就可以只需要一条语句就可以实现排序,这样就极大的节省了我们在刷题中所花费的时间。当然如果对这些经典的排序方法不熟悉的话还是建议大家去了解一下这些方法,比较一下这些方法的优劣以及使用的情景。sort()函数的实现原理
也许你会疑问,我使用sort方法对数据进行排序就一定合适吗?sort()可以根据我的需要对数据进行排序吗?其实sort()函数还是一个比较灵活的函数。很多解释是:sort()函数是类似于快速排序的方法,时间复杂度为n*log2(n),执行效率较高。
其实STL中的sort()并非只是普通的快速排序
,除了对普通的快速排序进行优化,它还结合了插入排序和堆排序。根据不同的数量级别以及不同情况,能自动选用合适的排序方法。当数据量较大时采用快速排序,分段递归。一旦分段后的数据量小于某个阀值,为避免递归调用带来过大的额外负荷,便会改用插入排序。而如果递归层次过深,有出现最坏情况的倾向,还会改用堆排序。所以说sort()是一个比较灵活的函数,它也会根据我们数据的需要进行排序,所以我们就不用担心以上的问题了。对于大部分的排序需求,sort()都是可以满足的。sort()的使用方法
头文件
在C++中使用sort()函数需要使用
#include<algorithm>
头文件。algorithm意为"算法",是C++的标准模版库(STL)中最重要的头文件之一,提供了大量基于迭代器的非成员模版函数。该头文件的详细使用方法以及包含的函数请参考:C++API之algorithm。sort()基本使用方法
sort()函数可以对给定区间所有元素进行排序。它有三个参数
sort(begin, end, cmp)
,其中begin为指向待sort()的数组的第一个元素的指针
,end为指向待sort()的数组的最后一个元素的下一个位置的指针
,cmp参数为排序准则,cmp参数可以不写,如果不写的话,默认从小到大进行排序。如果我们想从大到小排序可以将cmp参数写为greater<int>()
就是对int数组进行排序,当然<>
中我们也可以写double、long、float等等。如果我们需要按照其他的排序准则,那么就需要我们自己定义一个bool类型的函数来传入。比如我们对一个整型数组进行从大到小排序:#include<iostream> #include<algorithm> using namespace std; int main(){ int num[10] = {6,5,9,1,2,8,7,3,4,0}; sort(num,num+10,greater<int>()); for(int i=0;i<10;i++){ cout<<num[i]<<" "; }//输出结果:9 8 7 6 5 4 3 2 1 0 return 0; }
自定义排序准则
上面我们说到sort()函数可以自定义排序准则,以便满足不同的排序情况。使用sort()我们不仅仅可以从大到小排或者从小到大排,还可以按照一定的准则进行排序。比如说我们按照每个数的个位进行从大到小排序,我们就可以根据自己的需求来写一个函数作为排序的准则传入到sort()中。
我们可以将这个函数定义为:bool cmp(int x,int y){ return x % 10 > y % 10; }
然后我们将这个cmp函数作为参数传入sort()中即可实现了上述排序需求。
#include<iostream> #include<algorithm> using namespace std; bool cmp(int x,int y){ return x % 10 > y % 10; } int main(){ int num[10] = {65,59,96,13,21,80,72,33,44,99}; sort(num,num+10,cmp); for(int i=0;i<10;i++){ cout<<num[i]<<" "; }//输出结果:59 99 96 65 44 13 33 72 21 80 return 0; }
对结构体进行排序
sort()也可以对结构体进行排序,比如我们定义一个结构体含有学生的姓名和成绩的结构体Student,然后我们按照每个学生的成绩从高到底进行排序。首先我们将结构体定义为:
struct Student{ string name; int score; Student() {} Student(string n,int s):name(n),score(s) {} };
根据排序要求我们可以将排序准则函数写为:
bool cmp_score(Student x,Student y){ return x.score > y.score; }
完整代码:
#include<iostream> #include<string> #include<algorithm> using namespace std; struct Student{ string name; int score; Student() {} Student(string n,int s):name(n),score(s) {} }; bool cmp_score(Student x,Student y){ return x.score > y.score; } int main(){ Student stu[3]; string n; int s; for(int i=0;i<3;i++){ cin>>n>>s; stu[i] = Student(n,s); } sort(stu,stu+3,cmp_score); for(int i=0;i<3;i++){ cout<<stu[i].name<<" "<<stu[i].score<<endl; } return 0; }
再比如每一个学生有四科成绩,我们需要根据学生的四科成绩的平均分高低进行排名,那么这个cmp函数我们就可以定义为:
bool cmp_score(Student x,Student y){ double average_x,average_y; average_x = (x.score[0]+x.score[1]+x.score[2]+x.score[3])/4; average_y = (y.score[0]+y.score[1]+y.score[2]+y.score[3])/4; return average_x > average_y; }
完整代码:
#include<iostream> #include<string> #include<algorithm> using namespace std; struct Student{ string name; double score[4]; }; bool cmp_score(Student x,Student y){ double average_x,average_y; average_x = (x.score[0]+x.score[1]+x.score[2]+x.score[3])/4; average_y = (y.score[0]+y.score[1]+y.score[2]+y.score[3])/4; return average_x > average_y; } int main(){ Student stu[3]; string n; int s; for(int i=0;i<3;i++){ cin>>stu[i].name; for(int j=0;j<4;j++){ cin>>stu[i].score[j]; } } sort(stu,stu+3,cmp_score); for(int i=0;i<3;i++){ cout<<stu[i].name<<" "; for(int j=0;j<4;j++){ cout<<stu[i].score[j]<<" "; } cout<<endl; } return 0; }
以上就是对于sort()用法的详解了。如果你觉得我的文章对你有用请点个赞支持一下吧,喜欢我写的文章那么请点个关注再走鸭,您的关注是对我最大的支持。如果此文章有错误或者有不同的见解欢迎评论或者私信。
我是ACfun:一个成长中的程序猿,感谢大家的支持。 -
多目标跟踪算法 | DeepSort
2022-02-08 16:54:59开源地址:https://github.com/nwojke/deep_sort 一、多目标跟踪的工作流程(常规) (1)给定视频的原始帧; (2)运行对象检测器以获得对象的边界框; (3)对于每个检测到的物体,计算出不同的特征.前言
本文分享多目标跟踪算法的经典算法DeepSort,它是一个两阶段的算法,达到实时跟踪效果,曾被应用于工业开发。DeepSort是基于Sort目标跟踪进行的改进,它引入深度学习模型,在实时目标跟踪过程中,提取目标的外观特征进行最近邻近匹配。
目的:改善有遮挡情况下的目标追踪效果;同时,也减少了目标ID跳变的问题。
核心思想:使用递归的卡尔曼滤波和逐帧的匈牙利数据关联。
论文名称:(ICIP2017)Single-Simple Online and Realtime Tracking with a Deep Association Metric
论文地址:https://arxiv.org/abs/1703.07402
开源地址:https://github.com/nwojke/deep_sort
目录
一、通常的多目标跟踪工作流程
(1)给定视频的原始帧;
(2)运行对象检测器以获得对象的边界框;
(3)对于每个检测到的物体,计算出不同的特征,通常是视觉和运动特征;
(4)之后,相似度计算步骤计算两个对象属于同一目标的概率;
(5)最后,关联步骤为每个对象分配数字ID。
二、前提知识:Sort 多目标跟踪
四个核心步骤
1. 获取目标检测框,( 检测器: Faster R-CNN 、 YOLO )。2. 卡尔曼滤波器预测当前位置 ,获得预测框。3. 进行相似度计算,计算 前面的帧和当前帧目标 之间的匹配程度。(只考虑运动信息)4. 通过匈牙利算法进行 数据关联,为每个对象分配目标的 ID 。它以“每个检测框”与“现有目标的所有预测框”之间的IOU作为前后帧之间目标关系的度量指标。
算法核心
- 卡尔曼滤波的预测和更新过程。
- 匹配的过程。
其中,卡尔曼滤波可以基于目标前面时刻的位置,来预测当前时刻的位置。匈牙利算法可以告诉我们当前帧的某个目标,是否与前面帧的某个目标相同。
优点:Sort目标跟踪算法速度很快;在没有遮挡的情况下准确度很高。
缺点:它对物体遮挡几乎没有处理,导致ID switch 次数很高;在有遮挡情况下,准确度很低。
三、DeepSort 多目标跟踪
背景:DeepSort是基于Sort目标跟踪进行的改进,它引入深度学习模型,在实时目标跟踪过程中,提取目标的外观特征进行最近邻近匹配。
目的:改善有遮挡情况下的目标追踪效果;同时,也减少了目标ID跳变的问题。
核心思想:使用递归的卡尔曼滤波和逐帧的匈牙利数据关联。
四、卡尔曼滤波器——跟踪场景定义
假定跟踪场景是定义在 8 维状态空间(u, v, γ, h, ẋ, ẏ, γ̇, ḣ)中, 边框中心(u, v),宽高比 γ,高度 h 和和它们各自在图像坐标系中的速度。
这里依旧使用的是匀速运动模型,并把(u,v,γ,h)作为对象状态的直接观测量。
在目标跟踪中,需要估计目标的以下两个状态:
• 均值 (Mean) :包含目标的位置和速度信息,由 8 维向量( u, v, γ, h, ẋ, ẏ, γ̇, ḣ )表示,其中每个速度值初始化为 0 。均值 Mean 可以通过观测矩阵 H 投影到测量空间输出( u , v , γ , h )。• 协方差 (Covariance) :表示估计状态的不确定性,由 8x8 的对角矩阵表示,矩阵中数字越大则表明不确定性越大。4.1 卡尔曼滤波器——预测阶段
•step1 :首先利用上一时刻 k-1 的后验估计值 ,通过 状态转移矩阵 F 变换,得到 当前时刻 k 的先验估计状态
其中,状态转移矩阵 F如下:
•step2:然后使用上一时刻 k-1 的后验估计协方差来计算当前时刻 k 的先验估计协方差
通过上一时刻的后验估计均值和方差 估计 当前时刻的先验估计均值x和协方差P
实现代码如下:
def predict(self, mean, covariance): # mean, covariance 相当于上一时刻的后验估计均值和协方差 std_pos = [ self._std_weight_position * mean[3], self._std_weight_position * mean[3], 1e-2, self._std_weight_position * mean[3]] std_vel = [ self._std_weight_velocity * mean[3], self._std_weight_velocity * mean[3], 1e-5, self._std_weight_velocity * mean[3]] # 初始化噪声矩阵 Q motion_cov = np.diag(np.square(np.r_[std_pos, std_vel])) # x' = Fx mean = np.dot(self._motion_mat, mean) # P' = FPF^T + Q covariance = np.linalg.multi_dot(( self._motion_mat, covariance, self._motion_mat.T)) + motion_cov # 返回当前时刻的先验估计均值 x 和协方差 P return mean, covariance
4.2 卡尔曼滤波器——更新阶段
- step1:首先利用先验估计协方差矩阵 P 和观测矩阵 H 以及测量状态协方差矩阵 R 计算出卡尔曼增益矩阵 K
- step2:然后将卡尔曼滤波器的先验估计值 x 通过观测矩阵 H 投影到测量空间,并计算出与测量值 z 的残差 y
- step3:将卡尔曼滤波器的预测值和测量值按照卡尔曼增益的比例相融合,得到后验估计值 x
- step4:计算出卡尔曼滤波器的后验估计协方差
卡尔曼滤波器更新阶段,代码实现:
def update(self, mean, covariance, measurement): # 将先验估计的均值和协方差映射到检测空间,得到 Hx' 和 HP' projected_mean, projected_cov = self.project(mean, covariance) chol_factor, lower = scipy.linalg.cho_factor( projected_cov, lower=True, check_finite=False) # 计算卡尔曼增益 K kalman_gain = scipy.linalg.cho_solve( (chol_factor, lower), np.dot(covariance, self._update_mat.T).T, check_finite=False).T # y = z - Hx' innovation = measurement - projected_mean # x = x' + Ky new_mean = mean + np.dot(innovation, kalman_gain.T) # P = (I - KH)P' new_covariance = covariance - np.linalg.multi_dot(( kalman_gain, projected_cov, kalman_gain.T)) # 返回当前时刻的后验估计均值 x 和协方差 P return new_mean, new_covariance
总结一下:
在目标跟踪中,需要估计目标的以下两个状态:
• 均值 (Mean) :包含目标的位置和速度信息,由 8 维向量( u, v, γ, h, ẋ, ẏ, γ̇, ḣ )表示,其中每个速度值初始化为 0 。均值 Mean 可以通过观测矩阵 H 投影到测量空间输出( u , v , γ , h )。• 协方差 (Covariance) :表示估计状态的不确定性,由 8x8 的对角矩阵表示,矩阵中数字越大则表明不确定性越大。predict 阶段和 update 阶段都是为了计算出卡尔曼滤波的估计均值 x 和协方差 P,不同的是前者是基于上一历史状态做出的先验估计,而后者则是融合了测量值信息并作出校正的后验估计。
五、匈牙利匹配——跟踪场景定义
解决卡尔曼滤波器的预测状态和测量状态之间的关联,可以通过构建匈牙利匹配来实现。
两个状态关联
- 卡尔曼滤波器的预测状态,后验的结果
- 测量状态,检测器的结果。
两个指标来实现关联
- 运动信息(用“马氏距离” 来计算)
- 外观特征(用“余弦距离” 来计算)
最后,使用线性加权和,将两个指标结合起来。
5.1、匈牙利匹配——马氏距离 关联 运动状态
马氏距离又称为协方差距离,是一种有效计算两个未知样本集相似度的方法,度量预测和检测的匹配程度。为了整合物体的运动信息,使用了预测状态和测量状态之间的(平方)马氏距离:
其中,d 和 y 分别代表测量分布和预测分布;S 为两个分布之间的协方差矩阵,它是一个实对称正定矩阵,可以使用 Cholesky 分解来求解马氏距离。
作用:马氏距离通过计算“预测框”距离“检测框”有多远的偏差,来估计跟踪器状态的不确定性。
这是一个指示器,比较的是马氏距离和卡方分布的阈值,9.4877,如果马氏距离小于该阈值,代表成功匹配。
注意:由于测量分布的维度(4 维)和预测分布的维度(8 维)不一致,因此需要先将预测分布通过观测矩阵 H 投影到测量空间中(这一步其实就是从 8 个估计状态变量中取出前 4 个测量状态变量。),代码实现:
# Project state distribution to measurement space. def project(self, mean, covariance): std = [ self._std_weight_position * mean[3], self._std_weight_position * mean[3], 1e-1, self._std_weight_position * mean[3]] # 初始化测量状态的协方差矩阵 R innovation_cov = np.diag(np.square(std)) # 使用的是对角矩阵,不同维度之间没有关联 # 将均值向量映射到检测空间 得到 Hx mean = np.dot(self._update_mat, mean) # 将协方差矩阵映射到检测空间,得到 HP'H^T covariance = np.linalg.multi_dot(( self._update_mat, covariance, self._update_mat.T)) return mean, covariance + innovation_cov # 加上测量噪声
计算马氏距离,代码实现:
# Compute gating distance between state distribution and measurements. def gating_distance(self, mean, covariance, measurements, only_position=False): # 首先需要先将预测状态分布的均值和协方差投影到测量空间 mean, covariance = self.project(mean, covariance) # 假如仅考虑中心位置 if only_position: mean, covariance = mean[:2], covariance[:2, :2] measurements = measurements[:, :2] # 对协方差矩阵进行 cholesky 分解 cholesky_factor = np.linalg.cholesky(covariance) # 计算两个分布之间对差值 d = measurements - mean # 通过三角求解计算出马氏距离 z = scipy.linalg.solve_triangular( cholesky_factor, d.T, lower=True, check_finite=False, overwrite_b=True) # 返回平方马氏距离 squared_maha = np.sum(z * z, axis=0) return squared_maha
5.2、匈牙利匹配——余弦距离 关联 外观特征
背景:
当物体运动状态的不确定性比较低时,使用马氏距离确实是一个不错的选择。由于卡尔曼滤波器使用的是匀速运动模型,它只能对物体的运动位置提供一个相对粗略的线性估计。当物体突然加速或减速时,跟踪器的预测框和检测框之间的距离就会变得比较远,这时仅使用马氏距离就会变得非常不准确。
设计:
DeepSort 还对每个目标设计了一个深度外观特征描述符,它其实是一个在行人重识别数据集上离线训练的 ReID 网络提取到的 128 维单位特征向量(模长为 1 )。对于每个追踪器 tracker,保留它最后 100 个与检测框关联成功的外观特征描述符集合 R 并计算出它们和检测框的最小余弦距离:
然后,可以设置一个合适的阈值来排除那些外观特征相差特别大的匹配。
ReID 网络:
Deep Sort 采用了经过大规模人员重新识别数据集训练的 Cosine 深度特征网络,该数据集包含 1,261 位行人的 1,100,000 多张图像,使其非常适合在人员跟踪环境中进行深度度量学习。
Cosine 深度特征网络使用了宽残差网络,该网络具有 2 个卷积层和 6 个残差块,L2 归一化层能够计算不同行人间的相似性,以与余弦外观度量兼容。通过计算行人间的余弦距离,余弦距离越小,两行人图像越相似。Cosine 深度特征网络结构如下图所示。
使用 Cosine 深度特征网络参数,将每个检测框
内图片压缩为最能表征图片特异信息的128维向量,并归一化后得到外观描述向量。
这里轨迹使用外观特征向量库保存近期匹配成功的外观特征向量,并在匹配时去余弦距离中最小距离可以适应短时遮挡情况。比如:当前被遮挡,则使用以往外观信息判别,只要进30帧中有出现过较好的匹配,则匹配上该轨迹和检测,较好地减少了遮挡问题。
余弦距离关联外观特征 具体展开:
使用余弦距离来度量表观特征之间的距离,reid模型抽出得到一个128维的向量,使用余弦距离来进行比对:
是余弦相似度,而余弦距离=1-余弦相似度,通过余弦距离来度量卡尔曼预测的表观特征和检测器对应的表观特征,来更加准确地预测ID。SORT中仅仅用运动信息进行匹配会导致ID Switch比较严重,引入外观模型+级联匹配可以缓解这个问题。
同上,余弦距离这部分也使用了一个指示器,如果余弦距离小于,则认为匹配上。这个阈值在代码中被设置为0.2(由参数max_dist控制),这个属于超参数,在人脸识别中一般设置为0.6。
5.3、匈牙利匹配——相互补充
两个指标可以互相补充从而解决关联匹配的不同问题:
一方面,马氏距离基于运动可以提供有关可能的物体位置的信息,这对于短期预测特别有用;
另一方面,当运动的判别力较弱时,余弦距离会考虑外观信息,这对于长时间遮挡后恢复身份特别有用。
为了建立关联问题,我们使用加权和将两个指标结合起来:
可以通过超参数 λ 来控制每个指标对合并成本的影响。在论文的实验过程中,发现当摄像机运动较大时,将 λ=0 是合理的选择(此时仅用到了外观信息)。
六、级联匹配——跟踪场景
当物体被长时间遮挡时,卡尔曼滤波器不能对目标状态准确预测。因此,概率质量在状态空间中散布,并且观察似然性的峰值降低。但是,当两个轨迹竞争同一检测结果时,马氏距离会带来较大的不确定性。因为它有效地减少了任何检测的标准偏差与投影轨迹均值之间的距离,而且可能导致轨迹碎片化增加和不稳定的轨迹。因此,Deep Sort引入了级联匹配,来更加频繁出现的目标赋予优先权,具体算法伪代码如下:
在级联匹配的花费矩阵里,元素值为马氏距离和余弦距离的加权和。实现代码:
def matching_cascade( distance_metric, max_distance, cascade_depth, tracks, detections, track_indices=None, detection_indices=None): if track_indices is None: track_indices = list(range(len(tracks))) if detection_indices is None: detection_indices = list(range(len(detections))) unmatched_detections = detection_indices matches = [] # 遍历不同年龄 for level in range(cascade_depth): if len(unmatched_detections) == 0: # No detections left break # 挑选出对应年龄的跟踪器 track_indices_l = [ k for k in track_indices if tracks[k].time_since_update == 1 + level ] if len(track_indices_l) == 0: # Nothing to match at this level continue # 将跟踪器和尚未匹配的检测框进行匹配 matches_l, _, unmatched_detections = \ min_cost_matching( distance_metric, max_distance, tracks, detections, track_indices_l, unmatched_detections) matches += matches_l # 挑选出未匹配的跟踪器 unmatched_tracks = list(set(track_indices) - set(k for k, _ in matches)) return matches, unmatched_tracks, unmatched_detections
该匹配的精髓在于:挑选出所有 confirmed tracks,优先让那些年龄较小的 tracks 和未匹配的检测框相匹配,然后才轮得上那些年龄较大的 tracks 。
这就使得在相同的外观特征和马氏距离的情况下,年龄较小的跟踪器更容易匹配上。至于年龄 age 的定义,跟踪器每次 predict 时则 age + 1。
七、IOU匹配——跟踪场景
这个阶段是发生在级联匹配之后,匹配的跟踪器对象为那些 unconfirmed tracks 以及上一轮级联匹配失败中 age 为 1 的 tracks. 这有助于解决因上一帧部分遮挡而引起的突然出现的外观变化,从而减少被遗漏的概率。
# 从所有的跟踪器里挑选出 unconfirmed tracks unconfirmed_tracks = [ i for i, t in enumerate(self.tracks) if not t.is_confirmed()] # 从上一轮级联匹配失败的跟踪器中挑选出连续 1 帧没有匹配上(相当于age=1) # 的跟踪器,并和 unconfirmed_tracks 相加 iou_track_candidates = unconfirmed_tracks + [ k for k in unmatched_tracks_a if self.tracks[k].time_since_update == 1] # 将它们与剩下没匹配上的 detections 进行 IOU 匹配 matches_b, unmatched_tracks_b, unmatched_detections = \ linear_assignment.min_cost_matching( iou_matching.iou_cost, self.max_iou_distance, self.tracks, detections, iou_track_candidates, unmatched_detections)
八、实验结果
- 选用MOTA、MOTP、MT、ML、FN、ID swiches、FM等指标进行评估模型。
- 相比SORT, Deep SORT的ID Switch指标下降了45%,达到了当时的SOTA。经过实验,发现Deep SORT的MOTA、MOTP、MT、ML、FN指标对于之前都有提升。
- FP很多,主要是由于Detection和Max age过大导致的。
- 速度达到了20Hz,其中一半时间都花费在表观特征提取。
九、Deep Sort开源代码
基于YOLO的Deep-Sort算法,网络上有不同版本的开源代码,下面三个分别是基于YOLOv5、YOLOv4、YOLOv3 实现的Deep Sort 多目标跟踪系统。
【1】YOLOv5+Pytorch+Deep Sort
开源代码地址:Yolov5_DeepSort_Pytorch
YOLOv5生成的检测结果是一系列在 COCO 数据集上预训练的对象检测架构和模型,被传递到Deep Sort多目标跟踪对象算法。
开发环境:
matplotlib>=3.2.2 numpy>=1.18.5 opencv-python>=4.1.2 Pillow PyYAML>=5.3.1 scipy>=1.4.1 torch>=1.7.0 torchvision>=0.8.1 tqdm>=4.41.0 seaborn>=0.11.0 pandas easydict
模型效果:
【2】YOLOv4+TF+Deep Sort
开源代码地址:Deep-SORT-YOLOv4-tf
开发环境:
# $ conda create --name <env> --file <this file> # platform: linux-64 imutils=0.5.3=pypi_0 keras=2.3.1=pypi_0 matplotlib=3.2.1=pypi_0 numpy=1.18.4=pypi_0 opencv-python=4.2.0.34=pypi_0 pillow=7.1.2=pypi_0 python=3.6.10=h7579374_2 scikit-learn=0.23.1=pypi_0 scipy=1.4.1=pypi_0 tensorboard=2.2.1=pypi_0 tensorflow=2.0.0=pypi_0 tensorflow-estimator=2.1.0=pypi_0 tensorflow-gpu=2.2.0=pypi_0
模型效果:
【3】YOLOv3+Pytorch+Deep Sort
开源代码地址:Deep-Sort_pytorch-yolov3
开发环境:
python 3 (python2 not sure) numpy scipy opencv-python sklearn torch >= 0.4 torchvision >= 0.1 pillow vizer edict
模型效果:
本文参考
[1] 包俊,董亚超,刘宏哲.基于Deep_Sort的目标跟踪算法综述.北京联合大学北京市信息服务工程重点实验室.
[2] 朱镇坤,戴德云,王纪凯,陈宗海.基于FasterR_CNN的多目标跟踪算法设计.中国科学技术大学自动化系.
[3] 谢佳形.拥挤场景下视频多目标跟踪算法研究.浙江大学.
[4] 谷燕飞.基于改进YOLO_V3+Deepsort多目标跟踪系统的研究与实现.辽宁大学.
网络文章参考:
参考1:https://zhuanlan.zhihu.com/p/97449724
参考2:https://yunyang1994.gitee.io/2021/08/27/DeepSort-%E5%A4%9A%E7%9B%AE%E6%A0%87%E8%B7%9F%E8%B8%AA%E7%AE%97%E6%B3%95-SORT-%E7%9A%84%E8%BF%9B%E9%98%B6%E7%89%88/
参考3:https://mp.weixin.qq.com/s?__biz=MzA4MjY4NTk0NQ==&mid=2247485748&idx=1&sn=eb0344e1fd47e627e3349e1b0c1b8ada&chksm=9f80b3a2a8f73ab4dd043a6947e66d0f95b2b913cdfcc620cfa5b995958efe1bb1ba23e60100&scene=126&sessionid=1587264986&key=1392818bdbc0aa1829bb274560d74860b77843df4c0179a2cede3a831ed1c279c4603661ecb8b761c481eecb80e5232d46768e615d1e6c664b4b3ff741a8492de87f9fab89805974de8b13329daee020&ascene=1&uin=NTA4OTc5NTky&devicetype=Windows+10+x64&version=62090069&lang=zh_CN&exportkey=AeR8oQO0h9Dr%2FAVfL6g0VGE%3D&pass_ticket=R0d5J%2BVWKbvqy93YqUC%2BtoKE9cFI22uY90G3JYLOU0LtrcYM2WzBJL2OxnAh0vLo
参考4:https://zhuanlan.zhihu.com/p/80764724
论文地址:https://arxiv.org/abs/1703.07402
开源地址:https://github.com/nwojke/deep_sort
本文只供大家参考学习,谢谢。如有错误,欢迎指出~
-
C++ Sort函数详解
2022-06-06 22:12:06C++sort函数介绍、案例,自定义排序方法。C++ Sort函数详解
前言 :sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使用
stable_sort
函数,这里不过多介绍。一、sort函数调用的两种方式
方式一(默认) void sort (RandomAccessIterator first, RandomAccessIterator last); 方式二(自定义) void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp); -
默认: 两个参数
first
,last
,将[first, last)
区间内元素升序排列。【注意区间为左闭右开】 -
自定义排序: 需用户指定排序规则
Compare comp
,将[first, last)
区间内的元素按照用户指定的顺序排列。
二、sort函数使用场景
由于在排序过程中涉及到元素交换等操作,所以sort函数仅支持可随机访问的容器,如数组, string、vector、deque等。
三、sort函数排序原理
sort()
并非只是普通的快速排序,除了对普通的快速排序进行优化,它还结合了插入排序和堆排序。根据不同的数量级别以及不同情况,能自动选用合适的排序方法。当数据量较大时采用快速排序,分段递归。一旦分段后的数据量小于某个阀值,为避免递归调用带来过大的额外负荷,便会改用插入排序。而如果递归层次过深,有出现最坏情况的倾向,还会改用堆排序。 所以无论元素初始时为何种状态,
sort()
的平均排序复杂度为均为O(N*log2(N)) ,具有不错的的性能,在刷算法题时,可以直接使用sort()来对数据进行排序,而不需手动编写排序函数。四、sort函数使用案例
1.升序排列
sort函数如果不传入第三个参数,则默认是升序排列。
#include<iostream> #include<vector> #include<algorithm> using namespace std; int main() { // 方式一、使用数组 int a[10] = {9, 6, 3, 8, 5, 2, 7, 4, 1, 0}; sort(a, a + 10); // 10为元素个数 for (int i = 0; i < 10; i++) cout << a[i] << ' '; // 输出排序后数组 cout << endl; // 方式二、使用 vector vector<int> arr = {9, 6, 3, 8, 5, 2, 7, 4, 1, 0}; sort(arr.begin(), arr.end()); // 10为元素个数 for (int i = 0; i < 10; i++) cout << arr[i] << ' '; // 输出排序后数组 return 0; }
2.降序排列
- 实现方式1
实现降序排列,需传入第三个参数–比较函数,
greater<type>()
,这里的元素为int
类型,即函数为greater<int>()
; 如果是其他基本数据类型如float
、double
、long
等也是同理。#include<iostream> #include<vector> #include<algorithm> using namespace std; int main() { // 方式一、使用数组 int a[10] = {9, 6, 3, 8, 5, 2, 7, 4, 1, 0}; sort(a, a + 10, greater<int>()); // 10为元素个数 for (int i = 0; i < 10; i++) cout << a[i] << ' '; // 输出排序后数组 cout << endl; // 输出 9 8 7 6 5 4 3 2 1 0 // 方式二、使用 vector vector<int> arr = {9, 6, 3, 8, 5, 2, 7, 4, 1, 0}; sort(arr.begin(), arr.end(), greater<int>()); for (int i = 0; i < 10; i++) cout << arr[i] << ' '; // 输出排序后数组 return 0; }
- 实现方式2
我们也可以使用自定义的比较函数,函数的返回值为
bool
类型, 例如bool cmp(int num1, int num2) { return num1 > num2; // 可以简单理解为 > 降序排列; < 升序排列 }
#include<iostream> #include<vector> #include<algorithm> using namespace std; bool cmp(int num1, int num2) { return num1 > num2; // 可以简单理解为 >: 降序排列; < : 升序排列 } int main() { // 一、使用数组 int a[10] = {9, 6, 3, 8, 5, 2, 7, 4, 1, 0}; sort(a, a + 10, cmp); // 使用自定义排序函数 for (int i = 0; i < 10; i++) cout << a[i] << ' '; // 输出排序后数组 cout << endl; // 输出 9 8 7 6 5 4 3 2 1 0 // 二、使用 vector vector<int> arr = {9, 6, 3, 8, 5, 2, 7, 4, 1, 0}; sort(arr.begin(), arr.end(), cmp); // 使用自定义排序函数 for (int i = 0; i < 10; i++) cout << arr[i] << ' '; // 输出排序后数组 return 0; }
3.结构体排序(自定义比较函数)
要对元素进行排序,前提是元素之间可以进行比较,即谁大谁小。 基本数据类型可直接进行大小比较, 但结构体元素之间的大小关系需要我们自己指定,如果不指定,则结构体之间大小关系就不确定,则不能够排序。
结构体排序案例1: 对学生信息进行排序
学生有
姓名
,分数
两个属性,struct Student { // 学生结构体 string name; // 学生姓名 int grade; // 学生分数 Student(); // 无参数构造函数 Student(string name, int grade) : name(name), grade(grade) {}; // 有参数构造函数 };
需求: 对一个班级内的学生成绩进行排序,首先按成绩进行排序降序排列,若成绩相同,则按照姓名字典顺序升序排列。
- 自定义排序函数
bool cmp(Student s1, Student s2) { // 自定义排序 if (s1.grade != s2.grade) { // 如果学生成绩不相同 return s1.grade > s2.grade; // 则按照成绩降序排列 } return s1.name < s2.name; // 否则按照姓名升序排列 }
- 排序代码
#include<iostream> #include<vector> #include<algorithm> using namespace std; struct Student { // 学生结构体 string name; // 学生姓名 int grade; // 学生分数 Student(); // 无参数构造函数 Student(string name, int grade) : name(name), grade(grade) {}; // 有参数构造函数 }; bool cmp(Student s1, Student s2) { // 自定义排序 if (s1.grade != s2.grade) { // 如果学生成绩不同 return s1.grade > s2.grade; // 则按照成绩降序排列 } return s1.name < s2.name; // 否则按照姓名升序排列 } int main() { vector<Student> studs; studs.emplace_back("Bob", 80); studs.emplace_back("Ali", 90); studs.emplace_back("Ann", 85); studs.emplace_back("Liming", 90); studs.emplace_back("Trump", 79); studs.emplace_back("Fury", 58); studs.emplace_back("Jam", 62); studs.emplace_back("Lucy", 89); sort(studs.begin(), studs.end(), cmp); // 排序 for (int i = 0; i < studs.size(); i++) { // 输出结果 cout << studs[i].name << "\t" << studs[i].grade << endl; } return 0; }
五、自定义comp函数返回true或false作用
bool cmp(int num1, int num2) { // 实现降序排列 return num1 > num2; // num1大于num2时返回true,否则返回false }
自定义函数返回值为
bool
类型- 若返回
true
,则表示num1
与num2
应该交换顺序; - 若返回
false
, 则num1
与num2
保持原有顺序;
下面举例说明自定义比较函数的执行过程。
对 2, 5, 1, 3, 4 降序排列 调用cmp函数时,将5赋值给num1, 2赋值给num2 (注意顺序) 5 > 2, 返回true,num1 与 num2需进行交换;即5应该在2的前面 数组变为 5, 2, 1, 3, 4 第二次 将3赋值给num1, 1赋值给num2, 3 > 1, 返回true,num1 与 num2需进行交换;即3应该在1的前面 数组变为 5, 2, 3, 1, 4 之后经过数次的比较与交换最终排序完成。 最终得到 5 4 3 2 1
六、参考文章链接
https://www.cplusplus.com/reference/algorithm/sort/
https://blog.csdn.net/qq_41575507/article/details/105936466
胡凡算法笔记
-
-
【Deep-sort多目标跟踪流程及其改进方法的解读】
2022-03-18 14:42:33【Deep-sort多目标跟踪流程及其改进方法的解读】文前白话相关的文章、资源链接流程及其改进方法的梳理一、多目标跟踪的流程二、Sort 与 deepSort 的对比三、DeepSort主要的跟踪流程四、关于DeepSort中部分模块原理的... -
SORT、DeepSORT
2020-05-08 23:22:593.8. SORT/deepSORT 学习目标: 理解SORT算法的原理 理解DeepSORT算法的原理 上一节给大家介绍了一下多目标跟踪MOT的一些基础知识。SORT和DeepSORT是多目标跟踪中两个知名度比较高的算法。DeepSORT是原团队对SORT... -
deepsort和MOT16指标评价
2022-04-13 15:02:09如何评价deepsort跟踪性能,常用的有MOT challenge提供的数据集,根据训练数据集的ground truth文件,对比deepsort跟踪窗口位置和跟踪ID,可以得到一系列评价指标。网上有很多有关MOT评价指标介绍和算法,近来找到一... -
sort函数
2019-09-13 23:20:353、sort函数有三个参数: (1)要排序数组的起始地址 (2)要排序数组的最后一个数据元素的下一个地址 (3)排序方法,如果没有排序方法的话,默认从小到大排序 二、实例 1、sort函数实现数的排序 #... -
deepsort训练车辆特征参数
2021-10-02 23:24:13deepsort用来跟踪被检测对象。网上常见的yolov5+deepsort,是pytorch版。此版本用ZQPei Github: https://github.com/ZQPei/deep_sort_pytorch#training-the-re-id-model ZQPei提供的ReID:deep模型文件ckpt.t7为行人... -
DeepSort 总结
2021-01-07 20:00:49DeepSort与Sort 先把两者区别写出来: Sort 算法利用卡尔曼滤波算法预测检测框在下一帧的状态,将该状态与下一帧的检测结果进行匹配,实现车辆的追踪。 DeepSort中采用了一个简单的CNN来提取被检测物体(检测框物体... -
Jetson 系列——基于yolov5和deepsort的多目标头部识别,跟踪,使用tensorrt和c++加速
2021-09-07 10:04:12使用yolov5+deepsort实现室内人头部追踪和计数,使用c++实现,并用tensorrt加速,在物体有70左右的情况下,在Jetson Xavier nx上整个项目的推理时间在130ms左右,即7FPS的速度。你可以体验一下python版本的yolov5+... -
C# List.Sort排序
2022-03-08 08:48:36C#中,List.Sort() 不仅为我们提供了默认的排序方法,还为我们提供了4种自定义排序的方法,通过默认排序方法,我们无需重写任何Sort()方法的实现代码,就能对单参数类型的List数据进行单一规则的排序,如果通过对... -
sort命令
2021-05-17 22:17:291.sort命令sort命令可以给文本进行排序。sort命令操作的是行。shell中sort命令有3中执行模式,分别是排序文本,检查文件是否已经排序,合并文件。2.sort的使用语法:sort [选项] ... [文件] ...选项:-b:忽略前面... -
近期关于Sort和DeepSort改进的工作
2022-03-29 16:57:31本文总结近期三篇对Sort和DeepSort改进的工作,Sort和DeepSort以及JDE的推理流程可以参考之前的文章:Sort和Deepsort原理解析及在JDE和Fairmot中的应用 一、ByteTrack: Multi-Object Tracking by Associating Every ... -
DeepSORT的改进
2021-12-26 21:52:40很多人解说DeepSORT时都是按照论文的思路说,陷入了细节,还贴上公式,让初次接触的人看完还是感觉很懵,我力求说得简单易懂点。 DeepSORT相对于SORT增加了个抽取特征数据的深度学习模型,这个模型可以是目标检测... -
更新版yolov5_deepsort_pytorch实现目标检测和跟踪
2022-03-28 20:36:01由于mikel-brostrom在github上发布的Yolov5_DeepSort_Pytorch更新,使整个代码封装性更好,进而允许采用多种REID特征识别模型,完善了deepsort在检测跟踪方面的性能。本博文记录如何使用此版本Yolov5_DeepSort_... -
go sort使用
2020-09-12 13:04:531、sort介绍 sort包提供了排序切片和用户自定义数据集以及相关功能的函数。 sort包主要针对[]int、[]float64、[]string、以及其他自定义切片的排序。 sort 包 在内部实现了四种基本的排序算法:插入排序(insertion... -
论文阅读:Observation-Centric SORT: Rethinking SORT for Robust Multi-Object Tracking——OC-SORT
2022-04-23 08:38:58文章提出了一种用于多目标跟踪的算法Obeservation-Centric SORT(OC-SORT),以解决多目标跟踪中模型对目标重叠、非线性运动的敏感和需要高帧率视频的问题。 -
深入浅出——零基础一文读懂DeepSORT(原理篇)
2021-12-15 14:37:18本文是笔者对DeepSORT算法学习的阶段性总结,基于笔者接触到的所有开源学习资料,辅以个人理解进行重新编排而成,力求清晰,使非专业的读者也能迅速对该算法原理有较为透彻的理解,便于后续代码学习。 笔者本人为非... -
记一次调试YOLOv5+DeepSort车辆跟踪项目的经过
2022-05-06 09:20:25摘要:学习别人的开源项目是日常的一项必备技能,本文通过一个车辆跟踪(YOLOv5+DeepSort)的例子介绍如何配置和调试GitHub上的开源代码。以第一人称的视角给出本人调试代码的过程,包括项目readme的阅读、python... -
YOLOv5+DeepSORT多目标跟踪与计数精讲
2021-05-10 23:39:05本课程使用YOLOv5和DeepSORT对视频中的行人、车辆做多目标跟踪和计数,开展YOLOv5目标检测和DeepSORT多目标跟踪强强联手的应用。 课程分别在Windows和Ubuntu系统上做项目演示,并对DeepSORT原理和代码做详细... -
关于YOLOv5_deepsort训练自己的数据集
2021-12-01 16:51:54关于YOLOv5_deepsort数据集训练自己的数据集——自学使用 参考链接放在这里咯: https://blog.csdn.net/qq_35975447 https://blog.csdn.net/didiaopao 一窍不通,我先看看这个标准数据集的介绍: *Market 1501数据集... -
DeepSORT简单理解
2022-03-06 08:49:15DeepSORT算法通过结合目标框的马氏距离(Mahalanobis distance)和特征余弦距离(Cosine similarity)两个度量来整合运动信息和外观信息。外观信息是指使用了一个简单的CNN网络去提取被检测物体的外观特征。运动信息是... -
pytorch yolo5+Deepsort实现目标检测和跟踪
2021-05-13 20:51:45yolo是一种运行速度很快的目标检测AI模型,目前最新版本是yolo5,最大可处理1280像素的图像。...Deepsort是实现目标跟踪的算法,从sort(simple online and realtime tracking)演变而来,其使用卡尔曼滤波器预测所检 -
21.[开源][安卓][拖拽]drag-sort-listview-master
2015-02-23 21:21:1621.[开源][安卓][拖拽]drag-sort-listview-master DragSortListView(DSLV)是Android ListView的一个扩展,支持拖拽排序和左右滑动删除功能。重写了TouchInterceptor(TI)类来提供更加优美的拖拽动画效果。 DSLV... -
Yolov5 + Deepsort 重新训练自己的数据(保姆级超详细)
2022-03-26 19:53:45从下面github库中拿代码: ...支持5.0版本可训练自己数据集最新版本yolov5+deepsort目标检测和追踪,能够显示目标类别,支 -
详述Java中sort排序函数
2022-06-04 19:08:11手写一个排序算法的效率是很慢的,当然这也不利于我们在比赛或者工程中的实战,如今几乎每个语言的标准库中都有排序算法,今天让我来给大家讲解一下Java语言中的sort排序Collections类中的sort方法可以实现List接口... -
智慧交通day02-车流量检测实现09:SORT/deepSORT
2021-11-15 10:34:06SORT和DeepSORT是多目标跟踪中两个知名度比较高的算法。DeepSORT是原团队对SORT的改进版本。现在来解析一下SORT和DeepSORT的基本思路。 1.SORT SORT核心是卡尔曼滤波和匈牙利匹配两个算法。流程图如下所示,可以... -
【python】 sort、sorted高级排序技巧
2020-12-22 01:16:05这篇文章主要介绍了Python中列表(List)的详解操作方法,包含创建、访问、更新、删除、其它操作等,需要的朋友可以参考下Python list内置sort()方法用来排序,也可以用python内置的全局sorted()方法来对可迭代的序列... -
python sort()排序算法
2020-12-02 15:20:14在刷leetcode一道要求时间复杂度的题目,用了sort排序,发现时间复杂度还可以。#python的排序详解排序,在编程中经常遇到的算法,我也在几篇文章中介绍了一些关于排序的算法。有的高级语言内置了一些排序函数。本文...