用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 相关文章推荐
python3.4下django集成使用xadmin后台的方法
Aug 15 Python
python实现数独游戏 java简单实现数独游戏
Mar 30 Python
pandas数值计算与排序方法
Apr 12 Python
Python对切片命名的实现方法
Oct 16 Python
对python中的iter()函数与next()函数详解
Oct 18 Python
解决Python3 被PHP程序调用执行返回乱码的问题
Feb 16 Python
Python实现的读取文件内容并写入其他文件操作示例
Apr 09 Python
python实现的生成word文档功能示例
Aug 23 Python
如何使用python切换hosts文件
Apr 29 Python
django 模版关闭转义方式
May 14 Python
python 操作mysql数据中fetchone()和fetchall()方式
May 15 Python
python 实现关联规则算法Apriori的示例
Sep 30 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
PHP系统命令函数使用分析
2013/07/05 PHP
PHP页面跳转操作实例分析(header方法)
2016/09/28 PHP
PHP使用HTML5 FileApi实现Ajax上传文件功能示例
2019/07/01 PHP
PHP检测一个数组有没有定义的方法步骤
2019/07/20 PHP
Laravel框架查询构造器 CURD操作示例
2019/09/04 PHP
yii2.0框架多模型操作示例【添加/修改/删除】
2020/04/13 PHP
JavaScript延迟加载
2021/03/09 Javascript
javascript 限制输入脚本大全
2009/11/03 Javascript
js 居中漂浮广告
2010/03/21 Javascript
基于pthread_create,readlink,getpid等函数的学习与总结
2013/07/17 Javascript
JS实现先显示大图后自动收起显示小图的广告代码
2015/09/04 Javascript
jquery.validate表单验证插件使用方法解析
2016/11/07 Javascript
AngularJS服务service用法总结
2016/12/13 Javascript
微信小程序 WebSocket详解及应用
2017/01/21 Javascript
js记录点击某个按钮的次数-刷新次数为初始状态的实例
2017/02/15 Javascript
JS实现禁止高频率连续点击的方法【基于ES6语法】
2017/04/25 Javascript
ES6深入理解之“let”能替代”var“吗?
2017/06/28 Javascript
JS库之Waypoints的用法详解
2017/09/13 Javascript
基于vue中解决v-for使用报红并出现警告的问题
2018/03/03 Javascript
用react-redux实现react组件之间数据共享的方法
2018/06/08 Javascript
vue中使用cookies和crypto-js实现记住密码和加密的方法
2018/10/18 Javascript
微信小程序实现下拉菜单切换效果
2020/03/30 Javascript
javascript中floor使用方法总结
2019/02/02 Javascript
Python将多个excel表格合并为一个表格
2021/02/22 Python
python 返回列表中某个值的索引方法
2018/11/07 Python
Python常用模块之requests模块用法分析
2019/05/15 Python
pandas DataFrame 警告(SettingWithCopyWarning)的解决
2019/07/23 Python
TensorFlow 显存使用机制详解
2020/02/03 Python
Python IDLE或shell中切换路径的操作
2020/03/09 Python
python 获取当前目录下的文件目录和文件名实例代码详解
2020/03/10 Python
超全Python图像处理讲解(多模块实现)
2020/04/13 Python
css3的transform中scale缩放详解
2014/12/08 HTML / CSS
中国领先的专业家电网购平台:国美在线
2016/12/25 全球购物
幼儿园安全检查制度
2014/01/30 职场文书
2014离婚协议书范文
2014/09/10 职场文书
2015秋季开学典礼新闻稿
2015/07/17 职场文书