依赖: C# OpenCVSharp WPF Numpy

目的:解决足球场上,人物投影到二维平面的位置坐标

图A / B/ C

一、基础概念

1.1标准球场的定义:

参考:https://zh.m.wikipedia.org/zh/%E8%B6%B3%E7%90%83%E5%A0%B4

图 D

1.2 配准思路

图A->图B,建立坐标关系对;

图B->图D,建立真实坐标系的配准基准点对(需要4个以上)

图D->图C,建立显示坐标系的配准基准点对(图C尺寸已知,所有关键位置A-J均已知)

对于全局,利用OpenCV FindHomography,求得D-C坐标转换矩阵T;

对于每帧图片,利用OpenCV FindHomography,求得B-D坐标转换矩阵T1;

将对象检测的球员投影到图3

二、代码实现

2.1 标画关键点

WPF界面

    <Grid><Grid.ColumnDefinitions><ColumnDefinition Width="0.4*"/><ColumnDefinition Width="0.6*"/></Grid.ColumnDefinitions><Grid x:Name="gridPointSelector" Width="350" Height="234" VerticalAlignment="Center" HorizontalAlignment="Center" ><Grid.ColumnDefinitions><ColumnDefinition Width="0.15619*"/><ColumnDefinition Width="0.34762*"/><ColumnDefinition Width="0.34762*"/><ColumnDefinition Width="0.15619*"/></Grid.ColumnDefinitions><Grid.RowDefinitions><RowDefinition Height="0.20441*" /><RowDefinition Height="0.59118*"/><RowDefinition Height="0.20441*"/></Grid.RowDefinitions><Image Source="{StaticResource 2D_field}" Grid.ColumnSpan="4" Grid.RowSpan="3" /><ToggleButton Content="A" Grid.Row="0" Grid.Column="0"  HorizontalAlignment="Left" VerticalAlignment="Top"  Margin="-15,-15,0,0" Click="ToggleButton_Click"/><ToggleButton Content="B" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,-15,-15,0"  Click="ToggleButton_Click"/><ToggleButton Content="C" Grid.Row="0" Grid.Column="3" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,-15,-15,0"  Click="ToggleButton_Click"/><ToggleButton Content="D" Grid.Row="1" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,-15,-15,0"  Click="ToggleButton_Click"/><ToggleButton Content="E" Grid.Row="1" Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,-15,-15,0"  Click="ToggleButton_Click"/><ToggleButton Content="F" Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,-15,-15,0"  Click="ToggleButton_Click"/><ToggleButton Content="G" Grid.Row="2" Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Top"   Click="ToggleButton_Click"/><ToggleButton Content="H" Grid.Row="2" Grid.Column="0"HorizontalAlignment="Left" VerticalAlignment="Bottom"    Click="ToggleButton_Click" /><ToggleButton Content="I" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Bottom"  Click="ToggleButton_Click"/><ToggleButton Content="J" Grid.Row="2" Grid.Column="3" HorizontalAlignment="Right" VerticalAlignment="Bottom"  Click="ToggleButton_Click"/></Grid><Grid Grid.Column="1" x:Name="gridMain" ClipToBounds="True"><Image x:Name="imgMain" Source="{Binding OriginalImage }" Stretch="Uniform"/><Canvas x:Name="canvas" Height="{Binding ElementName=imgMain,Path=ActualHeight}"Width="{Binding ElementName=imgMain,Path=ActualWidth}" Background="Transparent"IsEnabled="{Binding IsSelectting}" MouseLeftButtonUp="canvas_MouseLeftButtonUp"></Canvas></Grid></Grid>

数据结构

public class VedioPointMark
{public string? Name { get; set; }/// <summary>/// 球场坐标/// </summary>public Point2d FiledPoint { get; set; }/// <summary>/// 视频像素坐标/// </summary>public Point2d VideoPixelPoint { get; set; }}

WPF后端代码

