提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
视频倒放涉及到视频抓帧、图像转换、视频合成等技术,这三种方法不仅可以让我们熟练运用python语法和库,还有助于我们对图像处理有进一步的认知。
作为MJ的忠实粉丝,小编情不自禁地选取了他的一段舞蹈视频。
永远的MJ
视频抓帧主要是调用opencv库的方法,获取视频文件内容再按一帧为单位截取视频中每一帧的图片,最终将这些图片保存起来,保存的图片文件名按照先后顺序用数字标号。
帧:frame,帧率:fps(frame per second)表示一个视频每秒的帧数。
代码如下:
import cv2 #pip install opencv-python
# 定义保存图片函数
# image:要保存的图片
# pic_address:图片保存地址
# num: 图片后缀名,用于区分图片,int 类型
def save_image(image, address, num):
pic_address = address + str(num) + '.jpg'
cv2.imwrite(pic_address, image)
def video_to_pic(video_path, save_path, frame_rate):
# 读取视频文件
videoCapture = cv2.VideoCapture(video_path)
j = 0
i = 0
# 读帧
success, frame = videoCapture.read()
while success:
i = i + 1
# 每隔固定帧保存一张图片
if i % frame_rate == 0:
j = j + 1
save_image(frame, save_path, j)
print('图片保存地址:', save_path + str(j) + '.jpg')
success, frame = videoCapture.read()
if __name__ == '__main__':
# 视频文件和图片保存地址
SAMPLE_VIDEO = './video/test.mp4'
SAVE_PATH = './img/'
# 设置固定帧率
FRAME_RATE = 1
video_to_pic(SAMPLE_VIDEO, SAVE_PATH, FRAME_RATE)
在部分视频抓帧的过程中,按帧截取的图片可能是倒置的,我们可以调用pillow库中的方法,将文件夹中的每一张图片摆正。小编选择的视频在按帧截取的时候并未出现图片倒置的情况,因此,这里我不做演示。
代码如下:
import PIL.Image as img
import os
path_old = "./img/"
path_new = "./img/"
filelist = os.listdir(path_old)
total_num = len(filelist)
print(total_num)
for i in range(1,total_num + 1):
im = img.open(path_old + str(i) + ".jpg")
ng = im.transpose(img.ROTATE_180) #旋转 180 度角。
#ng = im.transpose(img.FLIP_LEFT_RIGHT) #左右对换。
# ng = im.transpose(img.FLIP_TOP_BOTTOM) # 上下对换。
ng.save(path_new + str(i) +'.jpg')
if i%20 == 0:
print(i)
print(i)
图像文件倒序其实并不难,就是利用python读取文件夹里的每一个图片文件,然后根据文件名的数字序列进行倒序存储。由于os库中open方法无法打开过多文件,所以我们选取视频当中一部分进行倒放。
选取第2001到第8190张图片,然后拼接成无声的视频part_test.mp4。
part_test代码如下:
import cv2
import os
# 保存的视频路径及视频size(1920, 1080)
writer = cv2.VideoWriter('./video/part_test.mp4', cv2.VideoWriter_fourcc('m', 'p', '4', 'v'), 60, (1920, 1080), True)
# **********设置帧的数量**********
# total_frame = len(os.listdir('./img_rev/'))
# print(total_frame)
for frame_num in range(2001,3319 + 1):
img_path = './img/%d.jpg' % frame_num #图片路径
read_img = cv2.imread(img_path)
writer.write(read_img)
writer.release()
截取的图片生成视频如下。
part_test
接着将这些图片倒序
代码如下:
import PIL.Image as img
import os
path_old = "./img/"
path_new = "./img_rev/"
filelist = os.listdir(path_old)
# total_num = len(filelist)
# print(total_num)
new_imgs = [] #初始化一个新的列表,用来存放图像名称
for i in range(2001,3425 + 1):
im = img.open(path_old + str(i) + ".jpg")
new_imgs.append(im)
new_imgs.reverse() #列表倒序
for i,j in enumerate(new_imgs):
j.save(path_new + str(i+1) + '.jpg')
if i%20 == 0:
print(i)
print(i)
使用opencv库中的VideoWriter方法,将倒序完毕的图片按照固定的帧数拼接成想要倒放的视频。
代码如下:
import cv2
import os
# 保存的视频路径及视频size(1920, 1080)
writer = cv2.VideoWriter('./video/reversed_test.mp4', cv2.VideoWriter_fourcc('m', 'p', '4', 'v'), 60, (1920, 1080), True)
# **********设置帧的数量**********
total_frame = len(os.listdir('./img_rev/'))
print(total_frame)
for frame_num in range(1,total_frame + 1):
img_path = './img_rev/%d.jpg' % frame_num #图片路径
read_img = cv2.imread(img_path)
writer.write(read_img)
writer.release()
倒序的图片生成视频如下。
reversed_test
动态图倒放和视频倒放原理基本上是一样的,接下来我们先试着将几张不同的图片拼接起来合成一个动态图。
小编选择了一些《狂飙》的海报,一共十张图片,图片尺寸也不完全相同。
代码如下:
from PIL import Image
im = Image.open("../../data/img/1.jpg")
images = []
images.append(Image.open('../../data/img/2.jpg'))
images.append(Image.open('../../data/img/3.jpg'))
images.append(Image.open('../../data/img/4.jpg'))
images.append(Image.open('../../data/img/5.jpg'))
images.append(Image.open('../../data/img/6.jpg'))
images.append(Image.open('../../data/img/7.jpg'))
images.append(Image.open('../../data/img/8.jpg'))
images.append(Image.open('../../data/img/9.jpg'))
images.append(Image.open('../../data/img/10.jpg'))
im.save('gif.gif', save_all=True, append_images=images, loop=1, duration=1, comment=b"aaabb")
合成的gif动态图如下图所示:
gif
我们会发现,合成的gif图片,是从第一张图片依次往后覆盖的。也就是说,动态图的合成不会因为图片尺寸不同而失败,这是与视频合成最明显的区别。
代码如下:
from PIL import Image, ImageSequence
# 读取 GIF
im = Image.open("../../data/gif/ergouzi.gif")
# GIF 图片流的迭代器
iter = ImageSequence.Iterator(im)
i = 1 #索引/位置
# 遍历图片流的每一帧
for frame in iter:
print("image %d: mode %s, size %s" % (i, frame.mode, frame.size))
frame.save("../../data/gif/%d.png" % i)
i += 1
# 把 GIF 拆分为图片流
imgs = [frame.copy() for frame in ImageSequence.Iterator(im)]
# 图片流反序
imgs.reverse() #reverse()函数/方法,是用来将列表/数组/元组/集合/字典等进行倒序的
# 将反序后的所有帧图像保存下来
imgs[0].save("../../data/gif/reverse.gif", save_all=True, append_images=imgs[1:])
图片流部分展示:
倒放后的动态图如下:
关于视频倒放任务到目前为止只涉及到图像处理的部分,关于视频中音频提取与音频倒放的部分将在下一期呈现给大家。以上内容仅供参考,如果大家有其他突发奇想的方法,敬请在评论区一同探讨!