用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机器学习之随机森林(七)
Mar 26 Python
Python中文件的读取和写入操作
Apr 27 Python
详谈Pandas中iloc和loc以及ix的区别
Jun 08 Python
python实现列表中由数值查到索引的方法
Jun 27 Python
Pandas 数据处理,数据清洗详解
Jul 10 Python
python实现栅栏加解密 支持密钥加密
Mar 20 Python
Python 迭代,for...in遍历,迭代原理与应用示例
Oct 12 Python
django连接mysql数据库及建表操作实例详解
Dec 10 Python
Python 获取命令行参数内容及参数个数的实例
Dec 20 Python
python matplotlib包图像配色方案分享
Mar 14 Python
详解Python中的路径问题
Sep 02 Python
numpy数据类型dtype转换实现
Apr 24 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/01 无线电
jq的get传参数在utf-8中乱码问题的解决php版
2008/07/23 PHP
微盾PHP脚本加密专家php解密算法
2020/09/13 PHP
php模拟js函数unescape的函数代码
2012/10/20 PHP
PHP中预定义的6种接口介绍
2015/05/12 PHP
php字符串函数 str类常见用法示例
2020/05/15 PHP
javascript delete 使用示例代码
2010/03/29 Javascript
JS操作Cookies的小例子
2013/10/15 Javascript
jquery右下角自动弹出可关闭的广告层
2015/05/08 Javascript
jQuery插件windowScroll实现单屏滚动特效
2015/07/14 Javascript
JS获取子窗口中返回的数据实现方法
2016/05/28 Javascript
最全面的JS倒计时代码
2016/09/17 Javascript
Vue 2.0中生命周期与钩子函数的一些理解
2017/05/09 Javascript
JavaScript文件的同步和异步加载的实现代码
2017/08/19 Javascript
基于VUE实现的九宫格抽奖功能
2018/09/30 Javascript
详解nuxt 微信公众号支付遇到的问题与解决
2019/08/26 Javascript
JavaScript代码模拟鼠标自动点击事件示例
2020/08/07 Javascript
[06:20]2015国际邀请赛第三日top10
2015/08/08 DOTA
[44:37]完美世界DOTA2联赛PWL S3 Forest vs access 第一场 12.11
2020/12/13 DOTA
python获取本机mac地址和ip地址的方法
2015/04/29 Python
Python首次安装后运行报错(0xc000007b)的解决方法
2016/10/18 Python
如何利用python查找电脑文件
2018/04/27 Python
pandas 选择某几列的方法
2018/07/03 Python
转换科学计数法的数值字符串为decimal类型的方法
2018/07/16 Python
pandas 条件搜索返回列表的方法
2018/10/30 Python
python 伯努利分布详解
2020/02/25 Python
pycharm如何实现跨目录调用文件
2020/02/28 Python
python实现密码强度校验
2020/03/18 Python
Django用户身份验证完成示例代码
2020/04/03 Python
python golang中grpc 使用示例代码详解
2020/06/03 Python
《雨点儿》教学反思
2014/04/14 职场文书
公证委托书标准格式
2014/09/11 职场文书
教师遵守党的政治纪律情况对照检查材料
2014/09/26 职场文书
2014年财务科工作总结
2014/11/11 职场文书
关于拾金不昧的感谢信
2015/01/21 职场文书
幼师大班个人总结
2015/02/13 职场文书