利用OpenCV制作简易视频动态模板

Sep 29,2018   2324 words   9 min


前几天在看CCTV9纪录频道的时候,看到一个印象比较深刻的过场预告动画,如下视频所示(可能需要缓冲一会,有点慢)。

感觉挺好看的,作为程序员的毛病,就想着能不能自己实现个差不多类似的效果(当然没有这么好看了),所以便有了这篇博客。 简单分析一下这段动画,其实是有多个视频播放窗口,各窗口之间存在一定的时差,从而造成了这样的效果。 所以可以采用OpenCV对一段视频进行读取,然后处理,即可获得类似的效果。 废话不多说,直接上代码如下。

1.代码

# coding=utf-8
import cv2
import numpy as np

# 读取多少帧
RANGE = 500
# 不同窗口之间相差的帧数
INTERVAL = 4
# 窗口个数
VIDEO_NUM = 8
# 窗口间X方向偏移量
OFFSET_X = 25
# 窗口间Y方向偏移量
OFFSET_Y = 25
# 原视频X方向缩放比例
SCALE_RATIO_X = 0.3
# 原视频Y方向缩放比例
SCALE_RATIO_Y = 0.3

# Step 1 打开、读取视频
cap = cv2.VideoCapture("F:\\video.mp4")
fps = cap.get(5)

frames = []
for i in range(RANGE):
    cap.set(cv2.CAP_PROP_POS_FRAMES, i)
    ret, frame = cap.read()
    if frame is None:
        continue
    else:
        resize_img = cv2.resize(frame, None, fx=SCALE_RATIO_X, fy=SCALE_RATIO_Y, interpolation=cv2.INTER_CUBIC)
        frames.append((resize_img[:, :, 0], resize_img[:, :, 1], resize_img[:, :, 2]))
        print ((i + 1.0) / RANGE) * 100, "%"
cap.release()

# 解析视频
video = []
for num in range(VIDEO_NUM):
    temp_f = []
    for i in range(num * INTERVAL, frames.__len__() - INTERVAL * (VIDEO_NUM - num)):
        frame = frames[i]
        temp_f.append(frame)
        print temp_f.__len__()
    video.append(temp_f)

# 逐帧渲染
imgs_join = []
new_height = int(video[0][0][0].shape[0] + (VIDEO_NUM + 1) * OFFSET_Y)
new_width = int(video[0][0][0].shape[1] + (VIDEO_NUM + 1) * OFFSET_X)
old_height = video[0][0][0].shape[0]
old_width = video[0][0][0].shape[1]

for i in range(video[0].__len__()):
    join = np.zeros([new_height, new_width, 3], np.uint8) + 255
    for num in range(video.__len__()):
        join[-old_height - (VIDEO_NUM - num) * OFFSET_Y:-(VIDEO_NUM - num) * OFFSET_Y,
        -old_width - (VIDEO_NUM - num) * OFFSET_X:-(VIDEO_NUM - num) * OFFSET_X,
        0] = video[num][i][0]
        join[-old_height - (VIDEO_NUM - num) * OFFSET_Y:-(VIDEO_NUM - num) * OFFSET_Y,
        -old_width - (VIDEO_NUM - num) * OFFSET_X:-(VIDEO_NUM - num) * OFFSET_X,
        1] = video[num][i][1]
        join[-old_height - (VIDEO_NUM - num) * OFFSET_Y:-(VIDEO_NUM - num) * OFFSET_Y,
        -old_width - (VIDEO_NUM - num) * OFFSET_X:-(VIDEO_NUM - num) * OFFSET_X,
        2] = video[num][i][2]
    imgs_join.append(join)

# 输出视频
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter("output.avi", fourcc, fps, (new_width, new_height))

for item in imgs_join:
    out.write(item)
    cv2.imshow("frame", item)
    cv2.waitKey(50)
out.release()

在上述代码中,核心部分就是渲染这一步,但思路还是很简单的。 在上一步中,已经按照指定的时间差读取了不同的视频序列,渲染这一步就是需要把每一帧所需要用到的影像绘制到当前帧。 这里绘制的时候有顺序问题,早绘的内容会被晚绘的内容给覆盖掉,因此应该先绘较慢的那些帧,再绘较快的那些帧。 虽然整个代码看起来有点复杂,其实稍微看看就能明白了。 之所以觉得复杂是因为这里全部采用的是参数化运算,这样好处是不依赖于某个具体的视频或设置,可以在代码一开始的超参数中随意修改。

2.演示效果

简单演示效果如下,视频可能稍微慢一点,稍微等一下。

当然和原版过场动画相比优点辣眼睛,但也还凑合吧。生成的视频会保存在脚本运行目录下,叫output.avi

本文作者原创,未经许可不得转载,谢谢配合

返回顶部