在moviepy官网的案例《Tracking and blurring someone’s face》和CSDN的moviepy大神ucsheep《MoviePy - 中文文档4-MoviePy实战案例-追踪人脸,打马赛克》都提供了追踪人脸并给影片中卓别林脸部打马赛克的样例,二者代码完全相同。
但老猿按照同样的方法实现却报ImportError错:
from moviepy.video.tools.tracking import manual_tracking, to_fxfy
ImportError: cannot import name 'to_fxfy' from 'moviepy.video.tools.tracking' (C:\Program Files\Python37\lib\site-packages\moviepy\video\tools\tracking.py)
查看tracking .py的源码,确实没有 to_fxfy方法。
为了解决该问题,老猿到处查找该问题相关资料,在ucsheep大神的QQ群内进行了咨询,并在moviepy的github项目中进行了咨询。ucsheep大神可能太忙,没有就该问题进行答复,而moviepy开源项目负责人之一tburrows13是这样答复的:
"Indeed the examples for some of these advanced tools are out of date or nonexistent (#850, #167). I'm expecting to update them soon.
In the meantime, have a look at #1061 or consider the documentation: https://zulko.github.io/moviepy/ref/videotools.html#module-moviepy.video.tools.tracking which looks like it is more up-to-date."
参考了 1061相关答复,试了下还是报错:ValueError: assignment destination is read-only
。
没办法,老猿只好阅读源代码,反复进行测试,循序渐进,曲折前进,最后实现了三种不同方式的人脸追踪打马赛克,下面就为大家介绍第一种解决思路。
追踪人脸打马赛克需要使用headblur函数。
headblur(clip,fx,fy,r_zone,r_blur=None)
其中参数fx和fy是两个函数,该函数带参数t,用于确认t时刻需要模糊化范围的中心点位置,moviepy将对以中心点为圆心半径r_zone的圆范围内的图像进行模糊化处理,模糊化处理时的卷积核大小由r_blur指定。关于r_blur参数的作用请大家参考《moviepy音视频剪辑:headblur的参数r_blur卷积核的功能作用及用途》。
manual_tracking是moviepy的工具模块moviepy.video.tools.tracking提供的函数,该模块还提供了autoTrack函数和findAround,其中findAround是供manual_tracking和autoTrack使用,autoTrack是根据匹配模式自动选择跟踪对象,后面再介绍。manual_tracking用于手工选择要跟踪的位置。
manual_tracking(clip, t1=None, t2=None, fps=None, nobjects = 1, savefile = None)
manual_tracking会根据参数 t1、t2以及fps和nobjects 设定,将剪辑 t1到t2位置的子剪辑逐帧显示出来,让操作者通过鼠标点击跟踪对象如人脸的中心,manual_tracking记下点击位置的xy坐标和对应剪辑的位置t,直到该时间段内所有帧都会,返回值为一个Trajectory对象组成的列表,列表元素的个数为nobjects参数的值指定。
从headblur的参数介绍可以获知,fx、fy是返回二者参数t所在剪辑位置的跟踪点坐标,因此老猿按照这个目标来实现了这2个函数,由于跟踪点的手工跟踪一般不会设置fps很大,如设置为2,因此实际手工跟踪的帧的数量远少于实际数量,因此这个函数需要确保没跟踪的帧在fx、fy函数中也能返回对应坐标,老猿的处理很简单粗暴,对这种帧就返回距其最近的跟踪帧的坐标。
if __name__ == '__main__':
def main():
movie_in = sys.argv[1] #参数指定的视频文件名
if len(sys.argv) == 3: #是否指定了只加载视频的前n秒,n为浮点数
subclip_s = float(sys.argv[2])
else:
subclip_s = None
clip = VideoFileClip(movie_in)
if subclip_s is not None:
clip = clip.subclip(0, subclip_s)
tracking = moviepy.video.tools.tracking.manual_tracking(clip, fps=2)[0] #取返回的第一个跟踪对象,实际上nobjects使用的是默认值1,因此也就一个跟踪对象
trackingInf = list(zip(tracking.tt, tracking.xx, tracking.yy)) #将跟踪点相关的时间、横坐标信息、纵坐标保存到列表trackingInf,即列表中每个元素为元组,该元组为(t,x,y)形式
#定义fx和fy函数:
def fx(t):
nonlocal trackingInf
t1 = t2 = None
for tc in trackingInf:
if t == tc[0]: #t即为跟踪的帧
return tc[1]
elif t > tc[0]: #记录t前面最近的跟踪帧
t1 = tc
else:#找到t之后最近的跟踪帧
if t1: #前面有跟踪帧
d1 = t - t1[0]
d2 = tc[0] - t
d = min(d1, d2)
return t1[1] if d == d1 else tc[1]
else: #t应该至少前面有个跟踪帧、后面也有个跟踪帧,否则为异常数据
return 0
return tc[1] #返回x坐标
def fy(t):
nonlocal trackingInf
t1 = t2 = None
for tc in trackingInf:
if t == tc[0]:
return tc[2]
elif t > tc[0]:
t1 = tc
else:
if t1:
d1 = t - t1[0]
d2 = tc[0] - t
d = min(d1, d2)
if d > 0.5: return 0
return t1[2] if d == d1 else tc[2]
else:
return 0
return tc[2]#返回y坐标
clip_blurred = clip.fx(vfx.headblur, fx, fy, 30) #进行模糊化处理,圆半径设置为30像素
clip_blurred.write_videofile(movie_in + '_blurred_me.mp4', bitrate="3000k") #输出文件
main()
启动参数设置为F:\video\zbl1.mp4 54
,对应视频为卓别林《淘金记》,加载视频的前4秒,跟踪设置为整个剪辑跟踪,fps为2,因此需要选择8个帧的跟踪数据。最终输出后的视频动画:
总体效果看起来还不错,就是当头部动作幅度比较大时有少许帧会遮不住。
上面介绍了老猿结合manual_tracking使用headblur实现跟踪人脸打马赛克的过程,但这种方法其实是最笨的方法,上面的代码虽然达到了想要的效果,但一点也不Pythonic,老猿也非常不满意,但实现这个方法的同时,老猿想到了更好的办法,在付费专栏的文章《moviepy音视频剪辑:追踪人脸打马赛克的三种实现方式》介绍了另外两种更好的方法。同时在付费专栏《moviepy音视频剪辑:使用autoTrack、manual_tracking+headblur实现半自动追踪人脸打马赛克
》介绍了结合autoTrack的半自动跟踪实现方案和案例。
更多moviepy的介绍请参考《PyQt+moviepy音视频剪辑实战文章目录》或《moviepy音视频开发专栏》。
老猿的付费专栏《使用PyQt开发图形界面Python应用》专门介绍基于Python的PyQt图形界面开发基础教程,付费专栏《moviepy音视频开发专栏》详细介绍moviepy音视频剪辑合成处理的类相关方法及使用相关方法进行相关剪辑合成场景的处理,两个专栏加起来只需要19.9元,都适合有一定Python基础但无相关专利知识的小白读者学习。这2个收费专栏都有对应免费专栏,只是收费专栏的文章介绍更具体、内容更深入、案例更多。
对于缺乏Python基础的同仁,可以通过老猿的免费专栏《专栏:Python基础教程目录》从零开始学习Python。
如果有兴趣也愿意支持老猿的读者,欢迎购买付费专栏。
链接:https://pan.baidu.com/s/1Ka6Zl3dR15FNkireBPmDGw
提取码:5udf