用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实现的一个找零钱的小程序代码分享
Aug 25 Python
python实现自动登录人人网并访问最近来访者实例
Sep 26 Python
python socket多线程通讯实例分析(聊天室)
Apr 06 Python
python实现机器人行走效果
Jan 29 Python
使用Python读取大文件的方法
Feb 11 Python
python中正则表达式的使用方法
Feb 25 Python
django+mysql的使用示例
Nov 23 Python
python可视化篇之流式数据监控的实现
Aug 07 Python
使用python实现离散时间傅里叶变换的方法
Sep 02 Python
关于Python 常用获取元素 Driver 总结
Nov 24 Python
python 画函数曲线示例
Dec 04 Python
Django REST framwork的权限验证实例
Apr 02 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 多维数组的排序问题 根据二维数组中某个项排序
2011/11/09 PHP
PHP面向对象的进阶学习(抽像类、接口、final、类常量)
2012/05/07 PHP
php中限制ip段访问、禁止ip提交表单的代码分享
2014/08/22 PHP
php中explode函数用法分析
2014/11/15 PHP
PHP判断FORM表单或URL参数来的数据是否为整数的方法
2016/03/25 PHP
Smarty分页实现方法完整实例
2016/05/11 PHP
详解如何在云服务器上部署Laravel
2017/06/30 PHP
php中array_fill函数的实例用法
2021/03/02 PHP
JavaScript高级程序设计 阅读笔记(十四) js继承机制的实现
2012/08/14 Javascript
jQuery setTimeout()函数使用方法
2013/04/07 Javascript
现如今最流行的JavaScript代码规范
2014/03/08 Javascript
JS非Alert实现网页右下角“未读信息”效果弹窗
2015/09/26 Javascript
AngularJS基础 ng-list 指令详解及示例代码
2016/08/02 Javascript
手动初始化Angular的模块与控制器
2016/12/26 Javascript
浅谈Angular4中常用管道
2017/09/27 Javascript
详解Vue2 SSR 缓存 Api 数据
2017/11/20 Javascript
详解wepy开发小程序踩过的坑(小结)
2019/05/22 Javascript
Python警察与小偷的实现之一客户端与服务端通信实例
2014/10/09 Python
详解python如何调用C/C++底层库与互相传值
2016/08/10 Python
用python处理图片实现图像中的像素访问
2018/05/04 Python
用xpath获取指定标签下的所有text的实例
2019/01/02 Python
python字符串替换re.sub()方法解析
2019/09/18 Python
linux环境下安装python虚拟环境及注意事项
2020/01/07 Python
python 错误处理 assert详解
2020/04/20 Python
深入了解NumPy 高级索引
2020/07/24 Python
哄娃神器4moms商店:美国婴童用品品牌
2019/03/07 全球购物
成教自我鉴定
2013/10/27 职场文书
2014年打非治违工作总结
2014/11/13 职场文书
离婚协议书格式
2014/11/21 职场文书
建筑安全员岗位职责
2015/02/15 职场文书
工会工作个人总结
2015/03/03 职场文书
小学语文教师研修感悟
2015/11/18 职场文书
青年文明号创建口号大全
2015/12/25 职场文书
保险公司增员口号
2015/12/25 职场文书
2016年学校安全教育月活动总结
2016/04/06 职场文书
Spring Boot 的创建和运行示例代码详解
2022/07/23 Java/Android