opencv 视频处理(python)

Posted 暴风雨中的白杨

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了opencv 视频处理(python)相关的知识,希望对你有一定的参考价值。

视频处理

视频处理

视频是由一系列图像构成的,这一系列图像被称为帧,帧是以固定的时间间隔从视频中获取的。

获取(播放)帧的速度称为帧速率,其单位通常使用“帧/秒”表示,代表在1秒内所出现的帧数,对应的英文是FPS(Frames Per Second)。

如果从视频中提取出独立的帧,就可以使用图像处理的方法对其进行处理,达到处理视频的目的。

OpenCV提供了cv2.VideoCapture类和cv2.VideoWriter类来支持各种类型的视频文件。

在不同的操作系统中,它们支持的文件类型可能有所不同,但是在各种操作系统中均支持AVI格式的视频文件。

VideoCapture类

cv2.VideoCapture类处理视频的方式非常简单、快捷,而且它既能处理视频文件又能处理摄像头信息。

类函数介绍

cv2.VideoCapture类的常用函数包括初始化、打开、帧捕获、释放、属性设置等

  1. 初始化

    OpenCV为cv2.VideoCapture类提供了构造函数cv2.VideoCapture(),用于打开摄像头并完成摄像头的初始化工作。

    该函数的语法格式为:

    捕获对象=cv2.VideoCapture("摄像头ID号")
    
    • “摄像头ID号”就是摄像头的ID号码。需要注意的是,这个参数是摄像设备(摄像头)的ID编号,而不是文件名。其默认值为-1,表示随机选取一个摄像头;如果有多个摄像头,则用数字“0”表示第1个摄像头,用数字“1”表示第2个摄像头,以此类推。

      如果只有一个摄像头,既可以使用“0”,也可以使用“-1”作为摄像头ID号。

      在某些平台上,如果该参数值为“-1”, OpenCV会弹出一个窗口,让用户手动选择希望使用的摄像头。

    • “捕获对象”为返回值,是cv2.VideoCapture类的对象。

    要初始化当前的摄像头,可以使用语句:

    cap = cv2.VideoCapture(0)
    

    特别强调:视频处理完以后,要记得释放摄像头对象。

    该(构造)函数也能够用于初始化视频文件,初始化视频文件时,参数为文件名。

    此时函数的形式为:

    捕获对象=cv2.VideoCapture("文件名")
    

    例如,打开当前目录下文件名为“vtest.avi”的视频文件,可以使用语句:

    cap = cv2.VideoCapture('vtest.avi')
    
  2. cv2.VideoCapture.open()函数和cv2.VideoCapture.isOpened()函数

    一般情况下,使用cv2.VideoCapture()函数即可完成摄像头的初始化。

    有时,为了防止初始化发生错误,可以使用函数cv2.VideoCapture.isOpened()来检查初始化是否成功。

    该函数的语法格式为:

    retval = cv2.VideoCapture.isOpened()
    

    该函数会判断当前的摄像头是否初始化成功

    • 如果成功,则返回值retval为True
    • 如果不成功,则返回值retval为False

    如果摄像头初始化失败,可以使用函数cv2.VideoCapture.open()打开摄像头。

    该函数的语法格式为:

    retval = cv2.VideoCapture.open( index )
    
    • index为摄像头ID号。
    • retval为返回值,当摄像头(或者视频文件)被成功打开时,返回值为True

    同样,函数cv2.VideoCapture.isOpened()和函数cv2.VideoCapture.open()也能用于处理视频文件。在处理视频文件时,函数cv2.VideoCapture.open()的参数为文件名, 其语法格式为:

    retval = cv2.VideoCapture.open( filename )
    
  3. 捕获帧

    摄像头初始化成功后,就可以从摄像头中捕获帧信息了。

    捕获帧所使用的是函数cv2.VideoCapture.read()。该函数的语法是:

    retval, image=cv2.VideoCapture.read()
    
    • image是返回的捕获到的帧,如果没有帧被捕获,则该值为空。
    • retval表示捕获是否成功,如果成功则该值为True,不成功则为False
  4. 释放

    在不需要摄像头时,要关闭摄像头。关闭摄像头使用的是函数cv2.VideoCapture.release()。

    该函数的语法是:

    None=cv2.VideoCapture.release()
    

    例如,当前有一个VideoCapture类的对象cap,要将其释放,可以使用语句:

    cap.release()
    
  5. 属性设置

    需要获取cv2.VideoCapture类对象的属性,或是更改该类对象的属性。

    函数cv2.VideoCapture.get()用于获取cv2.VideoCapture类对象的属性,该函数的语法格式是:

    retval = cv2.VideoCapture.get( propId )
    

    式中,参数propId对应着cv2.VideoCapture类对象的属性

    例如,有一个cv2.VideoCapture类对象cvc,则:

    • 通过cvc.get(cv2.CAP_PROP_FRAME_WIDTH),就能获取当前帧对象的宽度。
    • 通过cvc.get(cv2.CAP_PROP_FRAME_HEIGHT),就能获取当前帧对象的高度。

    函数cv2.VideoCapture.set()用来设置cv2.VideoCapture类对象的属性。

    该函数的语法是:

    retval = cv2.VideoCapture.set( propId, value )
    

    式中,propId对应cv2.VideoCapture类对象的属性,value对应属性propid的值。

    例如,有一个cv2.VideoCapture类对象cvc,则:

    • 语句ret=cvc.set(cv2.CAP_PROP_FRAME_WIDTH, 640)将当前帧对象的宽度设置为640像素。
    • 语句ret=cvc.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)将当前帧对象的高度设置为480像素。

    属性值及含义

  6. cv2.VideoCapture.grab()函数和cv2.VideoCapture.retrieve()函数

    同步多个摄像头

    如果需要读取一个摄像头的视频数据,最简便的方法就是使用函数cv2.VideoCapture.read()。但是,如果需要同步一组或一个多头(multihead)摄像头(例如立体摄像头或Kinect)的视频数据时,该函数就无法胜任了。

    可以把函数cv2.VideoCapture.read()理解为是由函数cv2.VideoCapture.grab()和函数cv2.VideoCapture.retrieve()组成的。

    函数cv2.VideoCapture.grab()用来指向下一帧,函数cv2.VideoCapture.retrieve()用来解码并返回一帧。因此,可以使用函数cv2.VideoCapture.grab()和函数cv2.VideoCapture.retrieve()获取多个摄像头的数据。

    函数cv2.VideoCapture.grab()用来指向下一帧,其语法格式是:

    retval= cv2.VideoCapture.grab( )
    

    如果该函数成功指向下一帧,则返回值retval为True。

    函数cv2.VideoCapture.retrieve()用来解码,并返回函数v2.VideoCapture.grab()捕获的视频帧。

    该函数的语法格式为:

    retval, image = cv2.VideoCapture.retrieve( )
    
    • image为返回的视频帧,如果未成功,则返回一个空图像。
    • retval为布尔型值,若未成功,返回False;否则,返回True。

    对于一组摄像头,可以使用如下代码捕获不同摄像头的视频帧:

    success0 = cameraCapture0.grab() 
    success1 = cameraCapture1.grab() 
    if success0 and success1: 
    	frame0 = cameraCapture0.retrieve() 
    	frame1 = cameraCapture1.retrieve()
    

    与VideoCapture类内的其他函数一样,cv2.VideoCapture.grab()和cv2.VideoCapture.retrieve()也能用来读取视频文件。

