小言_互联网的博客

基于百度PaddleHub实现一键抠图+视频合成

291人阅读  评论(0)

利用百度 PaddlePaddle 平台的 PaddleHub 工具实现一键抠图,结合 opencv 等工具进行视频合成创作。

本视频为2.0版本,相比于第一版,自认为还是有所提升的。。。。。

1.0版本链接如下:
AI人像抠图及视频合成:让你体验复仇者联盟的终局之战

全套代码:

# 首先安装所需的包
# pip install paddlehub==1.6.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
# pip install moviepy -i https://pypi.tuna.tsinghua.edu.cn/simple
# 这里指定从清华源下载,速度快


import cv2
from PIL import Image
import numpy as np
import os
import paddlehub as hub
from moviepy.editor import *

# 从视频提取图片
def video2img(video_path, out_path):
    cap = cv2.VideoCapture(video_path)
    i=1
    while True:
        ret, frame = cap.read()
        if frame is None:
            break
        else:
            cv2.imwrite(out_path + str(i) + ".jpg", frame)
            i+=1
    return

# 抠图
def img2seg(img_path, out_path):
    # load model
    module = hub.Module(name="deeplabv3p_xception65_humanseg")
    # config
    test_img_path = [os.path.join(img_path, f_name) for f_name in os.listdir(img_path)]
    input_dict = {"image": test_img_path}

    results = module.segmentation(data=input_dict, output_dir=out_path)

# 合成图片
def blend_images(fore_image, base_image, out_path, img_num):
    # def blend_images(fore_image, base_image):
    """
    将抠出的人物图像换背景
    fore_image: 前景图片,抠出的人物图片
    base_image: 背景图片
    """
    # 读入图片
    base_image = Image.open(base_image).convert('RGB')
    fore_image = Image.open(fore_image).resize(base_image.size)

    # 图片加权合成
    scope_map = np.array(fore_image)[:, :, -1] / 255
    scope_map = scope_map[:, :, np.newaxis]
    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))
    f_name = str(img_num) + ".jpg"
    res_image.save(os.path.join(out_path, f_name))

# 把图片合成视频
def img2video(img_path, org_video_path, out_path):
    # 查看原始视频的参数
    cap = cv2.VideoCapture(org_video_path)
    ret, frame = cap.read()
    height = frame.shape[0]
    width = frame.shape[1]
    fps = cap.get(cv2.CAP_PROP_FPS)  # 返回视频的fps--帧率

    # 把参数用到我们要创建的视频上
    video = cv2.VideoWriter(out_path, cv2.VideoWriter_fourcc('m', 'p', '4', 'v'), fps, (width, height))  # 创建视频流对象
    """
    参数1 即将保存的文件路径
    参数2 VideoWriter_fourcc为视频编解码器 cv2.VideoWriter_fourcc('m', 'p', '4', 'v') 文件名后缀为.mp4
    参数3 为帧播放速率
    参数4 (width,height)为视频帧大小
    """

    # 获取图片总数
    file_list = os.listdir(img_path)
    img_num = len(file_list)

    for i in range(img_num):
        f_name = str(i + 1) + '.jpg'
        item = os.path.join(img_path, f_name)
        img = cv2.imread(item)  # 使用opencv读取图像,直接返回numpy.ndarray 对象,通道顺序为BGR ,注意是BGR,通道值默认范围0-255。
        video.write(img)  # 把图片写进视频
    video.release()  # 释放

# 从原始视频上提取声音合成到新生成的视频上
def sound2video(org_video_path, new_video_path, out_video_path):
    # 读取原始视频
    video_o = VideoFileClip(org_video_path)
    # 获取原始视频的音频部分
    audio_o = video_o.audio

    # 读取新生成视频
    video_clip = VideoFileClip(new_video_path)
    # 指向新生成视频的音频部分
    video_clip2 = video_clip.set_audio(audio_o)
    # 修改音频部分并输出最终视频
    video_clip2.write_videofile(out_video_path)

# Config
# 人物视频地址
human_video_path = './a.mp4'
# 背景视频地址
back_video_path = './b.mp4'
# 提取人物视频图像的存放地址
human_video_img_path = './human_video_img/'
# 提取背景视频图片的存放地址
back_video_img_path = './back_video_img/'
# 抠图后存放地址
human_img_seg_path = './human_img_seg/'
# 人物和背景合成后图片存放地址
blend_img_path = './blend_img/'
# 合成视频存放地址
img2video_path = './c.mp4'
# 添加声音后的视频最终输出地址
out_video_path = './d.mp4'

if __name__ == "__main__":

    # 第一步:人物视频->人物图像
    if not os.path.exists(human_video_img_path):
        os.mkdir(human_video_img_path)
    video2img(video_path=human_video_path, out_path=human_video_img_path)

    # 第二步:抠图
    if not os.path.exists(human_img_seg_path):
        os.mkdir(human_img_seg_path)
    img2seg(img_path=human_video_img_path, out_path=human_img_seg_path)

    # 第三步:背景视频->背景图片
    if not os.path.exists(back_video_img_path):
        os.mkdir(back_video_img_path)
    video2img(video_path=back_video_path, out_path=back_video_img_path)

    # 第四步:抠图+背景图片->合成
    if not os.path.exists(blend_img_path):
        os.mkdir(blend_img_path)
    # 由于人物视频长度和背景视频长度可能不同,所以要以短的为准
    if len(os.listdir(human_img_seg_path)) < len(os.listdir(back_video_img_path)):
        img_num = len(os.listdir(human_img_seg_path))
    else:
        img_num = len(os.listdir(back_video_img_path))
    # 对所有图片按顺序合成
    for i in range(img_num):
        blend_images(base_image=back_video_img_path+str(i+1)+'.jpg', fore_image=human_img_seg_path+str(i+1)+'.png',
                         out_path=blend_img_path, img_num=i+1)

    # 第五步:合成视频
    if not os.path.exists(img2video_path):
        img2video(img_path=blend_img_path, org_video_path=human_video_path, out_path=img2video_path)
    else:
        print('视频已存在,请查看输出路径')

    # 第六步:加上音频
    if not os.path.exists(out_video_path):
        sound2video(org_video_path=human_video_path, new_video_path=img2video_path, out_video_path=out_video_path)
    else:
        print('最终视频已存在,请查看输出路径')

成果展示:

Bilibili 的视频合成地址:

基于百度PaddleHub实现一键抠图+视频合成(附代码)

欢迎留言交流!


转载:https://blog.csdn.net/qq_42067550/article/details/105457679
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场