Moviepy 音视频剪辑:黑白视频的帧图像格式探究
一、引言
最近在学习图像处理的《直方图处理》,直方图均衡处理的变换感觉非常有用。
以前学习 Moviepy 音视频剪辑时,用的卓别林的一个黑白视频片段,感觉视频的噪点比较多,画面也整体偏暗,不禁想看看如果对其进行直方图均衡会怎么样。结果在进行相关处理时,发现黑白视频的视频帧对应图像居然不是灰度图。下面将验证过程及分析过程介绍一下。
二、基础知识介绍
1、Moviepy 的视频变换 fl_image 方法
由于是对视频图像进行灰度处理,因此需要逐帧读取视频帧,每帧应用直方图均衡。该视频变换处理有多种方法实现,老猿使用 fl_image。
fl_image 方法是对 get_frame 方法获取的帧进行变换的方法,本质上是 Clip 的 fl 方法在内容变换方面的一种变种和应用。
调用语法:fl_image(self, image_func, apply_to=None)
参数说明:
image_func:参数 image_func 是对剪辑帧进行图像变换的函数,带一个参数,参数就是要处理的帧,这个帧直接通过 get_frame 去获取,image_func 函数的返回值为经过变换后的帧
apply_to:apply_to 表示变换是否需要同时作用于剪辑的音频和遮罩,其值可以为’mask’、‘audio’、[‘mask’,‘audio’]
对比 fl 调用方法 fl(self, fun, apply_to=None, keep_duration=True):
fl_image 由于只变换内容,因此不涉及时间的变换,keep_duration 就是默认为 Trueimage_func 不带时间参数,这是因为系统默认调用 get_frame(t)来获取帧,无需 image_func 带时间参数
fl_image 本质上是执行如下语句来完成帧内容的变换:fl(lambda gf, t: image_func(gf(t)),apply_to)
更多关于 Moviepy 视频 fl_image 变换处理的介绍请参考《moviepy音视频剪辑:视频剪辑基类VideoClip的属性及方法详解》。
2、Moviepy 彩色视频转黑白视频变换 blackwhite 函数
blackwhite 函数用于将剪辑变成灰度剪辑,也就是将剪辑中的彩色像素灰度化。
调用语法:blackwhite(clip, RGB = None, preserve_luminosity=True)
参数说明:
clip:要处理的剪辑,通过 fx 或 subfx 调用时,会将调用者的实例对象 self 传入
RGB:浮点数三元组,用于设置 RGB 三种颜色的权重,缺省值为 None,如果为 None,则为 1:1:1,如果设置为‘CRT_phosphor’,则 RGB = [0.2125, 0.7154, 0.0721]
preserve_luminosity:preserve_luminosity 用于控制是否保持亮度,如果保持亮度不变,则最终的 RGB 三个值相加为 1。在这里的亮度 luminosity 不是 lightness,实际上是对明度的度量,也称为灰阶值,是不同权重的 R、G、B 的组合值。实际上亮度是对颜色的明度(brightness)的一种度量
3、OpenCV 的直方图均衡处理 equalizeHist 函数
OpenCV 的直方图均衡处理非常简单,就是对需要处理的黑白图像执行 cv2.equalizeHist 函数,返回图像即为直方图均衡后的图像。
4、OpenCV 的图像色系转换 cvtColor
cv2.cvtColor 是 openCV 提供的颜色空间转换函数。
调用语法:cvtColor(src, code, dstCn=None)
其中:
src:要转换的图像
code:转换代码,表示从何种类型的图像转换为何种类型,如 cv2.COLOR_BGR2GRAY 就是将 BGR 格式彩色图像转换成灰度图片,具体转换代码请参考官网文档
dstCn:目标图像的通道数,如果为 0 表示根据源图像通道数以及转换代码自动确认
三、测试使用的视频简介
以前学习 Moviepy 音视频剪辑时,用的卓别林的一个黑白视频片段,感觉视频的噪点比较多,画面也整体偏暗,下面是该视频的两张截图:
四、分析过程及遇到的问题
1、直接在 fl_image 调用直方图均衡的 equalizeHist 函数
实现代码如下:
结果执行报错(很长的报错信息,仅截取关键部分):
这些信息说明直方图均衡处理的图像数据类型不对,因为涉及到了 OpenCV 的函数,在 OpenCv-Python 中无法深入分析,因此也没办法看出具体原因。
2、将 equalizeHist 函数再次封装后给 fl_image 调用
代码如下:
报错信息依旧,由于用 flImg 封装了 cv2.equalizeHist,此时可以通过 Debug 跟踪 flImg 看到内部数据,居然发现 img 是三维数组,也即图像像素值是 RGB 格式,而不是灰度图格式。看样子虽然是个黑白视频,但格式是彩色视频格式?
3、将视频剪辑加载之后转成黑白视频再处理
好吧,黑白视频存放的是彩色格式,也许是因为视频保存格式控制的,那就把其先转成黑白视频,Moviepy 有个这样变换函数 blackwhite。
代码如下:
结果还是报同样错,观察 flImg 参数 img,居然还是 RGB 格式。黑白视频的帧是二维还是三维?让人一脸蒙圈!
4、强制图像转换为灰度图
好吧,老猿承认在黑白视频的帧数据认知上出问题了,黑白视频也是 RGB 格式,那只有对图像强制格式转换了,用 OpenCV 的 cvtColor 将 RGB 格式强制转换为灰度图。
代码如下:
这样处理后,执行没报错,但处理完的视频存在如下两个问题:
视频图像整体大小没变,但内容变成了同屏九窗播放模式:
视频播放速度是原视频的 3 倍左右,音频没变,导致视频的播放到 1/3 时长视频内容已经播放完成,然后视频就固定在最后一帧图像,音频还按原有节凑在播放。
总体来看,就是视频内容没有按照预定计划生成。为了查找问题,老猿做了如下处理:
将视频直接生成 gif,gif 是正常的;
去掉直方图均衡处理,只保留图像格式变换处理,还是存在问题;
去掉图像格式变换处理,flImg 直接返回原图像,视频生成后是正常的。
考虑上述处理可以得出结论,问题出现与视频帧图像 RGB 转灰度图处理有关,结合前面彩色视频使用 blackwhite 转黑白视频后图像像素仍然是 RGB 格式的情况,老猿感觉是黑白视频的帧图像格式不是我理解的灰度图,而是 RGB 格式的图像。那这个过程到底该怎么处理呢?为此老猿查看了 Moviepy 的 blackwhite 的源代码:
可以看到,该源代码是先将 RGB 像素值按照参数指定要求转换成灰度值,最后再将该灰度值复制 3 份作为返回图像的 RGB 值,因此这个处理实际上图像最终格式是 RGB 格式,但 RGB 的值都是对应的原 RGB 映射到的灰度值。
5、在帧图像变换时增加将灰度图变成 RGB 格式的灰度图处理
为了确保最终的黑白视频格式满足像素值为 RGB 格式要求,对灰度变换后的图像增加了将灰度值扩展成 RGB 格式的处理,在此用到了 numpy 的 dstack 函数。代码如下:
经验证,这样处理后的视频输出文件变成了正常播放的视频,但说实话直方图均衡处理的效果并不很好,虽然视频总体变亮了,但不同帧之间图像的差异变大了,导致视频的画面连续变化较大。
五、小结
本文通过介绍将视频帧转换为灰度图像,再构建黑白视频的处理过程所遇到的问题及解决办法,确认了无论是从输入黑白视频的像素值还是将黑白视频输出到视频文件的处理过程来看,黑白视频的帧图像不是二维的灰度图,而是对应三维的彩色图像格式,其像素值为 RGB 三元组格式,只是 R、G、B 三个分量的值都是为对应灰度图的灰度值。
更多关于 Moviepy 的介绍请大家参考《Python音视频剪辑库MoviePy1.0.3中文教程导览及可执行工具下载》。
评论