前言

最近在补《吐槽大会》第三季,突然发现弹幕们似乎变得智能了,能够自动识别人物前景和背景,不会再遮挡住人物,看起来好像从人物身后穿过去一样。这样既保留了弹幕的趣味性,也不会被遮住画面中的重要内容。

本来我也只是感叹一下腾讯视频的功能强大,结果之后在逛知乎时偶然刷到一位大佬Crossin的文章《弹幕君,别挡着我看小姐姐!》(是不是很熟悉的名字?没错,我想不到什么更好的名字,就把这位大佬的文章标题也抄过来了),讲解了怎么通过Python实现视频的前景识别和文字叠加等功能。

原来B站这位弹幕祖师网站也支持了这样的功能,虽然只有部分视频开启了这个功能,估计复杂的背景可能会影响识别效果。

 

开发环境安装

先说一下会用到哪些第三方库和他们的部署方法吧,以下基于Python3.5.3版本。

OpenCV, 一个用于图像处理、分析、机器视觉方面的开源函数库 ,主要拿来提取画面内容制作蒙版。

PIL,用于给图片加文字。

NumPy,支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库。

matplotlib,用于图像处理。

pip install opencv-python  
pip install pillow
python -m pip install --user numpy scipy matplotlib ipython jupyter pandas sympy nose

需要注意的是:

1.PIL 库已停止维护,新的库叫 Pillow,但在使用时仍然是 import PIL

2.默认的字体不支持中文,因此需要提供字体文件并指定。py2 也不支持中文,需要额外使用 freetype 库。

3.OpenCV 库的 putText 方法也可以实现给图片加文字,但缺少对中文的支持

 

代码

首先制作弹幕方面,给图片加文字的代码如下:

from PIL import Image, ImageDraw, ImageFont 

setFont = ImageFont.truetype(r"HKSNT.TTF", 50) #设置字体
fillColor = "#ff0000"    #设置文字颜色 
image = Image.open("timg.jpg") 
draw = ImageDraw.Draw(image)   #创建绘图对象
width, height = image.size 
draw.text((40, height - 90), u'文字测试!!', font=setFont, fill=fillColor)    #在图上绘制文字
image.save("out.jpg", 'jpeg')  #输出图片

提取图片前景部分核心代码如下:

import numpy as np
import cv2

mask = np.zeros(img.shape[:2],np.uint8) 
bgdModel = np.zeros((1,65),np.float64) 
fgdModel = np.zeros((1,65),np.float64) 
rect = (10, 10, img.shape[1]-10, img.shape[0]-10)
cv2.grabCut(img,mask,rect,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT)

有了蒙版之后,就可以对图像进行运算。分别取出带弹幕图像的背景部分(其实直接拿带弹幕图像就可以),和原图像的前景部分,两个叠加一起,就是我们最终需要的效果。

img = img * (1-mask)[:,:,np.newaxis] + img * mask[:,:,np.newaxis]

 

注意

1. 前景提取的速度比较慢,为了能达到实时效果,在提取前景时,将图片缩小,获取蒙版之后,再将其放大至原尺寸。蒙版不需要太高精度,通过这个方法就可以做到实时。

2. 每一帧的处理速度有快有慢,为了稳定帧率,我加入了每帧时间的计算,如果时间不足设定时长,就 sleep 剩下的时间

3. 一些过渡帧的识别效果会比较差,导致中间少数蒙版出现类似“跳帧”的效果。为了平滑这些帧,我在程序里记录每一帧蒙版中前景像素的数量,如果当前帧与之前 20 帧的平均值差距超过 50%,那就认为这一帧的前景提取不合格,直接使用之前的蒙版

完整的代码太长,直接放大佬的git地址

 


总有些人觉得你怎么做都不对,

也许你唯一的不对就是理会他们。

《脱线森林》

——狐狸阿北