【目标跟踪】多相机环视跟踪

news2025/1/20 2:02:31

文章目录

  • 一、前言
  • 二、流程图
  • 三、实现原理
    • 3.1、初始化
    • 3.2、输入
    • 3.3、初始航迹
    • 3.4、航迹预测
    • 3.5、航迹匹配
    • 3.6、输出结果
  • 四、c++ 代码
  • 五、总结

一、前言

  1. 相机目标跟踪主要是为了实现 360 度跟踪。单相机检测存在左右后的盲区视野。
  2. 在智能驾驶领域,要想靠相机实现无人驾驶,相机必须 360 度无死角全覆盖。
  3. 博主提供一种非深度学习方法,采用kalman滤波+匈牙利匹配方式实现环视跟踪。有兴趣可以参考往期【目标跟踪】系列博客。
  4. 本文干货满满,可以先点赞关注收藏,以免下次找不到。欢迎各位吴彦祖私信交流学习。

二、流程图

干货怎么会少了流程图,上图!上图!上图!。流程图用 processon 制作的,不是付费软件买不起,而是免费的更有性价比。

在这里插入图片描述

这里最重要的就是航迹管理,对于四路相机(front, left, right, back) 检测的目标都需要管理。这里面不仅要对单相机目标进行跟踪,且对跨相机的目标也需要进行跟踪,还有各种匹配,初始化,开始消亡等到。想想都头皮发麻,那到底怎么做?别慌,让我娓娓道来。

街上熙熙攘攘的人群交头接耳,果然雨过天晴就会有聊不完的话题。三只白鹭停泊在河边享受着阳光的沐浴,不时煽动自己羽毛,引起阵阵涟漪,如此惬意无不让打工人憧憬。远处的山峰层峦叠嶂,仿佛去到世界尽头。命运的齿轮正在转动,会当凌绝顶,一览纵山下。

wc !!!思绪差点飘离银河系,还是敲键盘要紧。下面看看实现原理。

三、实现原理

3.1、初始化

保存四路相机参数:4路相机内参与外参。

制定表格 1920 * 1080 ,用于后续判断计算校正后的像素点。对整张图片进行校正会占用大量 CPU,我们采用对原图进行计算。

在这里插入图片描述

3.2、输入

nuscenes 是六路相机。我们这里是四路相机,全部都是 190° 鱼眼相机。

输入四路相机检测结果。当前相机当前帧的所有目标信息

  • 二维 box 检测框 (x,y,w,h)
  • 目标列表 label。如person、car等
  • 目标的置信度 score

演示这里采取的是 2D 检测。如果是 3D 检测也是类似计算,且 3D 检测定位更加精准。


3.3、初始航迹

(1) 划分 4 象限

在这里插入图片描述

(2) 划分 8 区域

前相机第4象限、前相机第1象限、右相机第3象限、右相机第4象限、后相机第2象限、后相机第3象限、左相机第1象限、左相机第2象限。

在这里插入图片描述

(3) 划分 2 类

前相机第4象限、右相机第3象限、后相机第2象限、左相机第1象限 为一类 firstBox。

右相机第4象限、后相机第3象限、左相机第2象限、前相机第1象限 为一类 secondBox。

(4) 分配 id

当目标是属于 firstBox 一类的目标时,此时直接分配 id 。分配一个 id ,则 idCount ++。

(5) 匹配

sencondBox 中的目标需要与 firstBox 目标匹配。计算距离与角度。

如果匹配上时,id = firstBox 目标 id

如果未匹配上时,id = idCount , idCount ++

3.4、航迹预测

这部分比较简单, 正常预测就行。

对每个目标的box进行预测; 对每个目标的状态,距离、速度、加速度进行预测; 得到预测后的目标状态、box、相应传感器(枚举)。

3.5、航迹匹配

(1) 预测框与检测框

预测框是上一帧所获得的预测框;检测框是当前检测信息的检测框。同时对目标有预测的距离与测量距离

(2) 匈牙利匹配矩阵

如果属于不同相机检测的目标,则设为默认最大值;如果属于同一相机检测目标,计算iou。这个是与单相机跟踪类似。

(3) 匹配修正

