用Python制作灯光秀短视频的思路详解


Posted in Python onApril 13, 2021

一、引言

2021年4月8日武汉重启一周年,这是个值得庆祝的日子,作为一个武汉人和一个死宅程序员,老猿也想在这个日子留下点什么。想起武汉长江两岸的灯光秀,顿时有了主意,那就用程序实现一个武汉重启庆祝的灯光秀短视频吧,于是在4月7日晚开始构思和着手开发,4月8日晚终于顺利完成,并且通过使用OpenCV、OpenCV+Moviepy两种方式进行了实现。

本文介绍结合Python+OpenCV+Moviepy实现的思路和过程,Python+OpenCV实现的思路和过程将在另外的博文中单独介绍。

二、实现思路

2.1、视频内容设计

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

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

  • 片头:5秒时间,展现一幅黄鹤楼的照片,并带上“武汉重启一周年灯光秀”的标题
  • 视频内容:全长35秒,每隔2秒随机展现一张武汉灯光秀景观图,并在视频中附上向上滚动的文字“热烈庆祝武汉重启一周年!”、“武汉万岁!中国万岁!”,并在视频的左下角和右下角用红绿蓝三色画三条向上晃动的线条表示彩色激光
  • 片尾:25秒,每隔2秒随机展现武汉的一张风景照,并展现“制作:老猿Python”等制作信息。

 2.2、开发设计

 2.2.1、视频图片处理

视频中用到的图片都来源于互联网,为了确保视频输出,所有图片都调整到了统一大小。
在程序中通过全局变量将片头使用图像使用了全局变量进行保存,视频内容使用图像、片尾使用图像分别通过两个全局生成器进行访问,两个全局生成器能从列表中顺序取出图片,并在到达列表结尾时回到第一个元素。当然生成器访问的方法也可以不使用生成器而使用多个全局变量来实现。

2.2.2、灯光效果处理

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

2.2.3、帧图像的生成

为了符合Moviepy剪辑get_frame函数仅带一个时间参数t的要求,上面介绍的图像处理,全部集中在一个帧图像生成函数中处理,该函数仅带一个参数时间t。

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

2.2.4、输出到视频

为了将视频输出到文件,通过Moviepy构建剪辑,指定帧图像生成函数,并给视频附加音频,然后使用剪辑输出的方法将视频输出到指定文件,最终得到一个完整的视频。

三、具体实现

 3.1、总流程

  • 加载片头图像,构造视频内容、片尾需要使用图像访问的生成器;
  • 构建帧图像生成函数,根据帧图像生成逻辑生成帧图像
  • 加载并绑定音频音频、指定帧率、时长、帧图像生成函数构造视频剪辑对象;
  • 将视频剪辑输出到文件。

3.2、定义两个图片获取生成器函数

两个图片获取生成器为构造视频内容、片尾提供背景图像;

def getLightShowImgFun():#定义灯光秀图片访问生成器函数
    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)),
                        cv2.resize(readImgFile(r'f:\pic\武汉\灯光秀-汉口.jpg'), (800, 600)),
                        cv2.resize(readImgFile(r'f:\pic\武汉\灯光秀-汉口3.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)),
                        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)),
                        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))]
    index = 0
    count = len(lightShowImgList)
    while True:
        index += 1
        if index>=count:index = 1
        yield lightShowImgList[index-1]


def getWHImgFunc():#定义片尾图片访问生成器函数
    # 片尾背景图片
    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))]

    index = 0
    count = len(whImgList)
    while True:
        index += 1
        if index>=count:index = 1
        yield whImgList[index-1]

3.3、全局变量初始化

全局变量主要是视频片头使用图片hhlImg、两个生成器,以及preImg,preTime用于记录上次切换视频图片的图片和切换时间。

#片头背景图片
hhlImg = cv2.resize(readImgFile(r'f:\pic\武汉\黄鹤楼.jpg'),(800,600))

#灯光秀背景图片和片尾图片全局生成器初始化
getLightShowImg = getLightShowImgFun()
getWHImg = getWHImgFunc()

#上次切换背景图片和切换时间全局变量初始化
preImg,preTime = None,0

3.4、实现给背景图像添加彩色激光照射效果

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.4、帧图片生成

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

def makeframe(t):
    """
    makeframe函数实现帧图片生成,带一个参数t表示当前帧对应的剪辑时间,在函数内根据剪辑时间来判断是生成片头内容、灯光秀内容还是片尾内容生成t时刻对应帧图像返回:
    1. 对于片头,采用黄鹤楼照片作为背景,并在图片中央显示“武汉重启一周年灯光秀”;
    2. 对于视频内容,则每2秒从灯光秀图片队列中随机取一张图片作为当前背景图像,并调用lightShowImg增加左下角和右下角的彩色激光效果,同时动态向上滚动显示“热烈庆祝武汉重启一周年”、“武汉万岁!中国万岁!”等标语;
    3. 对于片尾,则每2秒从武汉风景图片队列中随机取一张图片作为当前背景图像,同时动态向上滚动显示制作信息。
    :param t: 生成帧对应剪辑的时间位置
    :return: 对应帧图像
    """
    #生成t时刻的视频帧图像
    global preImg,preTime
    print(f"\rt={t:4.2f}", end='')
    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 = next(getLightShowImg)
            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 = next(getWHImg)
            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://blog.csdn.net/LaoYuanPython",24,(255,255,255),vRefPos= -60-t)
        img = imgAddText(img, "2021年4月8日", 24, (255,255,255), vRefPos=-t)
    img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)

    return img

