使用OpenCV+Tensorflow跟踪排球的轨迹
本文将带领大家如何把人工智能技术带到体育项目中。
运动中的人工智能是一个很新的东西,以下是一些有趣的作品:
- 篮球
- https://dev.to/stephan007/open-source-sports-video-analysis-using-maching-learning-2ag4
- 网球
- https://www.researchgate.net/publication/329740964_Convolutional_Neural_Networks_Based_Ball_Detection_in_Tennis_Games
- 排球
- https://www.tugraz.at/institute/icg/research/team-bischof/lrs/downloads/vb14/
我是个排球迷,所以让我们来看看最后一个网站,这是一个奥地利研究所的网站,他分析了当地业余联赛的比赛数据。
其中有一些文档需要阅读,最主要的信息是视频数据集。
排球是一项复杂的运动,有许多不同的因素,所以我从一个很小但很重要的部分开始——球。
跟踪球是一项非常著名的任务。谷歌提供了很多链接,但其中有许多只是一个简单的演示。在摄像机前识别和跟踪一个彩色的大球是无法与真实的比赛用球检测相比较的,因为现实世界中的球很小,移动速度很快,而且融入了背景中。
最后,我们希望得到这样的结果:
在开始之前,让我们先来研究以下视频数据集的一些细节:
- 摄像机是静止的,位于球场后面
- 如果打球的运动员技术水平不是高,我们可以轻松地看到球(专业人士击球太猛,如果没有电视回放几乎不可能看到球)
- 球的颜色:蓝色和黄色,与地板的反差不大,这使得所有基于颜色的方法变得毫无意义
解决方案
到目前为止最明显的方法 —— 用颜色 —— 不起作用,所以我利用的是正在移动中的球。
让我们找到移动的物体并识别出它是球,听起来很简单。
OpenCV包含用于检测带有背景移除的移动对象的工具:
mask = backSub.apply(frame)
mask = cv.dilate(mask, None)
mask = cv.GaussianBlur(mask, (15, 15),0)
ret,mask = cv.threshold(mask,0,255,cv.THRESH_BINARY | cv.THRESH_OTSU)
将这样的图片
转化成:
在这个例子中,球在上面,人脑和眼睛可以很容易地检测到它,那我们是如何决定的?可以从图中可以推断出一些规律:
- 这个球是团状的
- 这是照片上最高的斑点
但第二条规律不太管用,例如在这张照片中,最高的斑点是裁判的肩膀。
但 highest-blob 方法提供了进一步的初始数据。
我们可以收集这些斑点并训练分类器来区分球。
此数据集如下所示:
在人工智能方面来看,这是一个二元分类的彩色图像,非常类似于Cats-vs-Dogs挑战。
- Cats-vs-Dogs:https://www.kaggle.com/c/dogs-vs-cats
实现的方法有很多种,但最流行的方法是使用VGG神经网络。
- VGG神经网络:https://blog.keras.io/building-powerful-image-classification-models-using-very-little-data.html
一个问题 —— 球的图片非常小,不适合用多个卷积层,所以我不得不将VGG简化为一个非常简单的架构:
model = Sequential([
Convolution2D(32,(3,3), activation='relu', input_shape=input_shape), MaxPooling2D(), Convolution2D(64,(3,3), activation='relu'),
MaxPooling2D(),
Flatten(),
Dense(64, activation='relu'),
Dropout(0.1),
Dense(2, activation='softmax')
])
model.compile(loss="categorical_crossentropy", optimizer=SGD(lr=0.01), metrics=["accuracy"])
该模型简单,结果一般:大约20%的假阳性和30%的假阴性。
但这总比什么都没有好,当然,这样还不够。
应用于游戏的模型会产生许多“假球”:
实际上有两种假球:
- 它们随机时间出现在随机位置
- 这个模型总是犯错误,把其他东西认作一个球
轨迹
下一步,我们的想法是这样:球不会随机移动,而是遵循抛物线或线性轨迹。
对这个几何体上的水滴运动进行验证将切断随机和一致的错误。
有一个记录一次打球的轨迹的例子:
其中有向路径为蓝色,静态路径为绿色,随机路径为灰色。
只有蓝色的轨迹才是有用的,它们至少由3个点组成,并且有一个方向。方向是非常重要的,因为有了方向就可以预测下一个点,以防它在实际流中丢失,并且没有检测到新的路径。
这个逻辑应用到片段中产生一个相当真实的跟踪:
链接
Github存储库
- https://github.com/tprlab/vball
参考链接:https://towardsdatascience.com/ball-tracking-in-volleyball-with-opencv-and-tensorflow-3d6e857bd2e7
☆ END ☆
- SpringCloud配置中心集群搭建
- HDU1846 Brave Game
- 拉格朗日插值
- python爬虫入门(二)Opener和Requests
- python爬虫入门(三)XPATH和BeautifulSoup4
- python爬虫入门(四)利用多线程爬虫
- LOJ #115. 无源汇有上下界可行流
- 数据库改名系列(数据库名,逻辑名,物理文件名)
- BZOJ1468: Tree
- 洛谷P3806 【模板】点分治1
- 探索ASP.NET MVC5系列之~~~5.缓存篇(页面缓存+二级缓存)
- 洛谷P3383 【模板】线性筛素数(Miller_Rabin)
- BZOJ3667: Rabin-Miller算法
- Numpy 修炼之道 (2)—— N维数组 ndarray
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Python实现图片查找轮廓、多边形拟合、最小外接矩形代码
- python中return如何写
- PHP钩子实现方法解析
- 浅谈numpy中函数resize与reshape,ravel与flatten的区别
- PHP文件类型检查及fileinfo模块安装使用详解
- PHP封装的分页类与简单用法示例
- ThinkPHP3.2框架自带分页功能实现方法示例
- PHP获取访问设备信息的方法示例
- PHP实现微信提现功能
- PHP实现微信退款功能
- PHP利用pdo_odbc实现连接数据库示例【基于ThinkPHP5.1搭建的项目】
- PHP登录验证功能示例【用户名、密码、验证码、数据库、已登陆验证、自动登录和注销登录等】
- 实现php删除链表中重复的结点
- Yii2.0框架实现带分页的多条件搜索功能示例
- 定位地理位置PHP判断员工打卡签到经纬度是否在打卡之内