 public partial class UCPointMark : UserControl{MPointMark model;private readonly int ellipseSize = 16;ToggleButton? selectButton;public UCPointMark(){InitializeComponent();model = new MPointMark();this.DataContext = model;}private Border? DrawPoint(){if (selectButton == null)return null;Border myBorder = new Border();myBorder.CornerRadius = new CornerRadius(ellipseSize);myBorder.Width = ellipseSize;myBorder.BorderBrush = new SolidColorBrush(Colors.Blue);myBorder.Background = new SolidColorBrush(Colors.Red);myBorder.Child = new TextBlock() { Text = selectButton.Content.ToString(),Foreground = new SolidColorBrush(Colors.White),TextAlignment = TextAlignment.Center};selectButton.Tag = myBorder;return myBorder;}public List<VedioPointMark> CalculateTransform() {var markPoints = new List<VedioPointMark>();//必须选择4各以上if (canvas.Children.Count < 4)return markPoints;var allPoints = VideoFieldTransform.CreatePointMarks();var axisX = (double)model.OriginalImageSize.Width / this.imgMain.ActualWidth;var axisY = (double)model.OriginalImageSize.Height / this.imgMain.ActualHeight;foreach (var element in this.gridPointSelector.Children){if (element is ToggleButton toggle && toggle.Tag is Border pixcelBorder&& pixcelBorder.Tag is Point pixcelPoint && toggle.IsChecked == true){var mark = allPoints.Where(p => p.Name?.Equals(toggle.Content.ToString()) == true).FirstOrDefault();if (mark == null)continue;mark.VideoPixelPoint = new OpenCvSharp.Point2d( pixcelPoint.X * axisX, pixcelPoint.Y * axisY -40 );markPoints.Add(mark);}}return markPoints;}private void canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e){try{var clickPoint = e.GetPosition(this.canvas);var ellipse = DrawPoint();if (null == ellipse) return;this.canvas.Children.Add(ellipse);Canvas.SetLeft(ellipse, clickPoint.X - ellipseSize / 2);Canvas.SetTop(ellipse, clickPoint.Y - ellipseSize / 2);ellipse.Tag = new Point(clickPoint.X, clickPoint.Y);model.IsSelectting = false;}catch (Exception ex){this.Log(ex);}}

2.2 配准转换类

    public class VideoFieldTransform{private readonly Point2d[] point1;  //视频可视范围内的关键点坐标(4个以上)private readonly Point2d[] point2;  //真实球场坐标系 或 平面坐标系private readonly Mat H; //变换矩阵public VideoFieldTransform(List<VedioPointMark> pointMarks,bool haspoint2 = false){if (!haspoint2){//视频坐标投影到物理坐标pointMarks = pointMarks.OrderBy(p => p.Name).ToList();point1 = pointMarks.Select(p => p.FiledPoint).ToArray();point2 = pointMarks.Select(p => p.VideoPixelPoint).ToArray();}else{//物理坐标投影到图片point1 = new Point2d[] {new Point2d(0, 0),new Point2d(16.4f, 13.9f),new Point2d(52.5f, 0.0f),new Point2d(88.6f, 13.9f ),new Point2d(105f, 0.0f),new Point2d(105f, 68.0f),new Point2d(52.5f, 68f),new Point2d(0, 68f)};point2 = new Point2d[] {new Point2d(0.0f, 0.0f ),new Point2d(164f, 152f ),new Point2d( 525f, 0.0f),new Point2d(886f, 152f),new Point2d(1050f, 0f),new Point2d(1050f, 699f),new Point2d(525f, 699f),new Point2d(0f, 699f),};}H = CalculateHomoGraphy(haspoint2);}public static List<VedioPointMark> CreatePointMarks(){return new List<VedioPointMark>(){new VedioPointMark() {  Name = "A", FiledPoint = new Point2d(0, 0)},new VedioPointMark() {  Name = "D", FiledPoint = new Point2d(16.4f, 13.9f)},new VedioPointMark() {  Name = "B", FiledPoint = new Point2d(52.5f, 0.0f)},new VedioPointMark() {  Name = "E", FiledPoint = new Point2d(88.6f, 13.9f)},new VedioPointMark() {  Name = "C", FiledPoint = new Point2d(105f, 0.0f)},new VedioPointMark() {  Name = "J", FiledPoint = new Point2d(105f, 68.0f)},new VedioPointMark() {  Name = "I", FiledPoint = new Point2d(52.5f, 68f)},new VedioPointMark() {  Name = "H", FiledPoint = new Point2d(0, 68f)},new VedioPointMark() {  Name = "F", FiledPoint = new Point2d(16.4f, 40.2f)},new VedioPointMark() {  Name = "G", FiledPoint = new Point2d(88.6, 40.2f)},};}/// <summary>///     视频帧(假定摄像头位置不变)与 平面模式的球场位置的坐标系换算,求得矩阵/// </summary>/// <param name="make_rotate"></param>/// <returns>返回H 为变换矩阵</returns>private Mat CalculateHomoGraphy(bool make_rotate = false){//var k = InputArray.Create(point2.GetData<float[]>());if (!make_rotate)return Cv2.FindHomography(point2, point1, HomographyMethods.Ransac);return Cv2.FindHomography(point1,point2, HomographyMethods.Ransac);}/// <summary>/// 根据坐标点(X,Y) 与 坐标系变换矩阵乘积,换算帧图像的位置到平面球场坐标的位置/// </summary>public Point Transform(Point p){var img2Bounds = new[]{new Point2d(p.X, p.Y)};var img2BoundsTransformed = Cv2.PerspectiveTransform(img2Bounds, H);var drawingPoints = img2BoundsTransformed.Select(p => (Point)p).FirstOrDefault();return drawingPoints;}}

2.3 初始化

图D->图C,建立显示坐标系的配准基准点对(图C尺寸已知,所有关键位置A-J均已知)

其中,pointMarks为WPF标记的点集

if (pointMarks?.Count > 3)proj_field_to_top = new VideoFieldTransform(pointMarks);  //真实球场坐标

从绝对球场尺寸到显示尺寸的变换,参见1.2 D-C变换

