足球视频AI(五)——球员与球的对象跟踪

news2024/11/26 10:32:18

一、基础概念

在这里插入图片描述

在之前的四节中,我们尝试解决:

1,球员识别、足球识别、裁判识别;

2,队伍的分类

3,平面坐标的换算

存在关键的问题是:每一帧的画面,每次都是重新识别,无法将特定的人与坐标对应上。

我们需要知道每个球员、裁判的实时位置,并将实时位置记录对应到关键帧,能够通过世界坐标的变换,得到想要的足球参数。

1.1 识别目标

利用对象跟踪,实现定人的实时追踪。

1.2 实现思路

1)初始帧画面,利用对象检测识别球员、裁判、球,并生成编号便于跟踪。

2)利用CSRT实现多目标追踪,跟踪检测到的对象,确保不丢失。

3)设计简单的刷新算法,确保视频范围内人员进出画面进行新的目标检测。

4)探讨当跟踪丢失(如对象离开画面、人员重叠后的跟踪丢失)

二、代码实现

2.1 对象检测

参见《足球视频AI(二)——球员与球的目标检测》

2.2 CSRT目标追踪技术

2.2.1 依赖包

Nuget Install OpenCvSharp4
Nuget Install OpenCvSharp4.Extensions
Nuget Install OpenCvSharp4.runtime.win
Nuget Install Numpy.Bare

其中OpenCvSharp包中,提供了类TrackerCSRT的实现。

CSRT概念性的内容需要大家翻阅数据,在此不再详述,本系列主要是实操内容。

2.2.2 跟踪的数据结构

    internal class TrackerObject
    {
        public TrackerObject(Tracker tracker, YoloPrediction prediction) 
        {
            Tracker = tracker;
            Prediction = prediction;
        }

        public Tracker Tracker{ get;set;}

        public Rect Rect { get;set;}

        public YoloPrediction Prediction { get; set; }

    }

该数据结构,将首次对象检测到的球员Bound,存储在YoloPrediction中。

因采用了多跟踪器的实现,所以每个球员、裁判、球对应一个Tracker对象。

2.2.3 接口定义

    public interface ITracker<T> : IDisposable
    {
        bool NeedFlush { get;}

        public int MaxCount { get; set; }

        void Init(List<T> detections, Mat frame);

        List<T>? Update(Mat frame);
    }

1, 对于人的跟踪,我们设计的简单的算法,假设视频首帧中检测到14人,其它球员在视频的视界范围外。存在以下情况:

​ 1),新的人员跑进视界;

​ 2),既有人员从视界中跑出去;

​ 3),跟踪器丢失了跟踪。

​ 算法中每格60帧重新对象检测,通过人员总数的变化率大于30%,则重设跟踪(已有跟踪没有进行鉴别)。

​ 当触发了30%阈值情况,则NeedFlush标志为True;MaxCount用来定义视界中的人员总数。

2, 对于球的跟踪,仅适用于跟踪丢失的情况。

2.2.4 跟踪器设计

    internal class MutiTrackerCV : ITracker<YoloPrediction>
    {
        private List<TrackerObject> trackers;
        private int MissCount=0;

        /// <summary>
        /// <inheritdoc/>
        /// </summary>
        public int MaxCount { get; set; }

        /// <summary>
        /// <inheritdoc/>
        /// </summary>
        public bool NeedFlush
        {
            get => MaxCount == 0 ? true : ((float)MissCount / MaxCount > 0.3);
        } 

        public MutiTrackerCV()
        {
            trackers = new List<TrackerObject>();
        }

        public void Dispose()
        {
            for (int i = 0; i < trackers?.Count; i++)
            {
                TrackerObject? tracker = trackers[i];
                tracker.Tracker?.Dispose();
                tracker = null;
            }
        }

        /// <summary>
        /// <inheritdoc/>
        /// </summary>
        public void Init(List<YoloPrediction> detections, Mat frame)
        {
            trackers = new List<TrackerObject>();
            int i = 0;
            foreach (var d in detections)
            {
                i++;
                var tracker = TrackerCSRT.Create();
                var rect = new Rect((int)d.Rectangle.X, (int)d.Rectangle.Y, (int)d.Rectangle.Width, (int)d.Rectangle.Height);
                tracker.Init(frame, rect);
                if (null != d.Label)
                    d.Label = new YoloLabel() { Color = d.Label.Color, Kind = d.Label.Kind, Name = d.Label.Name, Id = i};
                var trackerObject = new TrackerObject(tracker, d) { Rect = rect};
                trackers?.Add(trackerObject);
            }
            MaxCount = trackers.Count;
        }

        /// <summary>
        /// <inheritdoc/>
        /// </summary>
        public List<YoloPrediction>? Update(Mat frame)
        {
            var missObjects = new List<TrackerObject>();
            foreach (var tracker in trackers)
            {
                var rect = (Rect)tracker.Rect;
                var ismiss = tracker.Tracker?.Update(frame, ref rect);
                if (ismiss == null || ismiss == false)
                    missObjects.Add(tracker);
                else
                    tracker.Prediction.Rectangle = new System.Drawing.RectangleF(rect.X, rect.Y, rect.Width, rect.Height);
            }
            foreach (var tracker in missObjects)
                trackers.Remove(tracker);
            MissCount = missObjects.Count;
            return trackers?.Select(p => p.Prediction).ToList();
        }
    }

