基于YOLO的王者荣耀精彩时刻自动剪辑

分类栏目:用户体验 - 前端开发

29121

发布于 暂无评论

一、需求背景

企鹅电竞是一款游戏直播产品,其中游戏短视频社区是其重要组成部分。

为了丰富游戏短视频内容,针对王者荣耀,需要一套自动化剪辑精彩时刻的系统,以能够快速根据主播直播内容生成精彩时刻反馈到游戏短视频社区。

二、问题思考

我们准备使用深度学习去探索这个问题。

该问题在计算机视觉中属于时序行为定位(Temportal Action Localization)问题,即,要在源视频中识别出包含某些行为的一段视频,包括该行为的起始帧和结束帧。

起初,我们尝试了CDC(Convolutional-De-Convolutional)这种端到端的解决方案,实验结果表明,通过标注的精彩时刻和非精彩时刻片段训练出来的模型并不能满足我们的要求,主要有以下不足:

一、识别率低

原因为王者荣耀的精彩时刻与非精彩时刻画面差别并不大。

相比其他行为,比如游泳场景和打篮球场景等的画面差别就比较大,因此CDC网络在这种行为检测上可以表现良好,但是并不适合王者荣耀精彩时刻。

二、CDC模型过大

CDC涉及到3D卷积所以比较大,不适合生产环境。

重新分析精彩时刻视频,我们发现其具有明显特征:起始点为双方英雄接触的时刻,结束点为击败敌方英雄的时刻。所以,只要我们可以识别出画面中我方和敌方英雄的特征以及击败敌方英雄时的特征就可以定位精彩时刻,也就是将时序行为定位问题转化为了图像检测的问题。

由于队友或者敌方英雄在击败对手时也会出现击败的特征,所以需要通过击败者的头像和技能区域识别是否为当前英雄的击败。

综上所述,问题可以转化为:

一、击败特征检测;

二、敌方和我方英雄检测;

三、分类击败者英雄和当前英雄。

三、模型训练

图像检测常用的模型有R-CNN、Fast R-CNN、Faster R-CNN、SSD、YOLO等,综合考虑运行速度与识别率,我们选取了YOLOv2来检测敌我英雄和击败时刻的特征;图像分类的模型就更多了,主要有AlexNet、VGGNet、GoogLeNet、ResNet、SqueezeNet等,考虑到SqueezeNet模型较小,准确率也不错,这里选择其作为英雄分类的模型。

1.数据准备

1.1 王者荣耀视频准备

通常情况下,一局王者荣耀的大部分画面是在清兵或者打野,画面中出现多个英雄或者出现击败时刻的情况比较少,但是通过王者荣耀的王者时刻功能录制的视频基本都是存在多个英雄或者有较多的击败时刻,因此我们可以选择使用王者时刻的视频来产生训练所需要的图片。

具体操作上,我们可以自己打游戏录制或者从YouTube上下载王者时刻视频,但是这两种方法都需要耗费较多时间。之后我们发现网站游戏时刻中存在大量优质的精彩视频,我们通过脚本将其批量下载下来使用。

1.2 标记精彩时刻特征

我们基于之前所做的采用传统图像识别算法识别精彩时刻特征的项目来确定当前画面中是否存在精彩时刻特征。

图像识别使用ORB(Oriented FAST and Rotated BRIEF)算法快速提取和描述特征点,使用GMS(Grid-based Motion Statistics for Fast, Ultra-robust Feature Correspondence)算法将测试特征点与标准集的特征点进行比较。具体的实现步骤如下:

  1. 利用FAST特征点检测来检测特征点
  2. 利用Harris进行角点度量(Opencv还提供了FAST角点度量),然后从特征点中挑选出角点响应值最大的N个特征点
  3. 在特征点邻域利用BRIEF算法建立特征描述符(矩阵)
  4. 首先初始化将测试图的特征点和标准图的特征点映射到相同的矩阵内,建立若干个窗口网格
  5. 统计每个测试网格与标准网格的匹配数,选择其中匹配数最大的作为匹配
  6. 验证素有网格是否有效,即该网格的所有匹配是否大于阈值,若大于,则为正确匹配,否则作为错误匹配
  7. 统计所有正确匹配,得出相似度

如下图所示,左方为抠取预定位置图片以及标准图片的特征值,右方为经过GMS匹配后的图片:

确定存在精彩时刻的图片,截取包含特征的区域,然后使用特征匹配算法确定英雄头像的位置。

在opencv中我们使用我方英雄头像,敌方英雄头像,以及掩码图片进行匹配,如下图所示:

python实现代码如下:

res = cv2.matchTemplate(img_rect, template1, cv2.TM_CCORR_NORMED, mask=mask)

通过处理之后我们可以得到如下图片的标记:

相关成就特征,比如击败、五连绝世等位置和尺寸都是固定的,所以标记起来很简单。

1.3 标记英雄位置

检测英雄的位置,我们可以通过检测英雄血条的位置实现,这里仍然使用模版匹配的方法标记英雄的位置。

血条的模版和掩码图片如下图所示:

使用模板匹配时,我们发现:

由于不知道画面中具体存在几个英雄,所以不能只取匹配值最大的点;如果将大于某个阈值的点都算为英雄的话,由于大部分匹配值都很接近,所以阈值很难选择;而且,每个图片的阈值选择可能不同。

如下图所示,上方阈值为0.76,下方阈值为0.78,虽然很接近,但是结果却大相径庭:

我们尝试取前十个最大值进行筛选,实践下来发现也比较困难的,下图为前十个值的图框:

之后,我们通过观察图片的匹配值生成的灰度图,发现匹配的血条会出现中心亮点,周围较黑的形状,如下图所示:

因此,我们可以通过在匹配值灰度图上寻找局部极大值点去匹配血条位置,使用scipy的ndimage最大滤波器代码如下:

 result = ndimage.maximum_filter(res, size=20)

获得的图片如下图所示:

获得局部极大值点的匹配值为res,然后计算20*20的像素框内所有点与极值点之间的曼哈顿距离之和distance,最后根据这两个变量计算出最终的值:

取出value的前10个值,并计算血条位置的像素值,用以确定是否是真实的血条以及辨别英雄身份 – 自己、队友或者敌人。识别的图片结果如下:

上述方法实际运用中所产生的血条标注错误情况,我们可以人工去除。

1.4 标记技能区域

这个区域基本是固定的,就在右下角,在标记中输入固定的数值即可。

1.5 获取英雄头像和技能区域分类数据

在1.2章节中同样可以获得英雄头像,并且可以通过与标准头像的特征匹配进行分类。

技能区域我们截取英雄的第一个技能键,通过此技能键对当前英雄进行分类。因为技能键的位置是固定的,所以我们可以获得每个英雄的视频,然后通过FFMpeg截取对应位置。

2.训练模型

2.1 YOLOv2模型训练

(1) Darknet编译

首先从Github上下载源码:

git clone https://github.com/pjreddie/darknet
cd darknet

如果环境中安装了CUDA、cnDNN或者Opencv,可以将Makefile中如下的变量设置为1:

GPU=1

CUDNN=1

OPENCV=1

然后执行make等待编译完成

make

(2) 生成数据集

这里我们使用和VOC数据集一样的数据组织格式,在darknet目录下执行如下命令:

mkdir glory
cd glory
mkdir JPEGImages //需要用此文件名
mkdir labels     //需要用此文件名

JPEGImages文件夹中为训练需要的图片,labels文件夹中为JPEGImages中图片对应的标注。

假设JPEGImages中存在000001.jpg文件,则labels中对应存在000001.txt文件,内容为图片中包含的物体类别及其归一化坐标,形式如下图:

第一列为对应的类别,后面分别为归一化的标注中心点坐标以及宽高。

假设图片的宽高为w、h,标注的坐标为,则第二到第五列对应的为:

根据之前1.2、1.3、1.4小节说明的标注方法为每张图片生成相对应的标注数据,然后分别将图片和标注文件放入JPEGImages和labels文件夹。之后将图片分为训练集和测试集,并将路径分别写入train.txt和val.txt文件,格式类似如下图:

(3) 模型配置

darknet文件夹下的cfg文件为网络的配置文件,其描述了网络的结构,我们这里使用yolov2-voc.cfg,并做如下修改,在训练的时候使用Training的batch和subdivisions,在测试的时候使用Testing的batch和subdivisions:

并在文件的最后修改classes的值为分类的个数,以及fliters的值为num * (classes + 5):

新建文件qgame.names,里面内容为分类的名称。新建qgame.data,里面内容为训练数据路径,模型保存路径等配置:

(4) 开始训练

./darknet detector train qgame.data qgame.cfg darknet19_448.conv.23

可以观察loss的下降趋势,这里在训练到大概5000轮的时候,loss大概下降到1-2左右,保持稳定。

2.2 SqueezeNet模型训练

这里使用caffe来训练SqueezeNet,训练方法会在后续的文章中进行介绍,这里就不做过多展开。

四、系统构建

模型训练完成后,我们使用Python构建自动剪辑程序,此程序需要依赖OpenCV和FFMpeg。

在darknet/python文件中有调用darknet框架的python接口。

如果不是在darknet目录下运行python的话,需要修改libdarknet.so的路径,如下图所示:

如果是python3,需要做如下修改:

系统整体而言,首先,利用opencv每秒钟读取一帧数据,然后运行yolo网络检测是否存在击败者、被击败者、获得成就以及敌方英雄特征;然后,如果存在击败者、被击败者以及获得成就,则将击败者头像和技能头像送入SqueezeNet网络识别英雄,并判断两者是否相等;之后,如果相等则此时刻为精彩时刻的结束点,接下来向前追溯直到连续三秒没有出现敌方英雄,此时即为精彩时刻的起点;最后使用FFMepg命令裁剪、合并视频,其中list.txt中为分段的视频,格式为file moments_i.mp4:

ffmpeg -y -i video -ss start -t duration moments_i.mp4

ffmpeg -y -f concat -i list.txt -c copy moments.mp4

系统运行效果如下所示:

me代表当前英雄,friends代表友方英雄,enemies代表敌方英雄,winner代表我方英雄头像,loser代表敌方英雄头像,achievement代表当前成就,skills代表当前英雄技能。

原视频标记结果:

剪辑处理结果:

五、未来工作

继续优化性能,尝试YOLOv3,减少YOLO网络运行以及FFMepg裁剪耗时,以及保证系统的完整可用。

特别鸣谢

感谢@eaglekuang(况鹰),@klobliu(刘平),@ezrealwang(王飞)在传统的图像识别算法识别精彩时刻特征以及英雄分类网络给予的帮助。

参考资料

YOLO项目官网:https://pjreddie.com/darknet/yolo/