 proj_field_2d = new VideoFieldTransform(true);  //平面显示坐标

加载底图

//加载球场真实坐标系底图
D_field_photo = LoadImages.Load("2D_field.png");

初始化目标检测

//对象检测,此处略...参见AI机器学习(五)相关的内容
detector = new DetectorYolov7();

2.4 逐帧变换

假设某个球员的Mat 区域,在视频中的像素位置标记为rect

//获取帧图片
foreach(var frame in LoadImages.LoadVideo("你的视频路径"))
{Mat imagedetect = new Mat();//投影所有的球员foreach(var prediction =detector.Detect(imagedetect)){var rect = prediction.Rectangle;//拷贝真实球场坐标底图  var J = D_field_photo.Clone();//转换坐标系var pfield = proj_field_to_top.Transform(new OpenCvSharp.Point(rect.X , rect.Y));//用于显示的图像var pshow = proj_field_2d.Transform(new OpenCvSharp.Point(pfield.X, pfield.Y));Cv2.Circle(J, pshow.X, pshow.Y, 10, Scalar.LightCyan, -1);//发送给前端WPF,这里没给出用于显示的页面,大家自己实现一个即可,只需要一个Image控件OnDisplay?.Invoke(null, J.Resize(new OpenCvSharp.Size(300, 200)).ToBytes());}
}

足球视频AI(一)——位置与平面坐标的转换相关推荐

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

    一.基础概念 在之前的四节中,我们尝试解决: 1,球员识别.足球识别.裁判识别: 2,队伍的分类 3,平面坐标的换算 存在关键的问题是:每一帧的画面,每次都是重新识别,无法将特定的人与坐标对应上. 我 ...

  2. 足球视频AI(二)——球员与球的目标检测

    一.基础概念 1.1 识别目标: 1)固定机位的视频中球员逐帧识别 2)固定机位的视频中球逐帧识别 3)位置换算与记录 1.2 实现思路 1,利用OpenCV的相邻帧差异识别移动物体 2,利用YOLO ...

  3. 新一代视频AI服务 —— 阿里云智能视觉重磅发布

    2019独角兽企业重金招聘Python工程师标准>>> 3月27日下午,第51期阿里云产品发布会-智能视觉产品隆重发布,本次产品发布会首次面向全网用户深入的解读了智能视觉的前世今生. ...

  4. 视频AI,助力体育赛事转播走进智能时代