捕获摄像头视频

使用cv2.VideoCapture类捕获摄像头视频。

根据题目要求,编写代码如下:

import numpy as np 
import cv2 
cap = cv2.VideoCapture(0) 
while(cap.isOpened()): 
	ret, frame = cap.read() 
	cv2.imshow('frame', frame) 
	c = cv2.waitKey(1) 
	if c==27:   #ESC键 
		break 
cap.release() 
cv2.destroyAllWindows() 
播放视频文件

播放视频文件时,需要将函数cv2.VideoCapture()的参数值设置为视频文件的名称。在播放视频时,可以通过设置函数cv2.waitKey()中的参数值,来设置播放视频时每一帧的持续(停留)时间。

如果函数cv2.waitKey()中的参数值:

  • 较小,则说明每一帧停留的时间较短,视频播放速度会较快。
  • 较大,则说明每一帧停留的时间较长,视频播放速度会较慢。

该参数的单位是ms,通常情况下,将这个参数的值设置为25即可。( 1000 / 25 = 40)

**例子:**使用cv2.VideoCapture类播放视频文件

import numpy as np 
import cv2 
cap = cv2.VideoCapture('viptrain.avi') 
while(cap.isOpened()): 
	ret, frame = cap.read() 
	cv2.imshow('frame', frame) 
	c = cv2.waitKey(25) 
	if c==27:   #ESC键 
		break 
cap.release() 
cv2.destroyAllWindows() 
VideoWriter类

OpenCV中的cv2.VideoWriter类可以将图片序列保存成视频文件,也可以修改视频的各种属性,还可以完成对视频类型的转换。

类函数介绍

