背景弹幕是显示在视频上的评论,可以以滚动、停留甚至更多动作特效方式出现在视频上,是观看视频的人发送的简短评论。各大视频网站目前都有弹幕功能,之家也于2020年5月正式上线视频弹幕功能,受到了广大网友的喜爱,大家在观看视频的同时,也能通过弹幕进行互动。但密集的弹幕,遮挡视频画面,严重影响用户观看体验,如何解决?有人可能会说:可以关弹幕啊。但,把弹幕关闭看视频,那就完全失去了乐趣,其实弹幕也是视频的一部分。如何在用户既想看弹幕又不影响视频观看的体验呢?我们查阅了相关视频网站,发现B站推出了一种蒙版弹幕技术,可以让弹幕自动躲避人形区域,达到弹幕不挡人的效果。(B站视频弹幕不挡人的效果)(汽车之家视频弹幕不挡人的效果)原理如下:通过AI计算机视觉的技术,对视频内容进行分析,并将之前已经定义好的“视频主体内容”进行识别,生成蒙版并分发给客户端后,让客户端利用 CSS3 的特性进行渲染从而达成最终的效果。这样就形成了我们最终看到的,“不挡脸”弹幕效果。实现方法就正如 PS 中的“蒙版“一样,实心区域允许,空白区域拒绝,从而达到弹幕不挡人的效果。而技术的核心就在蒙版的生成上,所以将这个功能称之为‘蒙版弹幕’。
2.技术调研和选型蒙版弹幕原理已经了解了,但如何实现呢?我们调研了以下几个功能点:
1、提取视频帧画面。对音视频的处理,大家一般都会想到FFmpeg组件,我们也是使用FFmpeg组件提取每帧的视频画面,使用的是PyAV组件,PyAV是FFmpeg封装,能够灵活的编解码视频和音频,并且支持Python常用的数据格式(如numpy)。
2、识别视频帧画面人像区域。解决方案:使用AI计算机视觉的实例分割技术,可以识别视频帧画面的人像区域。
3、AI框架:目前市面上的AI框架,主要以TensorFlow,PyTorch最流行。TensorFlow:出身豪门的工业界霸主,由Google Brain团队研发。具有如下优点:支持多种编程语言;灵活的架构支持多GPU、分布式训练,跨平台运行能力强;自带TensorBoard组件,能可视化计算图,便于让用户实时监控观察训练过程;官方文档非常详尽,可查询资料众多;社区庞大,大量开发者活跃于此。PyTorch:以动态图崛起的学术界宠儿,是基于Torch并由Facebook强力支持的python端的开源深度学习库。具有如下优点:简洁:PyTorch在设计上更直观,追求尽量少的封装,建模过程透明,代码易于理解;易用:应用十分灵活,接口沿用Torch,契合用户思维,尽可能地让用户实现“所思即所得”,不过多顾虑框架本身的束缚;社区:提供完整的文档和指南,用户可以通过全面的教程完成从入门到进阶,有疑问也可以在社区中获得各种及时交流的机会。我们的选择:PyTorch。原因:TensorFlow入门难度较大,学习门槛高,系统设计过于复杂;而PyTorch入门难度低,上手快,而且提供的功能也非常易用,预训练模型也非常多。
4、实例分割技术:实例分割(Instance Segmentation)是视觉经典四个任务中相对最难的一个,它既具备语义分割(Semantic Segmentation)的特点,需要做到像素层面上的分类,也具备目标检测(Object Detection)的一部分特点,即需要定位出不同实例,即使它们是同一种类。实例分割方法和指标对比:Mask R-CNN:YOLACT:BlendMask:我们的选择:BlendMask。原因:BlendMask是一种自顶向下和自底向上设计策略相结合的实例分割算法,在精度上超越了MaskR-CNN,速度上快20%,可谓完美替代Mask R-CNN,是实例分割新标杆!
5、目标检测和实例分割的开源项目的选择:对于目标检测和实例分割,目前有以下几个通用的开源项目:Detectron:FAIR(FacebookAI Research)出品,基于caffe2;maskrcnn-benchmark:FAIR出品,基于PyTorch,可以看成Detectron的PyTorch升级版;Detectron2: FAIR出品,基于PyTorch,maskrcnn-benchmark的替代者MMDetection:商汤MMLab出品,基于PyTorch,Model zoo相当完备;SimpleDet:图森出品,基于MxNet;Tensorflow Object Detection:Google出品,基于TensorFlow 1.x;我们的选择:Detectron2。原因:基于PyTorch;FAIR出品;R-CNN系列最强实现;高度模块化,扩展性好;训练速度更快。BlendMask算法在Detectron2上也有实现:AdelaiDet组件。
6、人像区域蒙版图片存储。图片存储上,有PNG,JPG,SVG等格式可供选择,我们选择SVG来存储图片。因为SVG是矢量图形,不受像素影响,在放大或改变尺寸的情况下其图形质量不会有所损失,而且文件占用空间小。我们将人像区域蒙版图片存储成SVG图片,多张SVG图片压缩打包成单一文件并存储在之家FastDFS文件系统中。
7、客户端蒙版弹幕渲染。解决方案:前端使用CSS3的 mask-image属性,在弹幕层上来渲染蒙版。本文主要重点讨论的是蒙版弹幕的核心:视频蒙版的生成,客户端蒙版弹幕渲染,后续会出文章进行详细解读。3.视频蒙版生成设计思路通过技术调研,也参考了各类的分享资料,我们对视频蒙版生成整理了如下设计思路:
思路一:由于之家视频非常多,有些视频时间也比较长,使用AI计算机视觉识别单张图片的人像区域耗时长,我们可以将单个视频按照时间段分成多个任务,每个任务为视频的一段,这样,我们在多台机器上部署多个蒙版生成程序,让一个视频可以并行生成蒙版,保证视频蒙版的快速生成。所以我们想到了分布式任务管理系统。目前市面上分布式任务管理的系统大概有以下几个可供选择:xxl-job,ElasticJob等等,但由于接入起来成本较大(因为各种原因,如编程语言限制(python),使用场景可控性小),所以分布式任务调度系统选择自研。将视频蒙版生成以任务的形式进行生产、调度、消费,分为:蒙版任务生产端,蒙版任务调度端,蒙版任务消费端(视频蒙版生成程序)。蒙版任务生产端:对视频进行分析,将视频按照时长分段,各个分段视频信息包装成任务的形式存储在数据库中。比如一个视频时长为20分钟,我们可以将该视频分为两个任务:0-10分钟为第一个任务,11-20分钟为第二个任务。蒙版任务调度端:对蒙版任务进行调度,并对任务进行监控,如果任务有异常情况,进行报警。蒙版任务消费端:向蒙版任务调度端请求任务,执行蒙版生成任务,将各个蒙版图片压缩打包,存入之家FastDFS文件系统。
思路二:蒙版任务消费端进行蒙版生成的流程如下:根据蒙版任务包含的视频URL,蒙版任务对应的视频开始时间,蒙版任务对应的视频结束时间,使用FFmpeg依次提取视频每帧的视频画面,对提取的视频画面,使用Detectron2组件,进行实例分割,获得该视频画面对应的人像区域,将该人像区域使用potrace组件保存成SVG图像。并对多张SVG图片进行zlib压缩打包成单一文件,调用之家FastDFS上传文件接口将该文件存入之家FastDFS文件系统,将FastDFS返回的文件URL和视频信息存入数据库中。
思路三:蒙版文件数据交互协议:我们把多张蒙版SVG图片进行打包成单一文件,每张图片需要有对应视频的显示时间和其他的相关信息,而且需要支持压缩,所以,我们设计了一套私有二进制蒙版文件协议,方便数据传输和存储,该协议也能支持长连接传输。协议如下:名称类型长度偏移量描述Mask ?Header包头Tagint3240标识,固定值“ATHM”或者"MHTA",如果值是"ATHM"表示高字节序(网络字节序),如果值是"MHTA"表示低字节序Versionint1624协议版本(目前是103)协议100:原始svg图片文本?协议101: svg去除换行符(\r\n),并只返回d=""里面的内容,多个d=""以"|"分隔svg:协议102: 蒙层图片路径扩大 1倍, 高斯模糊边缘虚化 大小设置为 8 , svg :?协议103: 蒙层图片路径扩大 1倍, 高斯模糊边缘虚化 大小设置为 8?, svg :?Safe ?Codeint1626安全校验码(目前是65535)Segmentsint1628该数据包里有多少个段数据Media ?Typeint8110多媒体类型
1: FLV 2:HLS(M3U8) 3:RTMPEtc.int8111扩展字段位,目前:1表示Segment?Data ?ZLiB压缩 255 表示Segment?Data?无压缩FPS(video ?frames per seconds)int8112视频帧率MPS(video ?masks per seconds)int8113视频一秒几帧蒙版Mask ?Meta Data12*Segments每段的元数据(一个Mask Meta Data: 12字节)Segment ?Start MilliSecondint324该段数据里开始的毫秒数Segment ?End MilliSecondint324该段数据里结束的毫秒数Segment?Lengthint324该段数据长度Body包体Segment...段数据Timeint324该张图蒙版对应的时间(毫秒数)Data?Lengthint324该张图蒙版数据长度Databyte[]该张图蒙版数据(svg数据)
4.实践之路4.1、蒙版任务生产端(开发语言:java)流程如下:定时采集所有视频的弹幕数,如果该视频的弹幕数超过一定数量(目前是大于100条弹幕,后期我们会根据各个视频的时长来动态调整),开始制作蒙版任务。这里大家会有个疑问,为什么只有弹幕数超过了一定数量才进行蒙版弹幕生产呢?因为咱们蒙版弹幕是需要显示在视频上面才有效果的,如果视频弹幕过少,视频的主体内容区域是不会被弹幕遮挡的。使用FFmpeg组件,查看当前视频的时长。对该视频按照每10分钟进行切分,制作成各个蒙版任务,蒙版任务包含字段:视频Url,任务对应的视频开始时间,任务对应的视频结束时间等。将各个蒙版任务保存在数据库中。?4.2、蒙版任务调度端(开发语言:java)1.提供如下接口,供蒙版任务消费端调用:a.获得待执行的蒙版生成任务b.领取蒙版生成任务c.上报任务执行状态d.上报生成好的蒙版文件2.为防止蒙版任务消费端出现各种异常,中断等情况,蒙版任务调度端对执行中的任务进行定时清理,对长时间未上报执行日志的任务进行回收,生成一个新任务,并将旧任务生成的蒙版文件进行删除。清理任务完成后进行短信报警,相关人员接到短信后可对该任务进行任务分析,任务回溯等。
4.3、蒙版任务消费端(开发语言:python)根据视频类型和场景不同,分为如下几种方式:4.3.1原创视频原创视频,格式为:mp41.领取任务a.调用蒙版任务调度端接口,获得当前待领取的任务b.调用蒙版任务调度端接口,领取该任务c.如未领取任务成功,程序休眠1分钟,然后继续领取任务2.执行任务a. ?根据任务定义的视频开始时间和结束时间,使用FFmpeg截取相关时段视频,并保存在本地。b. ?使用python的cv2库,依次读取该时间段视频的所有帧。c. 使用Detectron2提供的实例分割技术,对视频帧的人像区域进行预测,对一些识别评分低的,区域太大或太小的人像舍弃,仅保留我们需要的人像区域,评分太低的人像有可能该物体不是人像,区域太大的人像会“镂空”过多的弹幕,影响弹幕观看体验。人像区域太小,弹幕是否遮挡意义不大,所以也舍弃之。d. ?对预测的人像生成蒙版png图片,使用potrace组件,将该png图片转换成SVG(1KB左右)。e. ?多张SVG图片进行压缩,打包,打包时需要将每张图片对应的视频时间(毫秒级)也一并打包,生成单个二进制文件。f. ?将该二进制文件上传到之家云服务器(FastDFS)中。g. ?文件上传后将获得的文件url调用蒙版任务调度端接口,将该文件url存储到数据库中。h. ?定时向蒙版任务调度端上报任务执行情况。任务调度端会定期扫描所有正在执行的任务,如果任务消费端程序长时间未上报任务状态,说明该程序可能卡死,或者进程已被kill,任务调度端会根据该任务生成一个新的任务,供其他任务消费端程序领取执行,同时,任务调度端会进行短信报警。3.结束任务a.任务结束,调用蒙版任务调度端接口,告知该任务已完成。b.执行任务清理工作:删除本地视频,临时文件等。4.3.2重播视频原创视频,格式为:m3u8。1.领取任务与原创视频领取任务一致。2.执行任务a.读取重播视频m3u8文件列表。b.使用PyAV组件,依次读取每个ts文件的开始和结束pts时间。c.根据任务定义的视频开始时间和结束时间定位到具体ts文件的位置。d.依次读取该时间段视频的所有帧。e.使用Detectron2的实例分割技术,对视频帧的人像进行预测。f.之后的流程与原创视频执行任务一致。3.结束任务与原创视频结束任务一致。4.3.3 直播场景 (开发中)直播场景下的防遮挡弹幕目前正在开发中,由于直播场景的实时性要求,不管是前端还是云端服务,也遇到了更多的挑战。同时,为了烘托直播的氛围,提升用户停留时长,我们将加入智能弹幕,丰富弹幕的内容。后续,我们会出专门的文章,分别讲解前端和云端在直播场景下防遮挡弹幕和智能弹幕的实践,敬请期待。5.优化之路5.1 cuda显存优化问题:刚上线的时候,程序经常报cuda runtime error(2): out of memory。优化过程:这是因为PyTorch 使用缓存内存分配器来加速内存分配,有时候不会正确的释放所有cuda内存,需手动调用torch.cuda.empty_cache()来释放cuda内存。我们定义了一个阈值,在处理100张图片后调用一次torch.cuda.empty_cache(),手动释放cuda内存。结果:cuda内存占用由15G左右降低到900M左右。5.2 预测单张图片时间过长问题:预测单张图片时间过长,单张图片200多ms。优化过程:a.更换模型,由Mask-RCNN更换成最新的BlendMask模型b.预测前,对图片进行等比缩小.
37152402000126号
鲁ICP备17032091号-13 Powered by qibosoft X1.0 Code ©2003-2020