其中,Tracker?.Update函数返回的是单个跟踪对象是否丢失的标志位。

当为false时说明跟踪对象丢失——例如白色的足球,滚动仅纯白色区域后。再出现时跟踪会丢失。

2.2.5 目标跟踪实现

[Fact]
        public void TestPlayerTracker()
        {
            var detector = new DetectorYolov7();
            var mutiTracker = new MutiTrackerCV();
            var mats = LoadImages.LoadVideo("test.mp4");
            int frameNumber = 0;
            //逐帧处理
            foreach (var mat in mats)
            {
                if ((frameNumber % 60) == 0)
                {
                    //目标检测
                    var predictions = detector.Detect(mat);
                    var playerNumber = 1;
                    //绘制对象Bound
                    predictions.ForEach(item=>
                    {
                        //略...区分人、球
                        //略...原始视频CV绘制球员Bound
                        //原始视频CV绘制球员号码
                        Cv2.PutText(mat, $"{item.Label?.Id}", new OpenCvSharp.Point(item.Rectangle.X, item.Rectangle.Y),
                                   HersheyFonts.HersheySimplex, 0.5, Scalar.AliceBlue, 1);
                        //略...平面投影坐标点绘制,参见《足球视频AI(一)——位置与平面坐标的转换》
                        //略...平面投影球员号码绘制
                        playerNumber++;
                    });
                    //跟踪人员
                    if (mutiTracker.NeedFlush || (predictions.Count > mutiTracker.MaxCount * 1.3))
                        mutiTracker.Init(predictions, mat);
                }
                //刷新跟踪
                mutiTracker.Update(mat);
                frameNumber++;
            }
            Assert.True(mutiTracker?.MaxCount >0);
        }

三、总结

通过目标跟踪的实现,确定球员、裁判、球三个要素的唯一性。

结合二维平面投影,记录顺时的位置,便可以统计相关的比赛数据。

其中,总移动距离、顺时速度、持球速度、传射、防守等换算信息,不是我们研究的重点,属于工程化问题。

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

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

相关文章

【OpenCV】数字图像的表示 | 图像IO操作接口 | 图像混合操作

Ⅰ. 数字图像的表示 0x00 位数 计算机采用0/1编码的系统&#xff0c;数字图像也是利用0/1来记录信息。 我们平常接触的图像都是8位数图像&#xff0c;包含0&#xff5e;255灰度。 0&#xff1a;代表最黑 1&#xff1a;表示最白0x01 二值图像 一幅二值图像的二维矩阵仅由0、1…

VTK-vtkAbstractTransform及其子类

前言&#xff1a;本博文主要研究vtkAbstractTransform及其子类的实现原理&#xff0c;以及由此扩展的类。 目录 vtkAbstractTransform vtkGeneralTransform vtkGeoSphereTransform(9.0.3中没有该接口) vtkGeoTransform(9.0.3中没有该接口) vtkHomogeneousTransform vtkId…

企业微信客户联系自定义工具栏开发

一、问题说明&#xff1a;企业微信中私聊微信客户&#xff0c;在聊天输入框上面有一行快捷工具&#xff0c;这边怎么自定义开发&#xff1f;如下图&#xff0c;我们可以点击快捷发送&#xff0c;然后弹出一个页面&#xff0c;页面中有我们需要发送给当前会话的各种资源&#xf…

Maven高级-聚合-继承

多模块构建维护 作用&#xff1a;聚合用于快速构建maven工程&#xff0c;一次性构建多个项目/模块。 制作方式&#xff1a; 创建一个空模块&#xff0c;打包类型定义为pom <packaging>pom</packaging>定义当前模块进行构建操作时关联的其他模块名称 <modules&g…

2021年MathorCup高校数学建模挑战赛—大数据竞赛B题信息流智能推荐算法中的序列评估问题求解全过程文档及程序

2021年MathorCup高校数学建模挑战赛—大数据竞赛 B题 信息流智能推荐算法中的序列评估问题 原题再现&#xff1a; 随着互联网信息的蓬勃发展&#xff0c;用户在使用互联网应用时面临着信息过载的问题。推荐算法的出现&#xff0c;满足了用户个性化的内容消费需求&#xff0c;…

分布式任务调度系列 - XXL-JOB

一、前言 本内容仅用于个人学习笔记&#xff0c;如有侵扰&#xff0c;联系删除 二、传统的定时任务 1. 概念 1.1、定时任务的基本概念 程序为解决一个信息处理任务而预先编制的工作执行方案&#xff0c;这就是定时任务&#xff0c;核心组成如下&#xff1a; 执行器&#…

28个数据可视化图表的总结和介绍

数据可视化本身就是一种通用语言。我们这里通用语言的意思是&#xff1a;它能够向各行各业的人表示信息。它打破了语言和技术理解的障碍。数据是一些数字和文字的组合&#xff0c;但是可视化可以展示数据包含的信息。 “数据可视化有助于弥合数字和文字之间的差距”——Brie E…

