当前位置:   article > 正文

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

球员跟踪

一、基础概念

在这里插入图片描述

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

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
  • 1
  • 2
  • 3
  • 4

其中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; }

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

该数据结构,将首次对象检测到的球员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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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();
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

其中,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);
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

三、总结

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

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/747991
推荐阅读
相关标签
  

闽ICP备14008679号