作者 | ZackSock

来源 | ZackSock(ID:ZackSock)

Python牛已经不是一天两天的事了,但是我开始也没想到,Python能这么牛。前段时间接触了一个批量抠图的模型库,而后在一些视频中找到灵感,觉得应该可以通过抠图的方式,给视频换一个不同的场景,于是就有了今天的文章。

我们先看看能实现什么效果,先来个正常版的,先看看原场景:

下面是我们切换场景后的样子:

看起来效果还是不错的,有了这个我们就可以随意切换场景,坟头蹦迪不是梦。另外,我们再来看看另外一种效果,相比之下要狂放许多:

实现步骤

我们都知道,视频是由一帧一帧的画面组成的,每一帧都是一张图片,我们要实现对视频的修改就需要对视频中每一帧画面进行修改。所以在最开始,我们需要获取视频每一帧画面。

在我们获取帧之后,需要抠取画面中的人物。

抠取人物之后,就需要读取我们的场景图片了,在上面的例子中背景都是静态的,所以我们只需要读取一次场景。在读取场景之后我们切换每一帧画面的场景,并写入新的视频。

这时候我们只是生成了一个视频,我们还需要添加音频。而音频就是我们的原视频中的音频,我们读取音频,并给新视频设置音频就好了。

具体步骤如下:

  1. 读取视频,获取每一帧画面
  2. 批量抠图
  3. 读取场景图片
  4. 对每一帧画面进行场景切换
  5. 写入视频
  6. 读取原视频的音频
  7. 给新视频设置音频

因为上面的步骤还是比较耗时的,所以在视频完成后通过邮箱发送通知,告诉我视频制作完成。

模块安装

我们需要使用到的模块主要有如下几个:

pillowopencvmoviepypaddlehub

我们都可以直接用pip安装:

pip install pillowpip install opencv-pythonpip install moviepy

其中OpenCV有一些适配问题,建议选取3.0以上版本。

在我们使用paddlehub之前,我们需要安装paddlepaddle:具体安装步骤可以参见官网。用paddlehub抠图参考:别再自己抠图了,Python用5行代码实现批量抠图。我们这里直接用pip安装cpu版本的:

# 安装paddlepaddlepython -m pip install paddlepaddle -i https://mirror.baidu.com/pypi/simple# 安装paddlehubpip install -i https://mirror.baidu.com/pypi/simple paddlehub

有了这些准备工作就可以开始我们功能的实现了。

具体实现

我们导入如下包:

import cv2 # opencvimport mail # 自定义包,用于发邮件import mathimport numpy as npfrom PIL import Image # pillowimport paddlehub as hubfrom moviepy.editor import *

其中Pillow和opencv导入的名称不太一样,还有就是我自定义的mail模块。另外我们还要先准备一些路径:

# 当前项目根目录,系统自动获取当前目录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 + 'esult.mp4'

接下来我们按照上面说的步骤一个一个实现。

(1)读取视频,获取每一帧画面

OpenCV中提供了读取帧的函数,我们只需要使用VideoCapture类读取视频,然后调用read函数读取帧,read方法返回两个参数,ret为是否有下一帧,frame为当前帧的ndarray对象。完整代码如下:

def getFrame(video_name, save_path):"""读取视频将视频逐帧保存为图片,并返回视频的分辨率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.readwhile ret:cv2.imwrite(save_path + str(name) + '.jpg', frame)ret, frame = video.readname += 1video.releasereturn fps, size

在标?处,我获取了帧的总数,然后通过如下公式获取比帧数大的整十整百的数:

frame_name = math.pow(10, len(frame_num))

这样做是为了让画面逐帧排序,这样读取的时候就不会乱。另外我们获取了视频的帧率和分辨率,这两个参数在我们创建视频时需要用到。这里需要注意的是opencv3.0以下版本获取帧率和画面大小的写法有些许差别。

(2)批量抠图

批量抠图需要用到paddlehub中的模型库,代码很简单,这里就不多说了:

def getHumanseg(frames):"""对帧图片进行批量抠图:param frames: 帧的路径:return:"""# 加载模型库humanseg = hub.Module(name='deeplabv3p_xception65_humanseg')# 准备文件列表files = [frames + i for i in os.listdir(frames)]# 抠图humanseg.segmentation(data={'image': files})

我们执行上面函数后会在项目下生成一个humanseg_output目录,抠好的图片就在里面。

(3)读取场景图片

这也是简单的图片读取,我们使用pillow中的Image对象:

def readBg(bgname, size):"""读取背景图片,并修改尺寸:param bgname: 背景图片名称:param size: 视频分辨率:return: Image对象"""im = Image.open(bgname)return im.resize(size)

这里的返回的对象并非ndarray对象,而是Pillow中定义的类对象。

(4)对每一帧画面进行场景切换

简单来说就是将抠好的图片和背景图片合并,我们知道抠好的图片都在humanseg_output目录,这也就是为什么最开始要准备相应的变量存储该目录的原因:

def setImageBg(humanseg, bg_im):"""将抠好的图和背景图片合并: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]