cv2.VideoWriter类常用的成员函数包括:构造函数、write函数等。

  1. 构造函数

    OpenCV为cv2.VideoWriter类提供了构造函数,用它来实现初始化工作。

    该函数的语法格式是:

    <VideoWriter object> = cv2.VideoWriter( filename, fourcc, fps, frameSize[,isColor] ) 
    
    • filename指定输出目标视频的存放路径和文件名。如果指定的文件名已经存在,则会覆盖这个文件。

    • fourcc表示视频编/解码类型(格式)。

      在OpenCV中用函数cv2.VideoWriter_fourcc()来指定视频编码格式。cv2.VideoWriter_fourcc()有4个字符参数。这4个字符参数构成了编/解码器的“4字标记”,每个编/解码器都有一个这样的标记。

      几个常用的标记:

      • cv2.VideoWriter_fourcc(‘I’, ‘4’, ‘2’, ‘0’)表示未压缩的YUV颜色编码格式,色度子采样为4:2:0。该编码格式具有较好的兼容性,但产生的文件较大,文件扩展名为.avi。
      • cv2.VideoWriter_fourcc(‘P’, ‘I’, ‘M’, ‘I’)表示MPEG-1编码类型,生成的文件的扩展名为.avi。
      • cv2.VideoWriter_fourcc(‘X’, ‘V’, ‘I’, ‘D’)表示MPEG-4编码类型。如果希望得到的视频大小为平均值,可以选用这个参数组合。该组合生成的文件的扩展名为.avi。
      • cv2.VideoWriter_fourcc(‘T’, ‘H’, ‘E’, ‘O’)表示Ogg Vorbis编码类型,文件的扩展名为.ogv。
      • cv2.VideoWriter_fourcc(‘F’, ‘L’, ‘V’, ‘I’)表示Flash视频,生成的文件的扩展名为.flv。

      更多的字符参数组合:http://www.fourcc.org

      若参数fourcc为“-1”,则程序运行时会弹出一个对话框, 在该对话框中,用户可以根据自己的需要选择合适的压缩程序和压缩质量。

    • fps为帧速率。

    • frameSize为每一帧的长和宽。

    • isColor表示是否为彩色图像。

    **例子: **下面的语句完成了cv2.VideoWriter类的初始化工作

    fourcc = cv2.VideoWriter_fourcc(*'XVID')
    out = cv2.VideoWriter('output.avi', fourcc, 20, (1024,768))
    

    如果希望通过对话框设置编/解码格式,可以使用语句:

    fourcc = -1
    out = cv2.VideoWriter('output.avi', fourcc, 20, (1024,768))
    
  2. write函数

    cv2.VideoWriter类中的函数cv2.VideoWriter.write()用于写入下一帧视频。

    该函数的语法格式为:

    None=cv2.VideoWriter.write(image)
    
    • image是要写入的视频帧。

    通常情况下,要求彩色图像的格式为BGR模式。

    在调用该函数时,直接将要写入的视频帧传入该函数即可。

    例如,有一个视频帧为frame,要将其写入上面的示例中名为out的cv2.VideoWriter类对象内,则使用语句:

    out.write(frame)
    

    上述语句会把frame传入名为output.avi的out对象内。

  3. 释放

    在不需要cv2.VideoWriter类对象时,需要将其释放。

    释放该类对象时所使用的是函数cv2.VideoWriter.release()。

    该函数的语法格式是:

    None = cv2.VideoWriter.release( )
    

    当前有一个cv2.VideoWriter类的对象out,可以用以下语句将其释放:

    out.release()
    
保存视频

保存视频包括创建对象、写入视频、释放对象等多个步骤

  1. 创建对象

    在创建对象前,首先需要设置好参数。

    • 设置好要保存的具体文件名,例如:filename=‘‘out.avi’’。
    • 使用cv2.VideoWriter_fourcc()确定编/解码的类型,例如:fourcc =cv2.VideoWriter_fourcc(*‘XVID’)。
    • 确定视频的帧速率,例如:fps=20。
    • 确定视频的长度和宽度,例如:size=(640,480)。

    然后利用上述参数,创建对象。例如:

    out = cv2.VideoWriter( filename , fourcc , fps , size )
    

    当然,也可以直接在构造函数内用需要的参数值创建对象。例如:

    out = cv2.VideoWriter('out.avi', fourcc, 20, (640,480))
    
  2. 写入视频

    用函数cv2.VideoWriter.write()在创建的对象out内写入读取到的视频帧frame。使用的代码为:

    out.write(frame) 
    
  3. 释放对象

    在完成写入后,释放对象out。代码为:

    out.release()
    

    **例子:**使用cv2.VideoWriter类保存摄像头视频文件。

    import numpy as np 
    import cv2 
    cap = cv2.VideoCapture(0) 
    fourcc = cv2.VideoWriter_fourcc('I', '4', '2', '0') 
    out = cv2.VideoWriter('output.avi', fourcc, 20, (640,480)) 
    while(cap.isOpened()): 
    	ret, frame = cap.read() 
    	if ret==True: 
    		out.write(frame) 
    		cv2.imshow('frame', frame) 
    		if cv2.waitKey(1) == 27: 
    			break 
    	else: 
    		break 
    cap.release() 
    out.release() 
    cv2.destroyAllWindows() 
    

    运行上述程序,就会捕获当前摄像头的视频内容,并将其保存在当前目录下名为“output.avi”的视频文件中。

