写在前面
当《空山新雨后》的小姐姐遇上了《你的名字》,这将会是一场怎样的盛宴呢!
最近被《空山新雨后》疯狂洗脑,在B站看到小姐姐的歌伴舞后就有了给小姐姐换个背景的相法,
于是找来了你的名字的照片,让小姐姐在星空下配合着悦耳的歌翩翩起舞
项目AIstudio地址:https://aistudio.baidu.com/aistudio/projectdetail/765734
视频B站地址:https://www.bilibili.com/video/BV1QV41127gd/
思路
- 将原视频提取为1帧1帧的图片
- 对每一张图片做人物分割和背景合成两部分工作
- 将第二步得到的图片按照顺序合并成一个新的视频(注意这里的视频是没有声音的)
- 将原视频的bgm添加到新合成的视频上面
技术栈
- paddlehub
- deeplabv3p_xception65_humanseg(paddlehub提供的人物分割的模型)
- opencv
模型介绍
DeepLabv3+ 是Google DeepLab语义分割系列网络的最新作,其前作有 DeepLabv1, DeepLabv2, DeepLabv3。在最新作中,作者通过encoder-decoder进行多尺度信息的融合,同时保留了原来的空洞卷积和ASSP层, 其骨干网络使用了Xception模型,提高了语义分割的健壮性和运行速率,在 PASCAL VOC 2012 dataset取得新的state-of-art performance。该PaddleHub Module使用百度自建数据集进行训练,可用于人像分割,支持任意大小的图片输入。点击查看详情
模型效果展示
核心代码
step1: 从视频中提取帧
#从视频中提取帧
def extract_images(src_video, dst_dir):
'''
src_video:为目标的视频文件地址
dst_dir:为视频图片的保存路径
'''
video = cv2.VideoCapture(src_video)
count = 0
with tqdm(total=video.get(cv2.CAP_PROP_FRAME_COUNT)) as pbar:
while True:
flag, frame = video.read()
if not flag:
break
cv2.imwrite(os.path.join(dst_dir, str(count) + '.png'), frame)
count = count + 1
pbar.update(1)
print('extracted {} frames in total.'.format(count))
step2: 分割图像
#分割图像
def seg():
dst_img_dir = './work/images'
img_list = os.listdir(dst_img_dir)
img_list = [os.path.join(dst_img_dir,str(i)+'.png') for i in range(5085)]
print(len(img_list))
module = hub.Module(name="deeplabv3p_xception65_humanseg")
from tqdm import trange
try:
with trange(len(img_list)) as pbar:
for i in pbar:
tmp_list = []
tmp_list.append(img_list[i])
module.segmentation(data={'image':tmp_list},use_gpu=True,visualization =True,output_dir='output')
pbar.set_description("Processing %s" % img_list[i].split('/')[-1])
except KeyboardInterrupt:
pbar.close()
raise
pbar.close()
step3: 图片转换为视频
#图片转换为视频
def img2video(dst_video_path,pic_path,size,frame):
'''
dst_video_path:合成视频的保存路径(包含文件名)
pic_path:合成的所有图片的路径
size:图片的大小,即是视频的大小
frame:帧率
VideoWriter_fourcc为视频编解码器
fourcc意为四字符代码(Four-Character Codes),顾名思义,该编码由四个字符组成,下面是VideoWriter_fourcc对象一些常用的参数,注意:字符顺序不能弄混
cv2.VideoWriter_fourcc('I', '4', '2', '0'),该参数是YUV编码类型,文件名后缀为.avi
cv2.VideoWriter_fourcc('P', 'I', 'M', 'I'),该参数是MPEG-1编码类型,文件名后缀为.avi
cv2.VideoWriter_fourcc('X', 'V', 'I', 'D'),该参数是MPEG-4编码类型,文件名后缀为.avi
cv2.VideoWriter_fourcc('T', 'H', 'E', 'O'),该参数是Ogg Vorbis,文件名后缀为.ogv
cv2.VideoWriter_fourcc('F', 'L', 'V', '1'),该参数是Flash视频,文件名后缀为.flv
cv2.VideoWriter_fourcc('m', 'p', '4', 'v') 文件名后缀为.mp4
'''
dst_video = cv2.VideoWriter(dst_video_path, cv2.VideoWriter_fourcc(*'mp4v'), frame, size, True)
try:
with trange(len(os.listdir(pic_path))) as pbar:
for index in pbar:
frame = cv2.imread(os.path.join(pic_path,'{}.png'.format(index)))
dst_video.write(frame)
pbar.set_description("Processing %s.png" % index)
except KeyboardInterrupt:
pbar.close()
raise
pbar.close()
dst_video.release()
step4: 添加bgm
#给视频加声音
def add_audio(s_video_path,d_video_path,name):
'''
给视频加声音
:param s_video_path: 原视频地址-含有声音的
:param d_video_path: 目的视频地址-需要加声音的
:return:
'''
video_s = VideoFileClip(s_video_path)
video_d = VideoFileClip(d_video_path)
audio_o = video_s.audio
video_dd = video_d.set_audio(audio_o)
video_dd.write_videofile(d_video_path[0:d_video_path.rfind('/')+1]+name)