目標
paddlehub項目鏈接:https://aistudio.baidu.com/aistudio/projectdetail/437104
PaddleHub DeepLabv3+模型(deeplabv3p_xception65_humanseg)實現一鍵摳圖
實現功能
1.實現單張小姐姐圖片的摳圖(背景替換)
2.實現對視頻的摳圖(背景替換)將紅昭願的視頻更換爲宇宙背景
實現思路
功能1:
單張圖片摳圖採用paddlehub真的是一鍵摳圖呀。
#安裝1.6的paddlehub
pip install paddlehub==1.6.0 -i https://pypi.tuna.tsinghua.edu.cn/simple -t /home/aistudio/external-libraries
import paddlehub as hub
# 待預測圖片
test_img_path = ["./data/yu.jpg"]
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
img = mpimg.imread(test_img_path[0])
# 展示待預測圖片
plt.figure(figsize=(10,10))
plt.imshow(img)
plt.axis('off')
plt.show()
待摳圖的圖片:
#加載paddlehua提供的人臉分割的模型
module = hub.Module(name="deeplabv3p_xception65_humanseg")
#paddlehub的所有輸入必須要是一個字典格式的data,並且字典裏面的內容要是一個list
input_dict = {"image": test_img_path}#傳入也必須是個字典,字典的value必須是個list
# execute predict and print the result使用模型得到結果
results = module.segmentation(data=input_dict)
for result in results:
print(result)#打印結果
# 預測結果展示
test_img_path = "./humanseg_output/yu.png"
img = mpimg.imread(test_img_path)
plt.figure(figsize=(10,10))
plt.imshow(img)
plt.axis('off')
plt.show()
摳圖結果:
可以發現,前面的街道背景已經沒有了。對於各種情況下的真人扣取都試試了一下,發現效果都挺好的。下面是模型的實現結果
模型結果對比
背景替換函數:
#圖像合成函數
from PIL import Image
import numpy as np
#blend 混合
def blend_images(fore_image, base_image,savepath):
"""
將摳出的人物圖像換背景
fore_image: 前景圖片,摳出的人物圖片 爲png格式 會多一個通道
base_image: 背景圖片
savepath:圖片要保存的路徑,圖片保存的文件名和前景圖片的名字一樣
"""
# 讀入圖片
fore_image_dir = fore_image
base_image = Image.open(base_image).convert('RGB')#如果是png可能多了一個A通道
fore_image = Image.open(fore_image).resize(base_image.size) #將前圖縮放背後景圖一樣大小
# 圖片加權合成
scope_map = np.array(fore_image)[:,:,-1] / 255
scope_map = scope_map[:,:,np.newaxis] #np.newaxis 爲 numpy.ndarray(多維數組)增加一個軸
scope_map = np.repeat(scope_map, repeats=3, axis=2)
res_image = np.multiply(scope_map, np.array(fore_image)[:,:,:3]) + np.multiply((1-scope_map), np.array(base_image))
#保存圖片
res_image = Image.fromarray(np.uint8(res_image))
save_path = os.path.join(savepath, os.path.basename(fore_image_dir))
res_image.save(save_path)
功能2:視頻換背景
實現的思路:
因爲paddlehub的模型的喂數據的問題,所有不能直接喂視頻,需要傳入一個字典,字典裏面需要是一個list的數據。
1.先讀取視頻,使用opencv按幀讀取轉爲爲圖片
# video 2 images
###step1
def extract_images(src_video, dst_dir):
'''
src_video:爲目標的視頻文件地址
dst_dir:爲視頻圖片的保存路徑
'''
video = cv2.VideoCapture(src_video)
count = 0
while True:
flag, frame = video.read()
if not flag:
break
cv2.imwrite(os.path.join(dst_dir, str(count) + '.png'), frame)
count = count + 1
print('extracted {} frames in total.'.format(count))
2.對1中最後得到的的所有圖片進行人物摳圖
###step2
def manSeg(img_path,out_dir):
# test images
test_image_list = [os.path.join(img_path, img_name) for img_name in os.listdir(img_path)]
# segment images!
input_dict = {"image": test_image_list}
module.segmentation(data=input_dict,output_dir = out_dir)
3.對2中最後得到的所有圖片與背景圖片進行合成
# blend images
def blend_vides(input_dir,base_image,save_dir):
index = 0
for img in os.listdir(input_dir):
print('總共{}張,處理到了{}張圖片'.format(len(os.listdir(input_dir)),index+1))
fore_image = os.path.join(input_dir,img)
blend_images(fore_image, base_image, save_dir)
4.將3中合成的所有圖片合成爲視頻
# image2video
#將扣圖和背景組合的圖片從新合成爲視頻(
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)
for index in range(len(os.listdir(pic_path))):
frame = cv2.imread(os.path.join(pic_path,'{}.png'.format(index)))
dst_video.write(frame)
dst_video.release()
5.給4中得到的視頻添加bgm
def add_audio(s_video_path,d_video_path):
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]+'hong_audio.mp4')
主函數:
def change_video_bk():
##1.將視頻按幀提取出圖片image_list
src_video = './work/video/hong-withaudio.mov'
dst_dir = './work/video-imgs'
checkdir(dst_dir)#如果不存在就創建,存在就清除裏面內容 check1
#function1
extract_images(src_video, dst_dir)
##2.對image_list所有的圖片進行摳圖,然後更換背景保存圖片得到processed_image_list
out_dir = './work/video-seg_out'
checkdir(out_dir) #check2
#function2.1
manSeg(dst_dir,out_dir)
base_image = './work/img/sea.jpg'
save_dir = './work/video-imgs'
checkdir(save_dir) #check3
#function2.2
blend_vides(out_dir,base_image,save_dir)
#3.將processed_image_list中的所有圖片組成爲video
save_dir = './work/video-imgs'
base_image = './work/img/sea.jpg'
result_video_dir = './work/video-result/'
checkdir(result_video_dir) #check4
result_video_name = os.path.join(result_video_dir,'hong.mp4') #最終輸出的視頻的名字
size = Image.open(base_image).size
frame_size = 25
#function3 圖片合成爲視頻
img2video(result_video_name,save_dir,size,frame_size)
#function4 給視頻加上bgm
add_audio(src_video,result_video_name)