C++入门——引用

1.概念 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0c;编译器不会为引用变量开辟内存空 间&#xff0c;它和它引用的变量共用同一块内存空间 类型& 引用变量名(对象名) 引用实体&#xff1b; 而引用类型必然要与引用实体的类型一致。 …

下载微信小程序中的视频

工具准备&#xff1a;Fiddler 我这里用的5.0的版本。&#xff08;这个用来抓取视频下载地址&#xff09;Internet Download Manager&#xff08;idm&#xff09;版本6.37&#xff08;这个用来下载视频&#xff09;步骤&#xff1a;打开Fiddler如下图配置后抓包2.登录PC版微信&a…

1.5日报

今天完成项目环境的搭建&#xff0c;并成功剥离TestMrl相关接口&#xff0c;并成功运行 遇到的问题&#xff1a; 缺少 <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <v…

展望2023,回首2022,让我们一起努力

回首2022 时光如白驹过隙般&#xff0c;飞逝而过。时光荏苒&#xff0c;日月如梭。不知不觉中&#xff0c;充满希望与光明的2023年已经到来了。回首2022年&#xff0c;有喜悦&#xff0c;有失落&#xff0c;有艰辛与困难&#xff0c;也有解决困难后的欣慰&#xff0c;有着无数…

第03章 运算符

运算符介绍 运算符是一种特殊的符号&#xff0c;用以表示数据间的运算、赋值和比较等。 算数运算符 算数运算符用于对数值型变量间的计算。 注意&#xff1a;图片中最后一行&#xff0c;字符串相加的结果中间没有空格&#xff0c;这个课件错误❌。 除法[ / ] System.out.pr…

RabbitMQ 消息类型

RabbitMQ 消息类型 下面我们简单介绍下RabbitMQ的一些消息种类&#xff0c;并结合Java代码进行学习。 如果需要执行代码&#xff0c;需要下载RabbitMQ的客户端&#xff08;例如java客户端&#xff1a; https://www.rabbitmq.com/java-client.html&#xff09; 使用maven&…

聊天突然尬住?教你用Python一键获取斗图表情包,各种表情包轻松化解尴尬

很多兄弟在聊天上没有下太多的功夫&#xff0c;导致自己聊天的时候很容易尬住&#xff0c;然后就不知道聊啥了&#xff0c;这时候合适表情包分分钟就能救场&#xff0c;但是一看自己收藏的表情包&#xff0c;好家伙&#xff0c;两只手都数得过来。 所以今天来给兄弟们分享一下…

IDEA书签,备份使用,全分支共享

IDEA原本设计就是给单分支保存书签使用的&#xff0c;但是我比较喜欢多个分支用同一个IDEA书签。然后在网上找到很久的IDEA书签全分支共享的办法&#xff0c;真心没找到合适的&#xff0c;自己浅浅总结了一下。首先找到我们需要备份书签的项目的目录然后在项目的目录下打开隐藏…

小程序 超长页面截图保存web-view+html2canvas

web-view文档建议参考----支付宝提供的文档&#xff0c;html2canvas官方文档&#xff08;官网可以下载html2canvas.js 和 html2canvas.min.js&#xff09;。由于篇幅受限&#xff0c;这里就贴了一下用法&#xff0c;对于web-view的配置情况&#xff0c;需要自己去查看文档&…

【测试】开发模型+测试模型

努力经营当下&#xff0c;直至未来明朗&#xff01; 文章目录一、开发模型和测试模型概述二、 开发模型一&#xff09; 瀑布模型二&#xff09;螺旋模型三&#xff09;增量模型和迭代模型四&#xff09;敏捷模型【重点:sunny:】三、 测试模型一&#xff09;V模型二&#xff09;…

【4.2】Ribbon负载均衡策略

【4.2】Ribbon负载均衡策略1 Ribbon--负载均衡策略2.1 修改负载均衡规则--代码方式2.1.1 具体测试&#xff1a;2.2 修改负载均衡规则--配置文件方式2.2.1 具体配置3 总结Ribbon负载均衡原理 中学习到&#xff1a; IRule接口决定了负载均衡的策略。 接下来学习IRule接口的实现有…

【Java编程进阶】Object类及常用方法详解

Java 编程基础教程系列&#xff1a;Java 编程进阶之路【从入门到精通】 &#xff0c;从入门到精通一站学习&#xff0c;买不了吃亏&#xff0c;买不了上当&#xff01;&#xff01; 文章目录1. Object类2. 常用的方法2.1 toString 方法2.2 equals 方法2.3 hashcode 方法3. 注意…

Verilog语法笔记(夏宇闻第三版)-数据类型及其常量、变量

目录 常量&#xff1a; 整数&#xff1a; x和z值: 负数: 下划线(underscore_)&#xff1a; 参数(Parameter)型&#xff1a; 变量&#xff1a; wire型&#xff1a; reg型&#xff1a; memory型&#xff1a; Verilog HDL中总共有十九种数据类型,数据类型是用来表示数字…