匹配上的目标,修正预测的状态与box。此时idCount 不变。

(4) 未匹配的检测框

对于未匹配的检测框,寻找航迹中在其他相机的目标,进行再次匹配。如果匹配上,则未匹配检测框id = 其他相机目标id。否则赋予id = idCount,idCount++。

3.6、输出结果

输出俯视角下所有目标的位置(x, y)。

四、c++ 代码

这部分代码比较多,展示划分象限初始航迹代码。其他代码放到资源自行下载。

// 1、管理航迹信息
void Tracking::ManageTrack(std::vector<TrackingBox> detectData) 
{
    std::vector<StateBox> firstBox;                 // 目标所处相机与第几象限 front 4, right 3, back 2, left 1 
    std::vector<StateBox> secondBox;                // 目标所处相机与第几象限 right 4, back 3, left 2, front 1 
    // 1.1 分配id
    for (unsigned int i = 0; i < detectData.size(); i++) {
        StateBox stateBox;
        stateBox.sensor = detectData[i].sensor;                                       // 相机传感器的方位
        stateBox.label = detectData[i].label;                                         // 目标标签
        stateBox.score = detectData[i].score;                                         // 目标置信度
        stateBox.box = detectData[i].box;                                             // 检测框
        std::vector<float> p = detectData[i].position;                                // 根据图像像素获取世界位置 x,y相对于车体
        stateBox.position = p;
        stateBox.state = {(cv::Mat_<float>(6, 1) << p[0], 0, 0, p[1], 0, 0), pInit};  // 初始化distanceKalman的状态与协方差
        if ((stateBox.sensor == 0 && p[1] < 0) || (stateBox.sensor == 1 && p[0] < 0) || 
                (stateBox.sensor == 2 && p[1] > 0) || (stateBox.sensor == 3 && p[0] > 0) ) {
            stateBox.kBox = KalmanTracker(detectData[i].box);                         // KalmanTracker所需的box
            stateBox.id = countId;
            countId++;
            firstBox.push_back(stateBox);
        }
        else {
            secondBox.push_back(stateBox);
        }
    }
    for (auto box:firstBox) {
        trackers.push_back(box);
    }
    // 1.2 匹配id
    unsigned int firstNum = firstBox.size();                             // 前相机在右侧个数
	unsigned int secondNum = secondBox.size();                           // 右相机在前侧个数
    std::vector<std::vector<double>> matrix;                             // 关联矩阵->匈牙利匹配
    matrix.resize(firstNum, std::vector<double>(secondNum, 1));          // resize关联矩阵大小
    std::vector<cv::Point> matchedPairs;  
    if (firstNum != 0 && secondNum != 0) {
        for (unsigned int i = 0; i < firstNum; i++) {
            for (unsigned int j = 0; j < secondNum; j++) {
                matrix[i][j] = GetWeight(firstBox[i].position, secondBox[j].position); // 计算角度权重
            }
        }
        HungarianAlgorithm hungAlgo;
        std::vector<int> assignment; 
        hungAlgo.Solve(matrix, assignment);     // 匈牙利匹配计算
        std::set<int> unMatchedSecondBox;       // 存放未匹配的sencond 类型的框
        for (unsigned int i = 0; i < firstNum; ++i) {
            if (assignment[i] == -1) { 
                continue;                       // 过滤掉无效的值
            }
            matchedPairs.push_back(cv::Point(i, assignment[i]));
        }
    }
    // 1.3 修正id
    int firstId, secondId;
    for (unsigned int i = 0; i < matchedPairs.size(); i++) {
        firstId = matchedPairs[i].x; 
        secondId = matchedPairs[i].y; 
        if (matrix[firstId][secondId] < hungarianThreshold) {
            secondBox[secondId].kBox = KalmanTracker(detectData[i].box);
            secondBox[secondId].id = firstBox[firstId].id;
            trackers.push_back(secondBox[secondId]);
        }
    }
    for (auto secBox:secondBox) {
        if (secBox.id == -1) {
            secBox.kBox = KalmanTracker(secBox.box);
            secBox.id = countId;
            countId++;
            trackers.push_back(secBox);
        }
    }
}

五、总结

