文章目錄
-
- 寫在前面
- 思路
- 技術棧
- 模型介紹
- 模型效果展示
- 核心代碼
寫在前面
當《空山新雨後》的小姐姐遇上了《你的名字》,這将會是一場怎樣的盛宴呢!
最近被《空山新雨後》瘋狂洗腦,在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使用百度自建資料集進行訓練,可用于人像分割,支援任意大小的圖檔輸入。點選檢視詳情
模型效果展示
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIyVGduV2YfNWawNCM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TVYVGcs5mYwx2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL4gzMxEDOyMjM4IDOwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
核心代碼
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)