视频操作基础

视频是由视频帧构成的,将视频帧从视频中提取出,对其使用图像处理的方法进行处理,就可以达到处理视频的目的。

例子:提取视频的Canny边缘检测结果。

import numpy as np 
import cv2 
cap = cv2.VideoCapture('viptrain.avi') 
while(cap.isOpened()): 
	ret, frame = cap.read() 
	frame=cv2.Canny(frame,100,200) 
	cv2.imshow('frame', frame) 
	c = cv2.waitKey(1)   # 0 是一直等待
	if c==27:   #ESC键 
		break 
cap.release() 
cv2.destroyAllWindows() 

OpenCV-Python+Moviepy 结合进行视频特效处理

一、引言

最近事情多,好久没更新了,将以前有2篇使用OpenCV-Python+Moviepy 结合进行视频特效处理的文章整合一下发出来更新一下。

Moviepy 是一个 Python 的音视频剪辑库,OpenCV 是一个图形处理库,我们知道视频的一帧就是一幅图像,因此在处理视频时可以结合 OpenCV 进行帧处理,将二者结合可以用来进行一些不错的视频特效的处理。老猿在此介绍两个这方面的应用案例供大家参考。

二、给视频添加雪花飘落特效

2.1、实现原理

雪花特效可以给视频增加特殊的效果,要给视频加雪花特效,是基于以下原理来实现的:

  1. 每个视频都是由一个个视频帧构成,每个视频帧都是一副静态的图像,通过视频帧的连续显示形成动态视频;
  2. 实现视频雪花飘落,就是在视频的每帧图像中添加雪花,并在前后相连的视频帧中变化雪花的位置,形成雪花下飘带横向移动的效果;
  3. 雪花的图片本身是一个矩形,矩形内有黑色背景和白色的雪花,在将雪花图片添加到视频帧时,需要确保黑色部分不会遮盖帧图像的内容,只有白色的雪花前景色才可以遮挡帧图像内容。这就需要通过图像的阈值处理确得到雪花图像的二值图,用该二值图及其补图作为掩膜,二值图作为雪花原始图片与自身与运算的掩码来获取雪花图片的前景色,补图作为帧图片雪花对应位置的子图与子图自身与运算的掩膜来获取雪花黑色背景部分对应的帧图像作为背景色;
  4. 对视频帧调用雪花融合图像的函数进行动态融合雪花的处理;
  5. Moviepy 的 fl_image 是视频剪辑基类 VideoClip 的方法,该方法用于对视频的帧图像进行变换,其参数包括一个对帧图像进行变换的函数image_func,具体变换由应用实现对图像进行变换处理的一个函数,然后将该函数作为 image_func 的值参传入fl_image,Moviepy 就会调用该函数完成对视频每帧图像的处理生成新的剪辑

2.2、具体实现

2.2.1、雪花图片

本次实现的案例对应的雪花图片为:

文件名为:f:\\pic\\snow.jpg。上述图片的雪花是标准的雪花图像,但图像比较大,在视频中直接展示这么大的雪花就很假,老猿经过测试,发现将其缩小到原图像的五分之一以内比较象真正的雪花。

2.2.2、实现流程

2.2.3、关键实现

2.2.3.1、初始化雪花

图片的雪花是固定大小的,而真正的雪花大小是不同的,为了模拟真正的雪花效果,需要有各种不同大小和角度的雪花,为此每片雪花需要根据图片雪花图像进行随机的大小和角度调整。

一帧图像中的雪花数量至少是几十到几百片,如果每次融合图像时,都需要从图片雪花图像进行大小和旋转角度的变换,是非常消耗系统的性能的,影响视频的生成耗时。

为了提升处理性能,老猿只在程序开始初始化时一次批量生产各种不同大小、不同旋转角度的各种雪花,后续程序生成雪花时,直接从批量生成的雪花中取一个作为要生成的雪花,而不用每次从基本的雪花图像开始进行变换。

生成各种雪花形状的示例代码:

# -*- coding: utf-8 -*-
import cv2,random
import numpy as np