在标?处,我们复制了背景,如果少了这一步的话,生成的就是我们上面的“千手观音效果”了。

其它步骤都很好理解,只有返回值比较长,我们来详细看一下:

# 将合成图转换成RGB,这样A通道就没了bg_im = bg_im.convert('RGB')# 将Image对象转换成ndarray对象,方便opencv读取im_array = np.array(bg_im)# 此时im_array为rgb模式,而OpenCV为bgr模式,我们通过下面语句将rgb转换成bgrbgr_im_array = im_array[:, :, ::-1]

最后bgr_im_array就是我们最终的返回结果。

(5)写入视频

为了节约空间,我并非等将写入图片放在合并场景后面,而是边合并场景边写入视频:

def writeVideo(humanseg, bg_im, fps, size):""":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

上面的代码也非常简单,执行完成后项目下会生成一个green.mp4,这是一个没有音频的视频,后面就需要我们获取音频然后混流了。

(6)读取原视频的音频

因为在opencv中没找到音频相关的处理,所以选用moviepy,使用起来也非常方便:

def getMusic(video_name):"""获取指定视频的音频:param video_name: 视频名称:return: 音频对象"""# 读取视频文件video = VideoFileClip(video_name)# 返回音频return video.audio

然后就是混流了。

(7)给新视频设置音频

这里同样使用moviepy,传入视频名称和音频对象进行混流:

def addMusic(video_name, audio):"""实现混流,给video_name添加音频"""# 读取视频video = VideoFileClip(video_name)# 设置视频的音频video = video.set_audio(audio)# 保存新的视频文件video.write_videofile(output_video)

其中output_video是我们在最开始定义的变量。

(8)删除过渡文件

在我们生产视频时,会产生许多过渡文件,在视频合成后我们将它们删除:

def deleteTransitionalFiles:"""删除过渡文件"""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)整合

我们将上面完整的流程合并成一个函数:

def changeVideoScene(video_name, bgname):""":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中调用

我们可以把前面定义的路径也放进了:

if __name__ == '__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 + 'esult.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__)

这样我们就完成了完整的流程。

发送邮件

邮件的发送又是属于另外的内容了,我定义了一个mail.py文件,具体代码如下:

import smtplibfrom email.mime.text import MIMETextfrom email.mime.multipart import MIMEMultipart # 一封邮件def sendMail(msg):#sender = '发件人'to_list = ['收件人']subject = '视频制作情况'# 创建邮箱em = MIMEMultipartem['subject'] = subjectem['From'] = senderem['To'] = 

