一、引言
在《moviepy1.03音视频剪辑:使用manual_tracking和headblur实现追踪人脸打马赛克》介绍了使用手动跟踪跟踪人脸移动轨迹和使用headblur对人脸进行打马赛克。
实际上,moviepy除了使用manual_tracking手动追踪,另外还提供了autoTrack自动追踪的方式,该方式提供需要匹配的跟踪对象的图像数组,自动在剪辑对应帧中去查找跟踪对象。对于一个较长的剪辑来说,自动追踪可以避免选择多个帧来手动跟踪目标对象。
对一个较长剪辑来说,结合手动跟踪和自动跟踪实现一种半自动跟踪的方式追踪人脸打马赛克是一种相对来说节约操作时间的方式,本文将介绍这种方式的实现。
二、实现思路
要实现半自动跟踪,首先必须取得匹配的模板图像,半自动跟踪的思路就是通过手工方式获取需要跟踪的模板图像。具体实现步骤如下:
1、确认视频中需要跟踪对象的关键位置时间点,这些点跟踪对象的人脸图像较前一刻有比较大的变化,如正脸、侧脸、大图像、小图像等情况。将这些点(t1、t2、t3…,tn)分别作为手动跟踪选择目标对象的关键点;
2、对每个关键点调用manual_tracking获取跟踪对象的中心位置,并将对应范围的图像记录到需要匹配的模板图像列表中;
3、将要跟踪剪辑的时间段(traceStartPos----traceEndPos)按照关键点分隔成多个剪辑,分别与上步保存的模板图像进行匹配跟踪和模糊化,得到的新剪辑进行保存;
4、将模糊后的所有剪辑进行拼接,并与原剪辑中没进行模糊化的部分进行拼接,最后输出新剪辑即是半自动人脸根据并打马赛克的剪辑。
三、实现代码
3.1、获取跟踪对象指定时刻跟踪位置对应图像
def getSample(clip,t,tracking,r_zone):
x,y = tracking.xx[0],tracking.yy[0]
x1, x2 = max(0, x - r_zone), min(x + r_zone, clip.size[0])
y1, y2 = max(0, y - r_zone), min(y + r_zone, clip.size[1])
frame = clip.get_frame(t)
sampleImg = frame[y1:y2, x1:x2]
return sampleImg
以上函数中,clip是源剪辑,t为跟踪关键的时间位置,tracking为手动跟踪获取的跟踪对象,r_zone为跟踪人脸的圆半径。
该函数时间是取跟踪点为中心的一个正方形范围的图像,正方形的边长为2*r_zone。
3.2、初始化处理
参数获取后,进行视频加载、设置关键取样时间点等初始化处理:
#加载原始剪辑,并对未指定跟踪结束位置的情况将结束位置设置为剪辑时长
clip = VideoFileClip(movie_in)
if traceEndPos is None or (traceEndPos>=clip.duration) :
traceEndPos = clip.duration
endClip = None
else:
endClip = clip.subclip(traceEndPos,clip.duration) #保留剪辑最后一段
sampleTime = [0, 3,7,14,15,20,24,27,32] #设置关键取样时间点,不同剪辑根据自身情况设置
clips = []
t1 = 0
if sampleTime[0]:
if sampleTime[0]!= traceStartPos:
raise ValueError("手工跟踪的第一个时间点必须与自动跟踪的起点相同")
clips.append(clip.subclip(0, sampleTime[0])) #保留剪辑跟踪前的开始部分
sampleImg = None
3.3、按时间段及采样图像进行人脸跟踪打马赛克
按采样时间点逐一进行手工采样并根据采样对象进行跟踪匹配,匹配完成后进行马赛克处理:
for t in sampleTime: #每个采样点进行处理
if t<traceStartPos or t>traceEndPos:
raise ValueError("手工跟踪的时间点必须介于开始时间点和结束时间点之间")
tracking = manual_tracking(clip, t, fps=1)[0] #手工获取跟踪对象
preSampleImg = sampleImg #保留上一采样图像,所有时间段匹配都是采用开始位置的跟踪对象图像进行匹配
sampleImg = getSample(clip,t,tracking,targetRadius) #获取跟踪点对应范围的图像
if t > traceStartPos:#不是第一个采用点位置
tempClip = clip.subclip(t1,t)
traceTimes = np.arange(0, t-t1, 1.0 / 2)
autoTrackObjects = autoTrack(tempClip, preSampleImg, tt=traceTimes, radius=targetRadius)
clip_blurred = tempClip.fx(vfx.headblur, autoTrackObjects.xi, autoTrackObjects.yi, targetRadius)
clips.append(clip_blurred)
t1 = t
if t<traceEndPos:#如果最后一个采用时间点小于要跟踪的视频段结束位置时间点
tempClip = clip.subclip(t, traceEndPos)
traceTimes = np.arange(0, traceEndPos - t, 1.0 / 2)
autoTrackObjects = autoTrack(tempClip, sampleImg, tt=traceTimes, radius=targetRadius)
clip_blurred = tempClip.fx(vfx.headblur, autoTrackObjects.xi, autoTrackObjects.yi, targetRadius)
clips.append(clip_blurred)
if endClip:clips.append(endClip) #如果跟踪结束位置不是剪辑结束位置,则需要将结束位置后面的剪辑保留
clip_blurred = concatenate_videoclips(clips) #拼接剪辑
clip_blurred.write_videofile(movie_in + '_blurred_autotrack.mp4')
四、执行效果
设置命令行参数为:F:\video\zbl1.mp4 50 0
最后转换的视频前3秒播放效果如下:
从上面效果可以看到,使用自动跟踪时精度比手动跟踪精确度要差一些。
五、小结
本文详细介绍了使用手工跟踪和自动跟踪相结合实现半自动追踪人脸打马赛克的详细实现过程,由于自动追踪无法精确判断对应帧中是否有跟踪对象,如果一个帧无对应跟踪对象它也会找到一个认为比较匹配的区域,另外有跟踪对象的帧中也不能完全保障精确匹配,因此精确度比手动跟踪要低。具体精确度与手工选择帧的时间位置、选取点的坐标位置、图像选择范围的半径以及原始剪辑的图像对比度、亮度等都会有关
更多moviepy的介绍请参考《PyQt+moviepy音视频剪辑实战文章目录》或《专栏:使用PyQt开发图形界面Python应用》。
广告
老猿关于PyQt的付费专栏《使用PyQt开发图形界面Python应用》只需要9.9元(老猿正在考虑是否调整专栏价格),本专栏《PyQt+moviepy音视频剪辑实战》文档的同样内容在付费专栏上也有相应内容,总体来说付费专栏介绍更详细或案例更多。
本节内容对应付费专栏的《moviepy音视频剪辑:视频半自动追踪人脸打马赛克》。如果有兴趣也愿意支持老猿的读者,欢迎购买付费专栏。