from opencvPublic import addImgToLargeImg,readImgFile,rotationImg
snowShapesList = [] #雪花形状列表
snowObjects=[]  #图片中要显示的所有雪花对象

def initSnowShapes():
    """
    从文件中读入雪花图片,并进行不同尺度的缩小和不同角度的旋转从而生成不同的雪花形状,这些雪花形状保存到全局列表中snowShapesList
    """
    global snowShapesList
    imgSnow = readImgFile(r'f:\\pic\\snow.jpg')
    imgSnow = cv2.resize(imgSnow, None, fx=0.3, fy=0.3) #图片文件中的雪花比较大,需要缩小才能象自然的雪花形象
    minFactor,maxFactor = 50,100  #雪花大小在imgSnow的0.5-1倍之间变化

    for factor in range(minFactor,maxFactor,5): #每次增加5%大小
        f = factor*0.01
        imgSnowSize = cv2.resize(imgSnow, None, fx=f, fy=f)
        for ange in range(0,360,5):#雪花0-360之间旋转,每次旋转角度增加5°
            imgRotate = rotationImg(imgSnowSize,ange)
            snowShapesList.append(imgRotate)

2.2.3.2、产生一排雪花

每帧图像除了保留上帧图像中未飘落出图像范围的雪花外,同时还会从顶部生成一排数量随机的雪花,形成生生不息的雪花。下面是从顶部初始化生成一排雪花的代码:

def generateOneRowSnows(width,count):
    """
    产生一排雪花对象,每个雪花随机从snowShapesList取一个、横坐标位置随机、纵坐标初始为0
    :param width: 背景图像宽度
    :param count: 希望的雪花数
    :y:当前行对应的竖直坐标
    :return:一个包含产生的多个雪花对象信息的列表,每个列表的元素代表一个雪花对象,雪花对象包含三个信息,在snowShapesList的索引号、初始x坐标、初始y坐标(才生成固定为0)
    """
    global snowShapesList
    line = []
    picCount = len(snowShapesList)
    for loop in range(count):
        imgId = random.randint(0,picCount-1)
        xPos = random.randint(0,width-1)
        line.append((imgId,xPos,0))
    return line

2.2.3.3、将所有雪花对象融合到背景图像

上帧图像中的雪花在当前帧中需要随机下落一定位置,并在一定幅度内横向漂移,当有雪花落到图像底部之下时,需要释放对应对象以节省资源。

def addSnowEffectToImg(img):
    """
    将所有snowObjects中的雪花对象融合放到图像img中,融合时y坐标随机下移一定高度,x坐标左右随机小范围内移动
    """
    global snowShapesList,snowObjects
    horizontalMaxDistance,verticalMaxDistance = 5,10 #水平方向左右漂移最大值和竖直方向下落最大值
    rows,cols = img.shape[:2]
    maxObjsPerRow = int(cols/100)
    snowObjects += generateOneRowSnows(cols, random.randint(0, maxObjsPerRow))
    snowObjectCount = len(snowObjects)
    rows,cols = img.shape[0:2]
    imgResult = np.array(img)
    for index in range(snowObjectCount-1,-1,-1):
        imgObj = snowObjects[index] #每个元素为(imgId,x,y)
        if imgObj[2]>rows: #如果雪花的起始纵坐标已经超出背景图像的高度(即到达背景图像底部),则该雪花对象需进行失效处理
            del(snowObjects[index])
        else:
            imgSnow = snowShapesList[imgObj[0]]
            x,y = imgObj[1:] #取该雪花上次的位置
            x = x+random.randint(-1*horizontalMaxDistance,horizontalMaxDistance) #横坐标随机左右移动一定范围
            y = y+random.randint(1,verticalMaxDistance) #纵坐标随机下落一定范围
            snowObjects[index] = (imgObj[0],x,y) #更新雪花对象信息
            imgResult = addImgToLargeImg(imgSnow,imgResult,(x,y),180) #将所有雪花对象图像按照其位置融合到背景图像中
    return imgResult #返回融合图像

2.2.3.4、实现视频雪花飘落特效视频合成

下面的代码调用 addSnowEffectToImg 实现对《粉丝记事本》视频的雪花飘落特效:

from  moviepy.editor import *

def addVideoSnowEffect(videoFileName,resultFileName):
    clip = VideoFileClip(videoFileName)
    newclip = clip.fl_image(addSnowEffectToImg, apply_to=['mask'])
    newclip.write_videofile(resultFileName)

if __name__ == '__main__':
    addVideoSnowEffect(r'f:\\video\\fansNote.mp4',r'f:\\video\\fansNote_snow.mp4')
2.2.4、雪花飘落效果

三、制作灯光秀短视频

