使用词云生成B站小姐姐舞蹈视频
- 实现思路
- 一. 获取资源
- 二. 视频处理
- 2.1 提取每帧图像内容
- 2.2 提取人体轮廓
- 三. 内容获取
- 3.1 弹幕获取分析
- 3.2 弹幕获取代码实现
- 四. 弹幕内容处理
- 4.1 概念介绍
- 4.2 实现过程
- 4.3 代码实现
- 五. 生成新的视频文件
- 六. 添加音频信息
本教程将分为几篇文章讲述如何使用词云生成小姐姐跳舞的视频。使用词云生成的视频的效果如下图所示。
实现思路
为了达成生成由词云填充的跳舞视频效果,需要由以下几个步骤构成:
- 获取需要的资源,我们找到B站上的视频链接(https://www.bilibili.com/video/BV1YE411C76u),下载链接对应的视频文件;
- 提取视频中每帧图像,从每帧图像中提取出人体轮廓,并与背景分离;
- 获取该视频的弹幕内容;
- 使用词云将弹幕内容填充到人体轮廓图片中;
- 添加音乐资源,并将词云填充的人体轮廓图生成视频内容;
以下内容将按照上述讲解的步骤来组织内容。
一. 获取资源
获取资源,并下载该资源文件,下载所需的工具为:you-get(https://github.com/soimort/you-get),在安装了pip的机器上,执行命令:pip install you-get 即可完成安装。其余安装方法可以参考链接页面上的“Installation”完成安装。下载该视频时,执行命令,you-get -o 本地地址 链接地址 即可下载视频。下载该视频到本机E盘根目录下载的命令为:you-get -o e: https://www.bilibili.com/video/BV1YE411C76u,示例图如下所示:
下载失败截图如下:
下载失败后,可以参考链接(https://github.com/soimort/you-get/pulls)来解决。you-get还可以支持更多的视频下载和其它更有趣的功能,其它功能不在我们讨论的问题之列。
二. 视频处理
以下将进入主要内容的讨论,先需要对视频进行处理,提取出构成视频的每帧图像内容,并对提取出的每帧图像分理出人体轮廓。为了提取出有效的人体轮廓,背景内容最好比较单一。
2.1 提取每帧图像内容
下载的视频为flv格式,下载的视频如下图所示。
为了处理方便,将文件命名为yaya.flv。为了提取每帧图像,将使用opencv-python(https://github.com/opencv/opencv-python)库。opencv是一个强大的图像处理和计算机是视觉库。在使用之前,需要安装opencv-python库,使用pip install opencv-python即可安装,为了保证下载及安装速度,推荐使用国内源进行安装。pip install opencv-python -i https://pypi.doubanio.com/simple ,这里安装时,使用了豆瓣源。
以下为提取每帧图像的代码:
import cv2
num = 0
cap = cv2.VideoCapture(r'yaya.flv')
# 读取800帧内容
while num < 800:
ret, frame = cap.read()
if ret:
cv2.imwrite(f'./pic1/img_{num}.jpg', frame)
num += 1
else:
break
cap.release()
cv2.VideoCapture将读取flv文件,使用read()方法读取每帧内容,我们这里只读取了800帧图片,并使用imwrite()将读取的每帧内容保存成图像内容。
生成的图片如下图所示:
2.2 提取人体轮廓
为了提取人体轮廓,这里使用了百度提供的人体分析功能中的人像分割功能。为了使用该功能,需要注册百度账号。参考人像分割文档(https://cloud.baidu.com/doc/BODY/s/Fk3cpyxua),完成应用创建。
复制应用中创建的APPID、API Key及App Secret信息,创建提取人体轮廓Python代码。
import cv2
import base64
import numpy as np
import os
from aip import AipBodyAnalysis
APP_ID = "your app's app id"
API_KEY = "your app's key"
SECRET_KEY = "your app's secrect key"
client = AipBodyAnalysis(APP_ID, API_KEY, SECRET_KEY)
path = './mask_img1/'
img_files = os.listdir('./pic1')
for num in range(0, len(img_files)):
img = f'./pic1/img_{num}.jpg'
img1 = cv2.imread(img)
height, width, _ = img1.shape
with open(img, 'rb') as fp:
img_info = fp.read()
seg_res = client.bodySeg(img_info)
labelmap = base64.b64decode(seg_res['labelmap'])
nparr = np.frombuffer(labelmap, np.uint8)
labelimg = cv2.imdecode(nparr, 1)
labelmap = cv2.resize(labelimg, (width, height), interpolation=cv2.INTER_NEAREST)
new_img = np.where(labelmap == 1, 255, labelimg)
mask_name = path + 'mask_{}.png'.format(num)
cv2.imwrite(mask_name, new_img)
在上述代码中,img1 = cv2.imread()表示读取图片信息,并通过img1.shape得到图片的像素信息。
client.bodySeg(img_info)表示将每张图片发送到百度提供的人体分析平台去处理,并得到处理图像的二值信息。以下几行代码就是将得到的二值信息最终生成一张新的图,生成的图片如下所示。
经过上述过程,即将人体轮廓与背景完成了分离。下一步,就可以获取弹幕信息了,将获取的弹幕信息经过分析处理,就可以填充进人体轮廓中了。
三. 内容获取
3.1 弹幕获取分析
为了填充有意义的内容,我们需要获取该视频对应的弹幕信息。因为还存在历史弹幕,我们需要爬取一个时间段的弹幕。爬取弹幕内容,需要找到弹幕对应的链接信息,通过使用浏览器自带的WEB开发工具,可以帮我们来更好地来解决这个问题,这里以Firefox浏览器来举例说明。打开浏览器上的"工具"->“Web开发者”->“Web控制台”,点击"网络"标签,点击"弹幕列表"中的"展开"。
为了找到弹幕对应的链接,我们首先清除掉当前展示信息。
点击"查看历史弹幕",并选择日期,则控制台将输出请求的信息。
点击这条信息,查看详情信息。
在右边的"消息头"处,可以看到GET内容,这里就是获取弹幕信息的API,其链接为:
https://api.bilibili.com/x/v2/dm/web/history/seg.so?type=1&oid=126220511&date=2021-01-01,有了这个链接,如果我们需要获取2020年1月1日到2021年1月1日间的弹幕信息,只需要在链接中,date部分,填入我们需要的日期即可。
3.2 弹幕获取代码实现
弹幕获取代码如下所示:
import pandas as pd
import requests
import re
from fake_useragent import UserAgent
# 获取弹幕URL地址
url = 'https://api.bilibili.com/x/v2/dm/web/history/seg.so'
# 获取弹幕时间段
start, end = '20191031', '20210201'
date_list = (x for x in pd.date_range(start, end).strftime('%Y-%m-%d'))
def getdanmu(dates):
headers = {
'origin': 'https://www.bilibili.com',
'referer': 'https://www.bilibili.com/video/BV1YE411C76u',
'cookie': "your cookie",
'user-agent': ua.random,
}
for date in dates:
params = {
'type': 1,
'oid': '126220407',
'date': date
}
r = requests.get(url, params=params, headers=headers)
r.encoding = 'utf8'
# 提取中文内容
comments = re.findall('.*?([\u4E00-\u9FA5]+).*?', r.text)
for i in comments:
with open('./doc/danmu.txt', mode='a', encoding='utf-8') as f:
f.write(i)
f.write('\n')
ua = UserAgent()
getdanmu(date_list)
print('#######弹幕获取完成########')
上述代码中,"\u4E00-\u9FA5"为中文内容正则表达式。
为了构造真实的请求,我们需要来构造请求头中的内容,以防止网站后台把我们识别成爬虫而不能获取到弹幕信息。请求头中的内容如下图所示:
其中,最重要的是Origin、Referer、Cookie和User-Agent参数,按照截图内容,Origin和Referer我们按照截图中内容填写即可。Cookie内容是登录B站后生成的内容。如果需要爬取历史记录,则需要有B站账号,登录之后,直接复制请求头中的Cookie内容到代码中。User-Agent是表示登录使用的浏览器的信息,这里我们使用fake_useragent库中的UserAgent提供的方法来模拟浏览器登录。
运行代码后,将弹幕内容存入相应的文件,弹幕内容如图所示:
接下来,将对获取的弹幕内容进行统计分析,最终使用词云填充,并最终生成新的图片。
四. 弹幕内容处理
4.1 概念介绍
对于获取的弹幕内容,需要对弹幕内容进行处理和加工,在处理之前,需要介绍两个概念,一个是词云,一个是停用词。
词云是美国西北大学教授里奇·戈登于2006年提出。词云是通过形成"关键字云层"或"关键词渲染",对网络文本中出现频率较高的关键词的视觉上的突出。词云图过滤掉大量的文本信息,使浏览网页者只要一眼扫过文本就可以领略文本的主旨。
停用词,是指在信息检索中,为节省存储空间和提高搜索效率,在处理孜然语言数据(或文本)之前或之后会自动过滤掉某些字或词,这些字或词即被称为Stop Words(停用词)。并非有一个标准的停用词库来适用于所有的工具。
我们在对获取的弹幕内容进行检索时,需要使用到中文停用词库。我们选用一个常用的中文停用词库(链接:https://github.com/goto456/stopwords),我们使用其中的百度停用词库,将该停用词库下载下来。
4.2 实现过程
我们使用停用词库,筛选出出现频率较高的关键词,使用词云的方式填充到人体轮廓中,并最终生成新的图片。词云库我们使用wordcloud库,对中文分词,我们使用jieba库。在安装了pip之后,可以直接通过: pip install wordcloud 和 pip install jieba完成这两个库的安装。对于代码依赖的PIL库,在python中,已经修改成了pillow库,故安装的命令为:pip install pillow。对于下载的百度停用词库,我们不需要编辑操作该代码,只需要将该停用词库放置到项目工程文件夹中,在代码中使用该停用词库即可。
4.3 代码实现
代码实现如下所示:
from wordcloud import WordCloud
import collections
import jieba
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
import os
# 打开弹幕文件
with open('./doc/danmu.txt', encoding='utf-8') as f:
data = f.read()
# 对弹幕文件进行中文分词
seg_list_exact = jieba.cut(data, cut_all=False)
liststr = "/ ".join(seg_list_exact)
result_list = []
# 使用下载的百度停用词库,对中文进行分词
with open('./stop_words.txt', encoding='utf-8') as f1:
f_stop_text = f1.read()
f_stop_seg_list = f_stop_text.splitlines()
# 完成中文分词
for word in liststr.split('/'):
if not (word.strip() in f_stop_seg_list) and len(word.strip()) > 1:
result_list.append(word)
# 统计中文分词数量
word_counts = collections.Counter(result_list)
path = './wordcloud/'
# 使用词云生成新的图片
img_files = os.listdir('./mask_img1')
for num in range(0, len(img_files)):
img = fr'./mask_img1/mask_{num}.png'
mask_ = 255 - np.array(Image.open(img))
plt.figure(figsize=(8, 5), dpi=200)
my_cloud = WordCloud(background_color='black', mask=mask_, mode='RGBA',
max_words=500, font_path='simhei.ttf',).generate_from_frequencies(word_counts)
plt.imshow(my_cloud)
plt.axis('off')
word_cloud_name = path + 'wordcloud_{}.png'.format(num)
my_cloud.to_file(word_cloud_name)
上述代码中,stop_words.txt文件即为我们下载的百度停用词库。执行上述代码,生成的词云文件如下图所示:
五. 生成新的视频文件
根据上述生成的词云图片,只需要将所有的图片文件合并成一个新的视频文件即可。
代码如下所示:
import cv2
size = (1920, 1080)
fourcc = cv2.VideoWriter_fourcc('X', 'V', 'I', 'D')
videoWriter = cv2.VideoWriter('yaya.avi', fourcc, 24, size)
# 把所有的图片合成视频,每秒24帧
for i in range(0, 800):
image = fr'./wordcloud/wordcloud_{i}.png'
frame = cv2.imread(image)
frame = cv2.resize(frame, size)
videoWriter.write(frame)
videoWriter.release()
六. 添加音频信息
添加音频信息,可以再次生成最新的视频文件。需要的库主要为moviepy。
代码如下:
import moviepy.editor as mpy
my_clip = mpy.VideoFileClip('./yaya.avi')
# 截图一段音频,添加到视频中
audio_background = mpy.AudioFileClip('./feel special.mp3').subclip(6, 50)
final_clip = my_clip.set_audio(audio_background)
final_clip.write_videofile('yaya-dance.mp4')
参考内容:
https://github.com/soimort/you-get https://github.com/goto456/stopwords https://github.com/amueller/word_cloud 百度百科等