使用 Python 实现视频 Logo 消除处理
一、引言
对于带 Logo(如抖音 Logo、电视台标)的视频,有三种方案进行 Logo 消除:
直接将对应区域用对应图像替换;
直接将对应区域模糊化;
通过变换将要去除部分进行填充。
其中:
方法 1 又可以使用三种方法,一是使用某固定图像替换、二是截取视频某帧的一部分图像替换、三是用每帧固定区域的图像替换当前帧的 Logo 区域,其中固定图像替换最简单,下面就不展开介绍;截取视频某帧的一部分图像比较简单,用每帧固定区域的图像替换当前帧的 Logo 区域最复杂;
方法 2 可以认为是方法 3 的特例,即填充值来源于简单计算,如 Logo 区域像素的均值等,我们在此不进行介绍;
方法 3 是以 Logo 去除后根据原 Logo 区域附近的图像像素对 Logo 区域进行插值填充,以确保填充后的图像整体比较协调、完整。
二、需要解决的问题
怎么确认 Logo 区域?当然是使用鼠标选择确认 Logo 区域最方便;
使用图像去替换 Logo 区域时,在鼠标选择过程中怎么确保替换图像大小与被替换图像大小一致?这个需有将替换图像进行裁剪或填充;
通过变换将要去除部分进行填充时,怎么确保填充值与整体视频比较协调?本文采用根据 Logo 邻近像素进行插值填充;
对于抖音这种在晃动的 Logo 怎么修复?老猿采用多次取样 Logo 区域来修复。
三、背景知识
3.1、OpenCV 视频预览方法
可以通过 cv2.imshow(winname, img)来显示一个图片,当读取视频文件的帧图片连续显示时就是一个无声的视频播放。其中的参数 winname 为一个英文字符串,显示为窗口的标题,OpenCV 将其作为窗口的名字,作为识别窗口的标识,相同名字的窗口就是同一个窗口。
对于相关窗口,OpenCV 提供鼠标及键盘事件处理机制。
3.2、OpenCV-Python 的鼠标事件捕获
OpenCV 提供了设置鼠标事件回调函数来提供鼠标事件处理的机制,设置回调函数的方法如下:
cv2.setMouseCallback(winName, OnMouseFunction, param)
其中 winName 为要设置鼠标回调处理的窗口名,OnMouseFunction 为回调函数,用于处理鼠标响应,param 为设置回调函数时传入的应用相关特定参数,可以不设置,但需要在回调函数访问设置回调函数对象属性时非常有用。
3.3、OpenCV 的几何图形绘制
OpenCV 提供了在图像中绘制几何图形的方法,绘制的图像包括矩形、椭圆、扇形、弧等。本文主要介绍矩形的绘制,具体调用语法如下:rectangle(img, pt1, pt2, color, thickness=None, lineType=None, shift=None)
其中参数:
img:要显示的图像,为 numpy 数组,格式为 BGR 格式
pt1:左上角点的坐标
pt2:右下角点的坐标
color:绘制的颜色,为 BGR 格式的三元组,如(255,0,0)表示蓝色
thickness:边框的厚度,如果为负数,则该矩形为实心矩形,否则为空心矩形
linetype:线型,包括 4 连通、8 连通以及抗锯齿线型,使用缺省值即可
shift:坐标值的精度,为 2 就表示精确到小数点后 2 位
另外该方法还有个变种调用方式:rectangle(img, rec, color[, thickness[, lineType[, shift]]])
,其中的 rec 为上面 pt1 和 pt2 构建的矩形。
3.4、Moviepy 的视频变换方法
fl_image 方法为 moviepy 音视频剪辑库提供的视频剪辑类 VideoClip 的视频变换方法,具体请参考《moviepy音视频剪辑:视频剪辑基类VideoClip的属性及方法详解》。
3.5、Python 的全局变量传值
在 python 中可以使用全局变量,关于全局变量的使用请参考《 Python函数中的变量及作用域》的介绍。
3.6、OpenCV 的图像修复方法
OpenCV 中的 cv2.inpaint()函数使用插值方法修复图像,调用语法如下:
dst = cv2.inpaint(src,mask, inpaintRadius,flags)
参数含义如下:
src:输入 8 位 1 通道或 3 通道图像
inpaintMask:修复掩码,8 位 1 通道图像。非零像素表示需要修复的区域
dst:输出与 src 具有相同大小和类型的图像
inpaintRadius:算法考虑的每个点的圆形邻域的半径
flags:修复算法标记,其中 INPAINT_NS 表示基于 Navier-Stokes 方法,INPAINT_TELEA 表示 Alexandru Telea 方法。具体方法在此不展开介绍
3.7、OpenCV 的颜色空间转换方法
cv2.cvtColor 是 openCV 提供的颜色空间转换函数,调用语法如下:
cvtColor(src, code, dstCn=None)
其中:
src:要转换的图像
code:转换代码,表示从何种类型的图像转换为何种类型,如下面需要使用的 cv2.COLOR_BGR2GRAY 就是将 BGR 格式彩色图像转换成灰度图片
dstCn:目标图像的通道数,如果为 0 表示根据源图像通道数以及转换代码自动确认
3.8、图像阈值处理
openCV 图像的阈值处理又称为二值化,之所以称为二值化,是它可以将一幅图转换为感兴趣的部分(前景)和不感兴趣的部分(背景)。转换时,通常将某个值(即阈值)当作区分处理的标准,通常将超过阈值的像素作为前景。
阈值处理有 2 种方式,一种是固定阈值方式,又包括多种处理模式,另一种是非固定阈值,由程序根据算法以及给出的最大阈值计算图像合适的阈值,再用这个阈值进行二值化处理,非固定阈值处理时需要在固定阈值处理基础上叠加组合标记。
调用语法:
retval, dst = cv2.threshold (src, thresh, maxval, type)
其中:
src:源图像,8 位或 32 位图像的 numpy 数组
thresh:阈值,0-255 之间的数字,在进行处理时以阈值为边界来设不同的输出
maxval:最大阈值,当使用固定阈值方法时为指定阈值,当叠加标记时为允许最大的阈值,算法必须在小于该值范围内计算合适的阈值
type:处理方式,具体取值及含义如下:
dst:阈值化处理后的结果图像 numpy 数组,其大小和通道数与源图像相同
retval:叠加 cv2.THRESH_OTSU 或 cv2.THRESH_TRIANGLE 标记后返回真正使用的阈值
案例:ret, mask = cv2.threshold(img, 35, 255, cv2.THRESH_BINARY|cv2.THRESH_OTSU)
补充说明:
阈值判断时,是以小于等于阈值和大于阈值作为分界条件
如果是 32 位彩色图像,则是以 RGB 每个通道的值单独与阈值进行比较,按每个通道进行阈值处理,返回的是一个阈值处理后的 RGB 各自的值
3.9、图像膨胀处理
关于膨胀处理的知识解释有点复杂,请参考《OpenCV-Python学习—形态学处理》以及《Opencv python 锚点anchor位置及borderValue的改变对膨胀腐蚀的影响》。图像的膨胀处理会使得图像中较亮的区域增大,较暗的区域减小。
四、具体实现
本部分介绍的内容对 Logo 去除采用了如下四种方式:
使用视频中某帧图像的指定区域内容替换 Logo
使用视频中每帧图像的指定区域内容替换当前帧的 Logo 区域
Logo 区域采用图像修复
多 Logo 区域采样图像修复
其中第四种方法是 Logo 区域的 Logo 在视频中为晃动的内容(如抖音的 Logo)时需要,如果是静止不变的 Logo 用第三种方法就够了。
以上四种处理方式,对应的消除 Logo 方法类型分别为:
4.1、实现思路
为了实现 Logo 标记的消除,具体步骤如下:
展现视频并设置鼠标回调函数;
识别鼠标动作用鼠标在视频图像中圈定 Logo 位置;
根据不同方法确认是否需要选择替换图像;
对视频中的每帧图像进行图像处理。
4.2、实现鼠标回调函数
这是一个比较通用的鼠标回调函数,代码如下:
所有使用该回调函数处理鼠标事件的对象,必须将自身通过 param 传入到回调函数中,并且必须定义一个 processMouseEvent(self)方法来处理鼠标事件。下面介绍的类 CImgMouseEvent 就是满足条件的类。
4.3、视频图像展现窗口的鼠标事件处理类
为了支持在视频图像中进行相关操作,需要比较方便的支持并识别鼠标操作的类,在此称为 CImgMouseEvent, CImgMouseEvent 用于 OpenCV 显示图像的窗口的鼠标事件处理,会记录下前一次鼠标左键按下或释放的位置以及操作类型,并记录下当前鼠标移动、左键按下或释放事件的信息。
4.3.1、CImgMouseEvent 关键属性
mouseIsPressed:表示鼠标左键当前是否为按下状态
playPaused:表示当前窗口播放视频(就是连续显示视频的帧)是否暂停状态,当鼠标左键按下时,播放暂停,通过鼠标左键双击或右键点击继续播放
previousePos、pos:上次和本次鼠标事件的位置
previousEvent、event:上次和本次鼠标事件的类型
parent:为创建 CImgMouseEvent 对象的调用者,该对象必须定义一个 processMouseEvent 方法,用于当鼠标事件执行时的具体操作
winName:CImgMouseEvent 处理鼠标事件所属的窗口名
img:在窗口中当前显示的图像对象,可以通过 showImg 显示图像并改变 winName、img
以上鼠标事件属性的记录处理都在 CImgMouseEvent 的方法 processMouseEvent 中,但 processMouseEvent 方法仅记录鼠标事件属性,记录后调用父对象的 parent 的 processMouseEvent 方法实现真正的操作。
4.3.2、CImgMouseEvent 主要方法
processMouseEvent:鼠标事件回调函数调用该方法记录鼠标事件数据,并由该方法调用父对象的 processMouseEvent 方法实现真正的操作
showImg:在窗口 winName 中显示 img 图像,并设置鼠标回调函数为 OnMouseEvent
getMouseSelectRange:获取鼠标左键按下位置到当前鼠标移动位置或左键释放位置的对应的矩形以及矩形最后位置的鼠标事件类型,如果无都有操作则返回 None
drawRect:画下当前鼠标事件选择的矩形或参数指定的矩形,一般供父对象调用
drawEllipse:画下当前鼠标事件选择的矩形或参数指定的矩形的内接椭圆,一般供父对象调用
4.3.3、 CImgMouseEvent 类实现代码
4.4、定义视频图像处理类
CSubVideoImg 类用于操作视频及视频的图像,主要用于对一个视频的帧图像进行操作。
4.4.1、CSubVideoImg 关键属性
replaceObject:替换图对象, 类型为四元组,分别对应 replaceImg, replaceRect, targetReplaceImg, frame,用于前两种消除方法,存储选择的替换图像、替换图像区域矩形、按照 Logo 区域进行替换图像裁剪和填充后的静态替换图像、以及替换图像选择时所在的帧图像
logoObjectList:列表,1…n 个元素(多次采样 Logo 区域图像时 n 大于 1),每个元素是个二元组,每个二元组表示一个 logo 图像信息,包括图像的数组以及图像的位置及大小等信息,形如:[(logoImg1,logoRect1),…,(logoImgn,logoRectn)],除了第四种消除方法,前面三种处理方法都只取最后一个元素使用,即最后选择的 Logo 图像有效
frameMask:记录下 Logo 图像掩码的帧,该帧除了 Logo 图像对应的掩码内容外,其他部分全为 0
multiFrameMask:多次采样的 frameMask 叠加
4.4.2、CSubVideoImg 主要方法
processMouseEvent:响应鼠标事件的方法
drawSelectRange:画出当前鼠标左键选择的范围,目前可以画矩形或椭圆
setVideoClipRect:按指定帧率播放视频(仅图像),并提供在视频图像中选中某个矩形范围,并在接下来播放中一直显示该矩形,按 EsC 或 q 或 Q 退出
getROI:在 setVideoClipRect 基础上返回选中 ROI 图像、并显示该 ROI 图像,可以获取视频中的多个 ROI 区域,选定一个 ROI 区域后,按 N、n、S、s 保存当前选择区域,按退出键会保存最后一个区域
replaceImgRegionBySpecImg:将指定图像的指定位置的一个矩形图像替换为参数指定图像内容
replaceImgRegionBySpecRange:将指定图像的指定位置的一个矩形范围内的图像替换为该图像内另一个矩形矩形范围对应的内容
adjuestImgAccordingRefImg:将指定图像大小调整为参数指定的参考图像的大小,如果指定图像大小超出参考图像则对原图像进行裁剪,否则对指定图像进行扩充
createImgMask:生成一个图像的掩码图像,采用转换为灰度图像后再进行图像阈值处理、再进行膨胀处理后返回该处理后的图像
genLogoFrameMask:将 Logo 图像的掩码图像与视频帧大小的全 0 图像叠加后生成的帧掩码图像
genMultiLogoFrameMask:将多个 Logo 图像生成的帧掩码图像叠加生成的帧掩码图像
convertVideo:将消除 Logo 图像的视频输出
previewVideoByReplaceLogo:预览图像替换消除 Logo 的视频
previewVideoByInpaintLogo:预览图像修复术消除 Logo 的视频
4.4.3、CSubVideoImg 类实现代码
上面相关定义的与视频预览、帧预览等方法定义时的参数包括了记录下完整 Logo 采用对象、替换对象、以及 Logo 掩码等,这些数据需要在操作视频图像时记录并在视频处理时传递给上述方法。
4.5、视频图像处理函数
上面视频图像处理类中使用了 processImg 函数,该函数用于视频生成的帧图像处理函数,用静态图像或同帧区域范围图像替换,或使用图像修复术修复。
在 processImg 函数中,使用了全局变量来传递该函数调用时的 CSubVideoImg 类对象及 Logo 消除的方式。具体实现就二十行代码,大家可以参考视频变换的介绍自己去实现。
4.6、主程序
主程序根据 Logo 消除类型来显示视频执行 Logo 图像选择、替换图像选择(前 2 种 Logo 消除类型)后,将视频进行消除处理。
上面的代码是以最复杂的 多 Logo 区域采样图像修复,可以给 main 函数传其他参数执行其他消除方式。
4.7、注意
程序执行需注意:
如果是多 Logo 区域采样修复方式消除 Logo,必须多次采样 Logo 区域图像,否则与 Logo 区域采样修复效果相同;
如果前三种方式 Logo 采样了多次,则只取最后一次采样进行处理;
视频播放采样时,通过 q、Q、ESC 三个键中的任意一个退出播放
视频采样时,通过 n、N、s、S 以及退出键都会保存当前选择的图像数据(必须画面上出现蓝色矩形);
视频采样时,鼠标左键按下会暂停播放等待采样完成,当采样完成(蓝色矩形选中且保存了当前采样区域)或放弃采样后可以通过鼠标右键点击或鼠标左键双击恢复播放;
视频采样时,蓝色边框出现后可通过重新选择范围。
五、程序执行效果
下面是一个多次 Logo 采样进行图像修复的运行案例截图。
1、视频 Logo 采样案例
采样左上角的 Logo,由于“抖音”二字播放时不停晃动,需要采样多次,尽量确保“抖音”二字在不同位置都有采样,下面只提供了一次截图:
针对右下角的 Logo 信息多次截图,下面是其中的一次截图:
2、处理后的视频截图
可以看到两个角落的 Logo 都消除了。
六、小结
本文详细介绍了消除视频 Logo 图标的几种方法以及涉及的背景知识,并提供了一套 Python+Moviepy+OpenCV 实现的消除视频 Logo 的代码。通过本文,可以了解视频剪辑处理结合 OpenCV 图像融合操作的相关知识。
评论