3.1、视频内容设计

基于多张静态景物叠加特效声光特效可以将静态图片变成有趣的灯光秀视频,老猿为了武汉这个英雄之城解封一周年用 Python 制作了个解封庆祝的灯光秀短视频。

老猿是个没有艺术细胞的人,因此这个视频内容只能说仅能代表是个视频而已,对最终的内容表现大家就不需要过多评价。

在创作该视频前,老猿对视频进行了简单规划,将创作视频分为片头、视频内容和片尾三部分:

  1. 片头:5 秒时间,展现一幅黄鹤楼的照片,并带上“武汉重启一周年灯光秀”的标题

  2. 视频内容:全长 35 秒,每隔 2 秒随机展现一张武汉灯光秀景观图,并在视频中附上向上滚动的文字“热烈庆祝武汉重启一周年!”、“武汉万岁!中国万岁!”,并在视频的左下角和右下角用红绿蓝三色画三条向上晃动的线条表示彩色激光

  3. 片尾:25 秒,每隔 2 秒随机展现武汉的一张风景照,并展现“制片:老猿 Python”等制作信息。

3.2、开发设计

3.2.1、视频图片处理

视频中用到的图片都来源于互联网,为了减少图片加载的时间和统一初始化,在程序中通过全局变量将片头使用图像、视频内容使用图像、片尾使用图像分别使用了三个全局变量进行保存,其中后两者为列表类型。为了确保视频输出,所有图片都调整到了统一大小。

3.2.2、灯光效果处理

在视频内容部分,左下角和右下角发射的彩色激光,采用在背景图片中根据时间动态绘制彩色线条,实现彩色激光晃动照射的效果,为了限制晃动范围,设定了激光终点的 x 值的最小值和最大值。激光终点的位置根据时间动态计算,并在到达 x 值的最小值或最大值时自动回扫。

3.2.3、帧图像的生成

上面介绍的图像处理,全部集中在一个函数中处理,该函数仅带一个参数时间 t,判断时间来决定现在生成的内容是片头、内容还是片尾,然后据此来进行帧图像的生成。生成时,需要判断图像是否切换,因此需要记录上一次切换的时间和切换后的图像,确保未达到切换时间前用上次图像作为帧图像的背景,达到切换时间要求后切换新的图像作为后续帧图像生成的背景。为此在该函数中使用了两个全局变量来记录当前帧图像背景图片和上次切换时间。

3.2.4、输出到视频

为了将视频输出到文件,通过 VideoWriter_fourcc 指定视频输出文件类型,采用 OpenCV 的 VideoWriter 类来进行视频输出,通过 Open 方法指定输出文件,通过 write 方法将一帧帧视频写入。

3.3、具体实现

3.3.1、总流程
  1. 加载片头、视频内容、片尾需要使用的图像;

  2. 创建新视频文件

  3. 按照帧率 24、时长 65 秒,构建 24*65 张帧图像,并逐一输出到视频文件。

3.3.2、全局变量初始化

全局变量主要是视频中需要使用的图片,分成片头使用图片 hhlImg、灯光秀图片列表 lightShowImgList 和片尾风景图片列表 whImgList 。另外 preImg,preTime 用于记录上次切换视频图片的图片和切换时间。

hhlImg = cv2.resize(readImgFile(r'f:\\pic\\武汉\\黄鹤楼.jpg'),(800,600))
lightShowImgList = [cv2.resize(readImgFile(r'f:\\pic\\武汉\\灯光秀_桥.jpg'),(800,600)),cv2.resize(readImgFile(r'f:\\pic\\武汉\\灯光秀_武汉江边.jpg'),(800,600)),
                    cv2.resize(readImgFile(r'f:\\pic\\武汉\\灯光秀_一桥.jpg'),(800,600)),cv2.resize(readImgFile(r'f:\\pic\\武汉\\灯光秀_一桥底部.jpg'),(800,600)),
                    cv2.resize(readImgFile(r'f:\\pic\\武汉\\灯光秀_一桥远景.jpg'),(800,600)),cv2.resize(readImgFile(r'f:\\pic\\武汉\\灯光秀_远桥.jpg'),(800,600))]