python合并视频和音频_真没想到,Python 还能实现 5 毛特效相关推荐

  1. python做动态课件素材_万万没想到,还能这么玩!用 Python 生成动态 PPT

    在工作的过程中,我们会发现那些能够把知识.成果讲透的人很多都会做动态图表. 这篇文章就介绍了 Python 中一种简单的动态图表制作方法,这样生成的动图就可以丰富我们的PPT啦~ 数据暴增的年代,数据 ...

  2. python可以做特效吗_真没想到,Python还能实现5毛特效

    来源 | ZackSock(ID:ZackSock) 图源 | 视觉中国 Python牛已经不是一天两天的事了,但是我开始也没想到,Python能这么牛.前段时间接触了一个批量抠图的模型库,而后在一些 ...

  3. python合并视频和音频_FFMpeg无损合并视频的多种方法 python | 猴头客

    方法二确实可行 方法1 我试着不行 需要的话自己试试把 python 的调用cmd命令是这么写的 res=os.system("ifconfig"); 众所周知,从某些视频网站下载 ...

  4. python合并视频和音频_Python做图像处理及视频音频文件分离和合成功能

    一.简介 Imageio是一个Python库,提供了一个简单的界面来读取和写入各种图像数据,包括动画图像,视频,体积数据和科学格式.它是跨平台的,运行在Python 2.7和3.4+上,易于安装. 作 ...

  5. ImageMagick将多张图片拼接成一张图片_真没想到!照片加文字和照片拼接,微信就能做到,太方便了...

    照片加文字以及照片拼接,用微信就能做到,太方便了 相信大家在端午佳节的时候,都拍摄了很多照片,分享到朋友圈,但是拍摄的图片太多,微信朋友圈只能发9张. 今天就来教大家如何用微信就照片添加文字和拼接长图 ...

  6. python实现视频ai换脸_超简单使用Python换脸实例

    换脸! 这段时间,deepfakes搞得火热,比方说把<射雕英雄传>里的朱茵换成了杨幂,看下面的图!毫无违和感! 其实早在之前,基于AI换脸的技术就得到了应用,比方说<速度与激情7& ...

  7. python识别视频中火焰_基于yolov3和python框架的火焰识别检测算法

    本算法识别的效果如下:有兴趣学习交流python 编程的伙伴可加群:1026352781 下面开始实际操作啦 一.配置环境 算法所需环境如下: Python: 3.7.4 Tensorflow-GPU ...

  8. python实现视频ai换脸_【原创】python实现视频内的face swap(换脸)

    ruby中excel简单操作以及文件读取操作方法 # -*-coding:utf-8 -*-#author:kanlijunrequire 'win32ole'require 'fileutils'c ...

  9. char转成string_真没想到,一个小小的String居然还有这么多窍门?

    推荐学习 公司来了位阿里P8大神,看完他的手写"Kafka笔记",万分膜拜 牛掰!"基础-中级-高级"Java程序员面试集结,看完献出我的膝盖 真没想到,一个小 ...

最新文章

  1. RStudio启动后修改文件(数据)读取默认目录
  2. 拼接图像亮度均匀调整_液晶拼接屏如何才能达到更好的显示效果
  3. Netlink 介绍(译)
  4. 如何优雅的实现pandas DataFrame 和spark dataFrame 相互转换
  5. JavaScript实现topologicalSort拓扑排序算法(附完整源码)
  6. 基于决策树的多分类_R中基于决策树的糖尿病分类—一个零博客
  7. 解决:VMware DHCP Service 服务无法启动
  8. Linux系统编程11:进程入门之详细阐述进程的一些状态区分僵尸状态和孤儿状态动图演示
  9. elasticsearch java api查询
  10. GridView中合并单元格
  11. C++编译原理 (转载)
  12. [转]JS对JSON的操作总结
  13. mysql char 二进制_SQL:char 和 varchar、binary 和 varbinary、二进制字符串、严格模式、汉字编码方式...
  14. Spring后端接收表单方式上传的文件和数据
  15. 如何禁用GeForce Experience的奖励广告
  16. 关于o1,o2,o3
  17. 数澜、宜信、贝壳三种数据中台建设模式探讨 | 数澜科技
  18. 低调的华丽:从服务器开发的角度认识 asp.net 中的回调技术
  19. 5款良心电脑软件,有一款虽已停更,却依然免费使用
  20. Mahony算法 AHRS系统

热门文章

  1. Leetcode--397. 整数替换
  2. linux服务器配置与管理_服务器维护实施步骤
  3. 服务器热修复,热修复探究,hotfix,patch
  4. android 2个界面抽屉,Android使用DrawerLayout创建左右两个抽屉菜单
  5. 算子基本思想_2.2 量子力学基本假设 Part 2
  6. python科学计算三剑客_机器学习三剑客之Numpy
  7. c++ map初始化同时赋值_Golang入门教程——map篇
  8. 6-4 二叉树的非递归遍历 (25分)_本周小结!(二叉树)
  9. pytorch将label转为one hot形式
  10. pytorch实现常用的一些即插即用模块(长期更新)