    摘要: 2018俄罗斯世界杯经过近20天的激战,已经进入到最关键的阶段.本次赛事除了精彩纷呈的比赛之外,还加入很多高科技的元素,例如门线.VAR技术等等.让本届世界杯成为科技含量最高的一届世界杯. 2 ...

  5. 视频AI对话杭州云栖:新一代视频智能生产的探索与实践

    在今年的杭州云栖大会AB区中庭,有一个全透明建筑备受瞩目,这就是云栖数据指挥中心.在这块大屏中的媒体中央厨房部分,正是采用了视频智能生产方案,将多模态内容理解与云导播和云剪辑相结合,使原来小时级别的视 ...

  6. 百度VidPress Sports团队获SoccerNet-v2足球视频理解竞赛双料冠军

    2021欧洲杯足球赛近日"姗姗来迟",牵动了无数球迷的心.精彩的球赛视频背后,令人意想不到的是 AI 技术正在重塑体育视频产业的内容生产方式. 在近期举办的国际计算机视觉与模式识别 ...

  7. 城管视频ai智能分析系统

    城管视频ai智能分析系统通过yolo系列架构模型人工智能深度学习技术,对现场画面中店外经营.乱堆物料.违规摆摊.乱扔垃圾.占道经营.非机动车乱停放等行为实时监测分析.YOLO的结构非常简单,就是单纯的 ...

  8. 云端智创 | 基于视频AI原理的音视频智能处理技术

    本文内容整理自「智能媒体生产」系列课程第二讲:视频AI与智能生产制作,由阿里云智能视频云高级技术专家分享视频AI原理,AI辅助媒体生产,音视频智能化能力和底层原理,以及如何利用阿里云现有资源使用音视频 ...

  9. 阿里云视频AI全能力解读

    摘要: 结合人工智能视频理解流程和用户的需求场景,我们将视频AI的功能分成四个大部分,视频智能审核.视频内容理解.视频智能编辑.视频版权保护.其中视频审核功能包括视频鉴黄.暴恐涉政识别.广告二维码识别 ...

最新文章

  1. 《游戏开发物理学(第2版)》一1.3 坐标系
  2. 11粘土人脖卡面撑怎么用_纽约春节必备小吃!鸭脖、炸串、麻辣烫...送到家门口,放肆撸爽!...
  3. springMVC 前台向后台传数组
  4. 01.神经网络和深度学习 W2.神经网络基础(作业:逻辑回归 图片识别)
  5. python定义字体颜色_windows print 自定义字体颜色【python】
  6. c4.5算法python实现_算法:用Python实现—最优化算法
  7. 数据与运算(以及补码)
  8. Hyperion神器之SmartView产品(下篇)
  9. 打开php页面变成下载的解决办法
  10. 人工智能 深度学习 机器学习
  11. css 小尖角,css如何实现气泡的小尖角效果 css实现气泡的小尖角效果代码示例
  12. 如何编写爬虫获取淘宝网上所有的商品分类以及关键属性 销售属性 非关键属性数据
  13. (53条消息)MySQL在Windows上的安装流程
  14. Tensorflow基础语法以及网络搭建
  15. 如何破解linux密码
  16. 零基础学python实战答案-Python3.6零基础入门与实战 PDF 带源码视频版
  17. C/C++《程序设计基础(C语言)课程设计》[2023-04-20]
  18. 旧手机先别扔,余承东:留着升级一下鸿蒙
  19. 合作共赢:加密云储——穿针引线,布局IPFS分布式存储领域
  20. Builder模式总结

热门文章

  1. PYNQ搭建系统-Petalinux上网方式
  2. UVA 1600 巡逻机器人
  3. NetBeans 尚未部署该模块错误 解决方案
  4. 彩色花砖机技术参数列表厂家分享
  5. 哪些5G芯片和5G模组已经问世?| 截止至2020年Q1
  6. 加载、编辑和保存Wod格式所见所得的Word文档处理控件TX Text Control ActiveX
  7. 正则表达式常用语法速查+一个简单使用案例
  8. 速领电商:怎么制作视频短片
  9. 快速搭建自己的conda环境---以bioconda为例
  10. BDW01手把手系列01:BDW01开发板基于TencentOS Tiny之helloworld!