whImgList =  [cv2.resize(readImgFile(r'f:\\pic\\武汉\\东湖1.jpg'),(800,600)),cv2.resize(readImgFile(r'f:\\pic\\武汉\\东湖2.jpg'),(800,600)),
                    cv2.resize(readImgFile(r'f:\\pic\\武汉\\东湖樱园樱花.jpg'),(800,600)),cv2.resize(readImgFile(r'f:\\pic\\武汉\\武大牌楼.jpg'),(800,600)),
                    cv2.resize(readImgFile(r'f:\\pic\\武汉\\武大牌楼远观.jpg'),(800,600)),cv2.resize(readImgFile(r'f:\\pic\\武汉\\武大樱花2.jpg'),(800,600)),
                    cv2.resize(readImgFile(r'f:\\pic\\武汉\\武大樱园顶高拍照.jpg'),(800,600)),cv2.resize(readImgFile(r'f:\\pic\\武汉\\武大樱园门洞.jpg'),(800,600)),
                    cv2.resize(readImgFile(r'f:\\pic\\武汉\\武大樱园入口.jpg'),(800,600)),cv2.resize(readImgFile(r'f:\\pic\\武汉\\武大老图书馆.jpg'),(800,600))]

preImg,preTime = None,0
3.3.3、实现给背景图像添加彩色激光照射效果

lightShowImg 函数实现给背景图像指定点发射彩色激光的特效,激光发射点固定,由参数 lightStartPos 指定,终点随参数 t 在一定范围内变化,终点 x 坐标受参数 minX, maxX 控制,同一个发射源的三激光之间的间距受参数 distance 控制。

def lightShowImg(bg,minX, maxX,distance,lightStartPos,t):
    """
    实现在背景图像上添加当前散发彩色激光的处理
    :param bg: 背景图像
    :param minX:灯光终点的最大x坐标
    :param maxX:灯光终点的最小x坐标
    :param distance: 不同灯光之间的间距
    :param lightStartPos: 灯光发射点
    :param t: 时间t
    :return: 添加了发射灯光的图像
    """

    x =  (minX+int(t*200))%(maxX*2) #按时间t计算灯光终点的x坐标,该坐标可以超出背景图像范围
    img = np.array(bg)
    if x>maxX: #到达最大范围,需要回扫
        x = 2*maxX-x
    color1,color2,color3 = (255,0,0),(0,255,0),(0,0 ,255 ) #蓝、绿、红三色
    cv2.line(img, lightStartPos, (x, 0), color1, 4)
    cv2.line(img, lightStartPos, (x + distance, 0), color2, 4)
    cv2.line(img, lightStartPos, (x - distance, 0), color3, 4)

    return img
3.3.4、帧图片生成

makeframe 函数实现帧图片生成,带一个参数 t 表示当前帧对应的剪辑时间,在函数内根据剪辑时间来判断是生成片头内容、灯光秀内容还是片尾内容生成 t 时刻对应帧图像返回:

  1. 对于片头,采用黄鹤楼照片作为背景,并在图片中央显示“武汉重启一周年灯光秀”;

  2. 对于视频内容,则每 2 秒从灯光秀图片队列中随机取一张图片作为当前背景图像,并调用 lightShowImg 增加左下角和右下角的彩色激光效果,同时动态向上滚动显示“热烈庆祝武汉重启一周年”、“武汉万岁!中国万岁!”等标语;

  3. 对于片尾,则每 2 秒从武汉风景图片队列中随机取一张图片作为当前背景图像,同时动态向上滚动显示制作信息。

def makeframe(t):
    #生成t时刻的视频帧图像
    global preImg,preTime
    if t<5:#5秒片头
        img = imgAddText(hhlImg,'武汉重启一周年灯光秀',64,(255,0,0),vRefPos='C')
    elif t<40:#5-40秒灯光秀
		minX, maxX = 200, 1200  # 灯光横向扫射范围
    	lightStartPos1 = (0, 600)  # 灯光1的发射点坐标设置为左下角
    	lightStartPos2 = (800, 600)  # 灯光2的发射点坐标设置为右下角
        if (t-preTime)>2 or preImg is None:
            img = np.array(random.choice(lightShowImgList))
            preImg,preTime  = img,t
        else:
            img = preImg
        img = lightShowImg(img, minX, maxX, 80,lightStartPos1, t)
        img = lightShowImg(img, minX-100, maxX+100,48, lightStartPos2, t)
        t = int((t-4)*40)%img.shape[0]
        img = imgAddText(img,'热烈庆祝武汉重启一周年!',48,(0,0,255),vRefPos=-80-t,)
        img = imgAddText(img, '武汉万岁!中国万岁!',48,(255,0,255),vRefPos=-t)
    else:#片尾

        if (t-preTime)>2 or preImg is None:
            img = np.array(random.choice(whImgList))
            preImg,preTime  = img,t
        else:
            img = preImg
        t = int((t - 39) * 20) % img.shape[0]
        img = imgAddText(img,"制片:老猿Python",36,(255,255,255),vRefPos=-120-t)
        img = imgAddText(img, "https://www.infoq.cn/u/laoyuanpython/publish",24,(255,255,255),vRefPos= -60-t)
        img = imgAddText(img, "2021年4月8日", 24, (255,255,255), vRefPos=-t)

    return img