3.5、制作视频文件

函数buildVideo用于制作视频文件:

def buildVideo():
    #构建视频
    audio = AudioFileClip(r"F:\video\友谊之光.mp3").set_duration(65)
    clip = VideoClip(makeframe, False, 65).set_fps(24).set_audio(audio)
    clip.write_videofile(r"F:\video\lightShow.avi", codec='png', threads=8)

调用buildVideo函数即可完成视频制作。

3.6、视频效果

用Python制作灯光秀短视频的思路详解

四、小结

本文完整介绍了用Python+OpenCV+Moviepy制作一个庆祝武汉重启一周年的武汉灯光秀短视频的实现思路、过程、关键函数等,有助于理解OpenCV的图像操作、Moviepy生成视频的实现。

到此这篇关于用Python制作灯光秀短视频的文章就介绍到这了,更多相关python制作短视频内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python中的闭包总结
Sep 18 Python
Python实现简单字典树的方法
Apr 29 Python
Python正则表达式经典入门教程
May 22 Python
利用Python读取txt文档的方法讲解
Jun 23 Python
Python 普通最小二乘法(OLS)进行多项式拟合的方法
Dec 29 Python
python Tkinter版学生管理系统
Feb 20 Python
利用python将图片版PDF转文字版PDF
May 03 Python
Pandas中resample方法详解
Jul 02 Python
python openCV获取人脸部分并存储功能
Aug 28 Python
flask 实现token机制的示例代码
Nov 07 Python
python 读取二进制 显示图片案例
Apr 24 Python
基于PyQT实现区分左键双击和单击
May 19 Python
python实现socket简单通信的示例代码
使用Selenium实现微博爬虫(预登录、展开全文、翻页)
python用字节处理文件实例讲解
Apr 13 #Python
Python中相见恨晚的技巧
Apr 13 #Python
利用python做表格数据处理
python glom模块的使用简介
Apr 13 #Python
Python的flask接收前台的ajax的post数据和get数据的方法
You might like
改造一台复古桌面收音机
2021/03/02 无线电
mysql_fetch_assoc和mysql_fetch_row的功能加起来就是mysql_fetch_array
2007/01/15 PHP
php中比较简单的导入phpmyadmin生成的sql文件的方法
2011/06/28 PHP
PHP 信号管理知识整理汇总
2017/02/19 PHP
phpStudy 2016 使用教程详解(支持PHP7)
2017/10/18 PHP
laravel 框架结合关联查询 when()用法分析
2019/11/22 PHP
Ext JS Grid在IE6 下宽度的问题解决方法
2009/02/15 Javascript
js 手机号码合法性验证代码集合
2012/09/29 Javascript
js复制网页内容并兼容各主流浏览器的代码
2013/12/17 Javascript
jQuery实现径向动画菜单效果
2015/07/17 Javascript
Bootstrap实现弹性搜索框
2016/07/11 Javascript
BootStrap中Datepicker控件带中文的js文件
2016/08/10 Javascript
javascript 判断当前浏览器版本并判断ie版本
2017/02/17 Javascript
vue项目webpack中Npm传递参数配置不同域名接口
2018/06/15 Javascript
vue-router源码之history类的浅析
2019/05/21 Javascript
JavaScript迭代器的含义及用法
2019/06/21 Javascript
微信小程序之 catalog 切换实现解析
2019/09/12 Javascript
javascript json对象小技巧之键名作为变量用法分析
2019/11/11 Javascript
JavaScript实现拖拽和缩放效果
2020/08/24 Javascript
ant design vue datepicker日期选择器中文化操作
2020/10/28 Javascript
基于vue-simple-uploader封装文件分片上传、秒传及断点续传的全局上传插件功能
2021/02/23 Vue.js
python正则表达式的使用
2017/06/12 Python
pyttsx3实现中文文字转语音的方法
2018/12/24 Python
Python图像处理模块ndimage用法实例分析
2019/09/05 Python
探秘TensorFlow 和 NumPy 的 Broadcasting 机制
2020/03/13 Python
个人自荐书
2013/12/20 职场文书
校园安全广播稿
2014/02/08 职场文书
单位工程竣工验收方案
2014/03/16 职场文书
低碳环保口号
2014/06/12 职场文书
求职信的正确写法
2014/07/10 职场文书
医院党员公开承诺书
2014/08/30 职场文书
结婚堵门保证书
2015/05/08 职场文书
代理词怎么写
2015/05/25 职场文书
运动会5000米加油稿
2015/07/21 职场文书
2016年寒假社会实践活动心得体会
2015/10/09 职场文书
通过Python把学姐照片做成拼图游戏
2022/02/15 Python