在计算过程中,特别注意边缘目标处理。由远到近目标航迹管理需要当心判断。还有马氏距离匹配参数、距离权重、kalamn参数需要自行调整。在宽阔场景,跨相机重要目标匹配成功率可以高达 80%

代码仅供参考,禁止商业用途。欢迎私信交流。

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

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

相关文章

工厂生产管理MES系统,开源代码+维护

商业开源的一套超有价值的JAVA制造执行MES系统源码 亲测 带本地部署搭建教程 教你如何在本地运行运行起来。 开发环境&#xff1a;jdk11tomcatmysql8springbootmaven 需要源码&#xff0c;私信我获取&#xff0c;可以项目合作维护一、系统概述&#xff1a; MES制造执行系统&…

2024年最新 MySQL的下载、安装、启动与停止

一、MySQL的下载 MySQL最常用的2个版本&#xff1a; 社区版&#xff1a;免费开源&#xff0c;自由下载&#xff0c;不提供官方技术支持&#xff0c;大多数普通用户选择这个即可。企业版&#xff1a;需要付费&#xff0c;不能在线下载&#xff0c;可以使用30天&#xff0c;提供…

SpringBoot深入解析:掌握自动装配机制及其定制化原理

推荐一款我一直在用的ChatGPT4.0国内站点&#xff0c;每日有免费使用额度&#xff0c;支持PC、APP、VScode插件同步使用 SpringBoot篇&#xff1a;SpringBoot的自动装配原理 SpringBoot是一个旨在简化Spring应用初始搭建以及开发过程的框架。它利用了Spring框架的依赖注入特性…

nodejs学习计划--(七)express框架

express框架 1. express介绍 express 是一个基于 Node.js 平台的极简、灵活的 WEB 应用开发框架&#xff0c;官方网址&#xff1a;https://www.expressjs.com.cn/ 简单来说&#xff0c;express 是一个封装好的工具包&#xff0c;封装了很多功能&#xff0c;便于我们开发 WEB …

【GitHub项目推荐--不错的 React 开源项目】【转载】

用 React Flow 连接你的想法 用 React Flow 连接你的想法&#xff0c;这是一个高度可定制的库&#xff0c;基于 React 用于构建基于节点的 交互式 UI、编辑器、流程图和图表。 开源地址&#xff1a;https://github.com/wbkd/react-flow Bulletproof React 一个简单、可扩展且…

Vue3+Vite使用Puppeteer进行SEO优化(SSR+Meta)

1. 背景 【笑小枫】https://www.xiaoxiaofeng.com上线啦 资源持续整合中&#xff0c;程序员必备网站&#xff0c;快点前往围观吧~ 我的个人博客【笑小枫】又一次版本大升级&#xff0c;虽然知道没有多少访问量&#xff0c;但我还是整天没事瞎折腾。因为一些功能在Halo上不太好实…

C++ 右值引用 std::move和std::forward的使用

前言 右值引用&#xff0c;std::move(移动语义)和std::forward(完美转发)都是C11里面的特性。 使用右值引用和移动语义&#xff0c;可以避免无谓的复制&#xff0c;提供了程序性能。 右值引用 在说明右值引用之前&#xff0c;先说下什么是左值&#xff0c;什么是右值。 左值…

Stable Diffusion与Midjourney:如何做出明智之选?

Stable Diffusion与Midjourney&#xff1a;如何做出明智之选&#xff1f; 在人工智能领域中&#xff0c;Stable Diffusion和Midjourney是两个备受瞩目的技术。它们各自具有独特的特点和优势&#xff0c;但选择哪一个更适合您的需求呢&#xff1f;本文将为您详细分析两者的差异…

Unity中创建Ultraleap 3Di交互项目

首先&#xff0c;创建新的场景 1、创建一个空物体&#xff0c;重命名为【XP Leap Provider Manager】&#xff0c;并在这个空物体上添加【XR Leap Provider Manager】 在物体XP Leap Provider Manager下&#xff0c;创建两个子物体Service Provider(XR)和Service Provider(…

蓝桥杯省赛无忧 课件46 第5次学长带练配套课件