3.3.5、制作视频文件

函数 buildVideoByCV 用于制作视频文件,按指定格式生成一个新的视频文件,然后循环调用 makeframe 生成一帧帧图像写入视频文件中。

def buildVideoByCV():
    videoMake = cv2.VideoWriter()
    fourcc = cv2.VideoWriter_fourcc(*'MP4V') #https://blog.csdn.net/whudee/article/details/108689420

    fps = 12
    videoMake.open(r"F:\\video\\lightShowCV.MP4", fourcc, fps, (800,600))
    for t in range(65*fps):
        img = makeframe(t*1.0/fps)
        videoMake.write(img)
        print(f'\\r视频制作进度:{(t*100.0)/(66*fps):4.2f}%',end='')
    videoMake.release()

上述函数构建后只需要调用 buildVideoByCV 函数即可完成视频制作。

3.3.6、视频效果

四、小结

本文介绍了制作视频雪花飘落特效和灯光秀的原理、实现的思想以及流程,并利用 Python+OpenCV+Moviepy 提供了关键的实现代码,可以供大家理解图像融合、图像制作视频、Moviepy 视频变换的完整案例。

写博不易,敬请支持:
如果阅读本文于您有所获,敬请点赞、评论、收藏,谢谢大家的支持!

如对文章内容存在疑问,可在博客评论区留言,或关注:老猿Python 微信公号发消息咨询。

更多图像处理的内容请参考专栏《https://blog.csdn.net/laoyuanpython/category_9979286.html OpenCV-Python图形图像处理 》、《https://blog.csdn.net/laoyuanpython/category_10581071.html OpenCV-Python初学者疑难问题集》及《图像处理基础知识》的介绍。

关于老猿的付费专栏

  1. 付费专栏《https://blog.csdn.net/laoyuanpython/category_9607725.html 使用PyQt开发图形界面Python应用》专门介绍基于Python的PyQt图形界面开发基础教程,对应文章目录为《 https://blog.csdn.net/LaoYuanPython/article/details/107580932 使用PyQt开发图形界面Python应用专栏目录》;
  2. 付费专栏《https://blog.csdn.net/laoyuanpython/category_10232926.html moviepy音视频开发专栏 )详细介绍moviepy音视频剪辑合成处理的类相关方法及使用相关方法进行相关剪辑合成场景的处理,对应文章目录为《https://blog.csdn.net/LaoYuanPython/article/details/107574583 moviepy音视频开发专栏文章目录》;
  3. 付费专栏《https://blog.csdn.net/laoyuanpython/category_10581071.html OpenCV-Python初学者疑难问题集》为《https://blog.csdn.net/laoyuanpython/category_9979286.html OpenCV-Python图形图像处理 》的伴生专栏,是笔者对OpenCV-Python图形图像处理学习中遇到的一些问题个人感悟的整合,相关资料基本上都是老猿反复研究的成果,有助于OpenCV-Python初学者比较深入地理解OpenCV,对应文章目录为《https://blog.csdn.net/LaoYuanPython/article/details/109713407 OpenCV-Python初学者疑难问题集专栏目录
  4. 付费专栏《https://blog.csdn.net/laoyuanpython/category_10762553.html Python爬虫入门 》站在一个互联网前端开发小白的角度介绍爬虫开发应知应会内容,包括爬虫入门的基础知识,以及爬取CSDN文章信息、博主信息、给文章点赞、评论等实战内容。

前两个专栏都适合有一定Python基础但无相关知识的小白读者学习,第三个专栏请大家结合《https://blog.csdn.net/laoyuanpython/category_9979286.html OpenCV-Python图形图像处理 》的学习使用。

对于缺乏Python基础的同仁,可以通过老猿的免费专栏《https://blog.csdn.net/laoyuanpython/category_9831699.html 专栏:Python基础教程目录)从零开始学习Python。

如果有兴趣也愿意支持老猿的读者,欢迎购买付费专栏。

老猿Python,跟老猿学Python!

☞ ░ 前往老猿Python博文目录 https://blog.csdn.net/LaoYuanPython

以上是关于opencv 视频处理(python)的主要内容,如果未能解决你的问题,请参考以下文章

opencv 视频处理(python)

Python3并行处理opencv视频帧

如何使用 OpenCV 和 Python 在视频流中逐帧处理视频图像

OpenCV-Python实战——图像与视频文件的处理(两万字详解,️建议收藏️)

OpenCV-Python3.OpenCV的图像基础操作

OpenCV-Python:如何从实时视频流中获取最新帧或跳过旧帧