其他
真没想到,Python 还能实现 5 毛特效
The following article is from ZackSock Author ZackSock
下面是我们切换场景后的样子:
看起来效果还是不错的,有了这个我们就可以随意切换场景,坟头蹦迪不是梦。另外,我们再来看看另外一种效果,相比之下要狂放许多:
实现步骤
读取视频,获取每一帧画面 批量抠图 读取场景图片 对每一帧画面进行场景切换 写入视频 读取原视频的音频 给新视频设置音频
模块安装
opencv
moviepy
paddlehub
pip install opencv-python
pip install moviepy
python -m pip install paddlepaddle -i https://mirror.baidu.com/pypi/simple
# 安装paddlehub
pip install -i https://mirror.baidu.com/pypi/simple paddlehub
具体实现
import mail # 自定义包,用于发邮件
import math
import numpy as np
from PIL import Image # pillow
import paddlehub as hub
from moviepy.editor import *
BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "."))
# 每一帧画面保存的地址
frame_path = BASE_DIR + '\\frames\\'
# 抠好的图片位置
humanseg_path = BASE_DIR + '\\humanseg_output\\'
# 最终视频的保存路径
output_video = BASE_DIR + '\\result.mp4'
(1)读取视频,获取每一帧画面
"""
读取视频将视频逐帧保存为图片,并返回视频的分辨率size和帧率fps
:param video_name: 视频的名称
:param save_path: 保存的路径
:return: fps帧率,size分辨率
"""
# 读取视频
video = cv2.VideoCapture(video_name)
# 获取视频帧率
fps = video.get(cv2.CAP_PROP_FPS)
# 获取画面大小
width = int(video.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))
size = (width, height)
# 🦍获取帧数,用于给图片命名
frame_num = str(video.get(7))
name = int(math.pow(10, len(frame_num)))
# 读取帧,ret为是否还有下一帧,frame为当前帧的ndarray对象
ret, frame = video.read()
while ret:
cv2.imwrite(save_path + str(name) + '.jpg', frame)
ret, frame = video.read()
name += 1
video.release()
return fps, size
(2)批量抠图
"""
对帧图片进行批量抠图
:param frames: 帧的路径
:return:
"""
# 加载模型库
humanseg = hub.Module(name='deeplabv3p_xception65_humanseg')
# 准备文件列表
files = [frames + i for i in os.listdir(frames)]
# 抠图
humanseg.segmentation(data={'image': files})
(3)读取场景图片
"""
读取背景图片,并修改尺寸
:param bgname: 背景图片名称
:param size: 视频分辨率
:return: Image对象
"""
im = Image.open(bgname)
return im.resize(size)
(4)对每一帧画面进行场景切换
"""
将抠好的图和背景图片合并
:param humanseg: 抠好的图
:param bg_im: 背景图片,这里和readBg()函数返回的类型一样
:return: 合成图的ndarray对象
"""
# 读取透明图片
im = Image.open(humanseg)
# 分离色道
r, g, b, a = im.split()
# 🦍复制背景,以免源背景被修改
bg_im = bg_im.copy()
# 合并图片
bg_im.paste(im, (0, 0), mask=a)
return np.array(bg_im.convert('RGB'))[:, :, ::-1]
bg_im = bg_im.convert('RGB')
# 将Image对象转换成ndarray对象,方便opencv读取
im_array = np.array(bg_im)
# 此时im_array为rgb模式,而OpenCV为bgr模式,我们通过下面语句将rgb转换成bgr
bgr_im_array = im_array[:, :, ::-1]
(5)写入视频
"""
:param humanseg: png图片的路径
:param bgname: 背景图片
:param fps: 帧率
:param size: 分辨率
:return:
"""
# 写入视频
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter('green.mp4', fourcc, fps, size)
# 将每一帧设置背景
files = [humanseg + i for i in os.listdir(humanseg)]
for file in files:
# 循环合并图片
im_array = setImageBg(file, bg_im)
# 逐帧写入视频
out.write(im_array)
out.release()
(6)读取原视频的音频
"""
获取指定视频的音频
:param video_name: 视频名称
:return: 音频对象
"""
# 读取视频文件
video = VideoFileClip(video_name)
# 返回音频
return video.audio
(7)给新视频设置音频
"""实现混流,给video_name添加音频"""
# 读取视频
video = VideoFileClip(video_name)
# 设置视频的音频
video = video.set_audio(audio)
# 保存新的视频文件
video.write_videofile(output_video)
(8)删除过渡文件
"""删除过渡文件"""
frames = [frame_path + i for i in os.listdir(frame_path)]
humansegs = [humanseg_path + i for i in os.listdir(humanseg_path)]
for frame in frames:
os.remove(frame)
for humanseg in humansegs:
os.remove(humanseg)
(8)整合
"""
:param video_name: 视频的文件
:param bgname: 背景图片
:return:
"""
# 读取视频中每一帧画面
fps, size = getFrame(video_name, frame_path)
# 批量抠图
getHumanseg(frame_path)
# 读取背景图片
bg_im = readBg(bgname, size)
# 将画面一帧帧写入视频
writeVideo(humanseg_path, bg_im, fps, size)
# 混流
addMusic('green.mp4', getMusic(video_name))
# 删除过渡文件
deleteTransitionalFiles()
(9)在main中调用
# 当前项目根目录
BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "."))
# 每一帧画面保存的地址
frame_path = BASE_DIR + '\\frames\\'
# 抠好的图片位置
humanseg_path = BASE_DIR + '\\humanseg_output\\'
# 最终视频的保存路径
output_video = BASE_DIR + '\\result.mp4'
if not os.path.exists(frame_path):
os.makedirs(frame_path)
try:
# 调用函数制作视频
changeVideoScene('jljt.mp4', 'bg.jpg')
# 当制作完成发送邮箱
mail.sendMail('你的视频已经制作完成')
except Exception as e:
# 当发生错误,发送错误信息
mail.sendMail('在制作过程中遇到了问题' + e.__str__())
发送邮件
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart # 一封邮件
def sendMail(msg):
#
sender = '发件人'
to_list = [
'收件人'
]
subject = '视频制作情况'
# 创建邮箱
em = MIMEMultipart()
em['subject'] = subject
em['From'] = sender
em['To'] = ",".join(to_list)
# 邮件的内容
content = MIMEText(msg)
em.attach(content)
# 发送邮件
# 1、连接服务器
smtp = smtplib.SMTP()
smtp.connect('smtp.163.com')
# 2、登录
smtp.login(sender, '你的密码或者授权码')
# 3、发邮件
smtp.send_message(em)
# 4、关闭连接
smtp.close()
总结
更多精彩推荐
☞用5G或需要换SIM卡;微软将放弃对32位Windows 10系统支持;TypeScript 3.9发布|极客头条
☞调查了 17,000 多位程序员,当前的云原生开发现状究竟如何?