作者|李楚基
制作| AI技术大本营(ID:rgznai100)
引言:随着计算机视觉领域视频动作识别技术的发展,体育动作识别研究在统计运动特性、运动学研究、体育教学展示等方面得到越来越广泛的应用。对于各种球类比赛,根据比赛类型,他们的结构特征可以分为时间和分数两种类型。时间型体育项目在篮球、足球、橄榄球等比赛过程中,没有任何一方选手的专业区域,双方选手在位置上处于混合交错状态,在一段时间内通过团队合作赢得了比赛的胜利。比分类型的项目包括网球、羽毛球、乒乓球等。比赛时,双方选手总是在自己的区域内运动,在对手和位置上处于对立状态。这种类型通常是运动员经过自己的水平赢得比赛。在观看这种比赛时,观众往往会关注运动员的动作特征。
羽毛球比赛中,运动员的动作姿势信息可以为了解比赛过程、发现运动员的动作特征提供重要线索。羽毛球与排球、网球、乒乓球的特征相似,满足马尔可夫过程条件,比赛中运动员的每一击动作瞬间完成。教练或观众为了更好地理解和掌握羽毛球视频中运动员动作等重要信息,实现对羽毛球运动员的动作智能认识是有意义的。
目前,计算机视觉技术在视频动作识别方向的相关研究上取得了突破性进展,但大部分是对不同日常动作的广泛动作识别,对羽毛球视频动作识别的研究不足。如果能在羽毛球视频中按时间顺序安排击球动作,并在羽毛球视频中比较准确地判断击球动作类型,就可以为观众提供各种击球动作类型的视频集合。另外,在体育视频分析领域,也可以根据羽毛球的动作分类迁移到网球等项目。因为那种比赛形式与羽毛球有很多共同点,所以很容易转移体育特征。
所以今天我们将使用torch对羽毛球动作进行实时训练和预测。本文将其分为数据集制作、数据处理、模型构建和可视化几个阶段。模型在训练2000回合中的效果如下(左边是当前动作,右边是预测的未来10帧后的羽毛球动作):
羽毛球动作识别发展简介
为了识别羽毛球的击球动作,Chu等人使用基于姿势识别的方法从玩家的边界框中提取方向梯度直方图HOG,基于HOG根据支持向量机SVM对击球动作进行分类,但使用的训练和测试数据是击球瞬间的单一图像,Careelmont对压缩羽毛球视频的镜头进行分类,检测羽毛球的运动轨迹,识别击球动作。Ramasinghe等基于密集轨迹和轨迹对齐的HOG特征,提出了羽毛球视频动作识别方法。将运动员击球动作分为正手击球、反手击球、杏子等类型,但HOG本身没有尺度不变性,由于渐变的性质,HOG对噪音相当敏感。杨静等在体育视频中像素质量差、非静态视频和图像分辨率低的问题背景下,提出了基于光流的运动描述符,检测主要音频元素,捕捉运动员的摆动图像。最后,使用支持向量机对运动员的三种典型挥杆动作——进行挥杆、左挥杆和右挥杆的分类。王等人为了识别羽毛球击球类型,提出了基于身体传感器网络的双层隐藏马尔可夫模型分类算法,但对于传感器捕获的击球状态数据,并不适用于视频中羽毛球动作的有效识别。Rahmad等人比较了Alex Net、Google Net、VggNet-16、VggNet-19四种深度卷积预训练模式,比较了它们在分类羽毛球比赛形象时的表现。掌握选手们的各种动作,Google net的分类准确度最高,但瞄准的是羽毛球击球手。
羽毛球运动预测的构建
为了更好地研究羽毛球视频动作识别,正在实现对羽毛球视频选手进行击球动作的现场定位。
该程序的设计分为数据集制作、数据处理、模型构建和可视化几个阶段。
2.1提取骨骼数据集
在此,将准备好的视频材质放在项目文件下,使用da提取骨骼点存储。使用Openpose逐帧提取2.mp4视频文件的骨骼数据,并将其存储在txt文件中。代码如下:
parser=arg(description=' action recognition by openpose ')
('-video 'help=' path to video file . ')
Args=()
#导入相关模型
estimator=load _ pretrain _ model(' vgg _ origin ')
#初始化参数
Realtime_fps='0.0000 '
Start_time=()
Fps_interval=1
Fps_count=0
Run_timer=0
frame _ count=0
#读取和写入视频文件
Cap=cv。VideoCapture('2.mp4 ')
#video_
writer = set_video_writer(cap, write_fps=in)) # 保存关节数据的txt文件,用于训练过程(for training) f = open('origin_da;, 'a+') num=0 while cv.waitKey(1) < 0: has_frame, show = cap.read() if has_frame: fps_count += 1 frame_count += 1 # pose estimation humans = e(show) # get pose info pose = T(show, humans) # return frame, joints, bboxes, xcenter #video_wri(show) if len(pose[-1])==36: num+=1 print(num) # 采集数据,用于训练过程(for training) joints_norm_per_frame = np.array(pose[-1]).astype) f.write(' '.join(joints_norm_per_frame)) f.write('\n') cv.imshow("tets",show) cv.waitKey(1) else: break cap.release() f.close()2.2 数据处理
通过对数据观察发现,由于拍摄的视频遮挡较多,部分肢体提取为0会较大的影响模型效果,这里将这几个部位去除。代码如下:
f=open('origin_da;)
text=f.read()
f.close()
datasets=[]
text=("\n")
for i in text:
temp=i.split(" ")
temp1=[]
state=True
for j in range(len(temp)):
try:
(float(temp[j]))
except:
pass
if len(temp1) == 36:
(28)
(28)
(30)
(30)
for t in temp1:
if t==0.:
state=False
if state:
da(temp1)
flap=30#
x_data = datasets[:-1-flap]
y_data=datasets[flap:-1]
n=len(x_data)
2.3 LSTM模型搭建和训练
这里设置LSTM层神经元64,设置损失函数为为MSE误差函数,优化器为adam优化器,迭代次数为100轮,并将其损失图动态绘制。代码如下:
times=[]
losss=[]
nums=0
Epoch=100
correct=0
for k in range(Epoch):
for i in range(n):
x_np=np.array(x_data[i],dtype='float32')#此时x的维度为1维
y_np=np.array(y_data[i],dtype='float32')
#需要把x维度扩充到三个维度,[batch,time_step,input_size]
x=variable(x_np[np.newaxis,:,np.newaxis]))
y=variable(y_np[np.newaxis,:,np.newaxis]))
prediction=rnn(x)
if ().da().any==y.flatten().da().any:
correct+=1
loss=loss_func(prediction,y)
o()
lo()
o()
nums += 1
accuracy=float(correct/nums)
print("|Epoch:",k,"|step:",nums,"|loss:",loss.da(),"|accuracy:%.4f"%accuracy)
(nums)
lo(floa))
(times,losss)
)
2.4 模型可视化
根据预测出的骨骼坐标,定义基本骨骼连接方法和颜色,同时这里还要考虑到已经去除的骨骼,最终代码如下:
import cv2
def draw(test):
back=cv2.imread("back.jpg")
image_h, image_w ,c= back.shape
centers = {}
CocoColors = [[255, 0, 0], [255, 85, 0], [255, 170, 0], [255, 255, 0], [170, 255, 0], [85, 255, 0], [0, 255, 0],
[0, 255, 85], [0, 255, 170], [0, 255, 255], [0, 170, 255], [0, 85, 255], [0, 0, 255], [85, 0, 255],
[170, 0, 255], [255, 0, 255], [255, 0, 170], [255, 0, 85], [255, 0, 85]]
CocoPairs = [
(1, 2), (1, 5), (2, 3), (3, 4), (5, 6), (6, 7), (1, 8), (8, 9), (9, 10), (1, 11),
(11, 12), (12, 13), (1, 0), (0, 14), (14, 15), (5, 15)
]#修改了
for pos in range(0,16):
center = (int((test[2*pos] * (image_w//2) + 0.5)), int((test[2*pos+1] * (image_h//2) )))
centers[pos] = center
cv2.circle(back, center, 3, CocoColors[pos], thickness=3, lineType=8, shift=0)
for pair_order, pair in enumerate(CocoPairs):
cv2.line(back, centers[pair[0]], centers[pair[1]], CocoColors[pair_order], 3)
完整代码:
李秋键,CSDN博客专家,CSDN达人课作者。硕士在读于中国矿业大学,开发有taptap竞赛获奖等。