01 聪明的小羊肖恩 02 阿坤老师的独特瓷器 03 妮妮的月饼工厂 04 可凑成的最大花束数 05 四个瓶子的问题 06 如今扔是遥远的理想之城

12.5内存操作流(血干JAVA系列)

12.5内存操作流 12.5内存操作流ByteArraylnputStream类的主要方法ByteArrayOutputStream 类的主要方法【例12.33】使用内存操作流完成一个大写字母转换为小写字母的程序 12.5内存操作流 在 流 的 操 作 中 除 了 进 行 文 件 的 输 入 与 输 出 操 作 之 外 &#xff0c; 也 可…

Modern C++ std::unique_ptr的实现原理

​ unique_ptr是一个非常简单的类,没有计数没有原子操作,非常类似纯指针。 它的类定义也非常简单: 它针对数组做了模板偏特化,因为它得支持数组操作比如Arr[i]。 unique_ptr的本质就是std::tuple, 里面第一项为指针指向管理对象,第二项为deleter:是一个函数指针或仿函数…

什么是网络?

你是一台电脑&#xff0c;你的名字叫 A 很久很久之前&#xff0c;你不与任何其他电脑相连接&#xff0c;孤苦伶仃。 直到有一天&#xff0c;你希望与另一台电脑 B 建立通信&#xff0c;于是你们各开了一个网口&#xff0c;用一根网线连接了起来。 用一根网线连接起来怎么就能&…

探索如何使用Python实现关注微信公众号实现登录的功能(用户认证)

文章目录 📖 介绍 📖🏡 环境 🏡📒 实现方法 📒📝 准备工作📝 源码分享⚓️ 相关链接 ⚓️📖 介绍 📖 本文将与大家分享一下如何使用python实现扫描二维码关注微信公众号,并通过用户认证从而实现登录。本文将主要分享功能实现的完整思路,并包含部分功能实…

使用人工智能助手 Github Copilot 进行编程 02

本章涵盖了 在您的系统上设置 Python、VS Code 和 Copilot引⼊ Copilot 设计流程Copilot 的价值在于基本的数据处理任务本章将帮助您在自己的计算机上开始使用 Copilot,并熟悉与其的交互方式。在设置好Copilot 后,我们将要求您尽可能跟随我们的示例进行操作。实践是最好的学习…

数据结构C++队列(数组模拟)

队列也是比较简单的数据结构了&#xff0c;队列的特点是先进先出 下面代码中hh是队头&#xff0c;tt是队尾。 默认是从队尾插入数据&#xff0c;队头弹出数据。 代码中的数据结构可以使用这图片来解释&#xff0c;整个区间是数组q。hh和tt分别控制队头和队尾。 例题&#x…

cocos添加节点事件的3种方式

我们以button为例来说明一下cocos怎样为节点添加事件&#xff1a; 直接通过cocos熟悉检查器绑定 添加事件脚本 import { _decorator, Component, Node, input, Input, Button, EventKeyboard } from cc; const { ccclass, property } _decorator;ccclass(Attack) export cla…

【笔试常见编程题01】删除公共字符串、组队竞赛、倒置字符串、排序子序列

1. 删除公共字符串 输入两个字符串&#xff0c;从第一字符串中删除第二个字符串中所有的字符。 例如&#xff0c;输入”They are students.”和”aeiou”&#xff0c;则删除之后的第一个字符串变成”Thy r stdnts.” 输入描述 每个测试输入包含2个字符串 输出描述 输出删除后的…

事务:分布式事务与本地事务的区别

分布式事务章节 分布式事务&#xff1a;2PC与3PC的区别-CSDN博客 分布式事务&#xff1a;X/Open DTP分布式事务处理模型与分布式事务处理XA规范-CSDN博客 事务简介 事务(Transaction)是操作数据库中某个数据项的一个程序执行单元(unit)。事务是由一组操作构成的可靠的独立的…

【Linux】Linux中的日志查询方法

文章目录 linux日志与日志的查询方法更多journalctl用法journalctl用法案例部分日志路径说明推荐阅读 linux日志与日志的查询方法 在Linux系统中&#xff0c;日志文件用于记录系统的各种运行信息和错误消息。常见的日志文件包括但不限于/var/log/下的各种日志&#xff0c;如me…