0
点赞
收藏
分享

微信扫一扫

FFmpeg中剪裁crop、绘制文字drawtext、叠加overlay滤镜在关于x坐标计算时的问题

由于本人不定期会整理相关博文,会对相应内容作出完善。因此强烈建议在原始出处查看此文。

背景

在FFmpeg中,有一个视频区域剪裁滤镜:crop。它可以在输入视频流中,根据指定的宽(w)和高(h),以及左上角顶点(x,y)坐标来进行剪裁,从而保留中心画面,并作为输出流传递给后续滤镜。如下图白色框所示,固定裁切区域的宽高,逐帧调整x坐标,即可实现画面的平移。

FFmpeg中剪裁crop、绘制文字drawtext、叠加overlay滤镜在关于x坐标计算时的问题_crop


但是在计算过程中,x坐标一直比预想的效果运行得快。并且在调试时,每一帧都输出两行。

FFmpeg命令示例

为实现该功能,以一张图片为输入源,进行静止帧后,尝试右移:

ffmpeg -y -hide_banner -i demo.jpg -filter_complex "zoompan=d=100:fps=25:s=1920x1080,\
crop=w=640:h=480:x='print(n);if(eq(n,0),0,print(x+2))'" out.mp4

上述命令中,当第一帧时,给x一个默认值:0,后续帧都在原有基础上自增2,每一次渲染先打印当前帧序号,然后对计算出的x坐标结果进行输出:

1.000000
2.000000
1.000000
4.000000
2.000000
6.000000
2.000000
8.000000
3.000000
10.000000
3.000000
12.000000

如果对于此结果感到疑惑,这样看似乎就更容易理解些:

1.000000  <---第一帧
2.000000
1.000000 <---第一帧
4.000000

2.000000 <---第二帧
6.000000
2.000000 <---第二帧
8.000000

3.000000 <---第三帧
10.000000
3.000000 <---第三帧
12.000000

有没有发现,对于同一帧,x坐标被自增了两次。而我们把坐标换成y坐标,就不会出现这个问题:

ffmpeg -y -hide_banner -i demo.jpg -filter_complex "zoompan=d=100:fps=25:s=1920x1080,\
crop=w=640:h=480:y='print(n);if(eq(n,0),0,print(y+2))'" out.mp4

结果为:

1.000000
2.000000
Last message repeated 1 times
4.000000
3.000000
6.000000
4.000000
8.000000

这里特别说明一下,如果输出的连续几条都是相同的内容,日志会自动进行折叠,以重复次数代替。
展开即为:

1.000000  <---第一帧
2.000000

2.000000 <---第二帧
4.000000

3.000000 <---第三帧
6.000000

4.000000 <---第四帧
8.000000

坐标计算正确无误。

透过源码看问题

既然发现了这个现象,那么引起该问题的原因是什么呢?由于crop属于滤镜,而滤镜都放在libavfilter库中,因此该滤镜的实现代码为:

https://github.com/FFmpeg/FFmpeg/blob/master/libavfilter/vf_crop.c

我们找到处理每一帧的函数:

static int filter_frame(AVFilterLink *link, AVFrame *frame)

下方有一段实现:

s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, NULL);
s->var_values[VAR_Y] = av_expr_eval(s->y_pexpr, s->var_values, NULL);
/* It is necessary if x is expressed from y */
s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, NULL);

先是对x坐标表达式进行了解析计算,而后对y坐标表达式进行了解析计算。那么问题来了,注释里说,为了解决在x坐标计算表达式中引用y坐标的情况,对x坐标表达式再次进行了解析。虽然这么做确实能解决引用y坐标的问题,但也带来了x坐标表达式重复计算的问题。尤其在本案例中,采用累加计算逻辑,多做一次累加,意味着其他相关的逻辑都会受到影响。

如何解决

根据上述分析,x坐标一定会多做一次计算,因此对于累加操作,可以使用临时变量,将奇偶性标记存储至FFmpeg中内置的10个变量中的任意一个

这里展开说一下,在FFmpeg的libavutil中,为我们提供了st函数和ld函数。用来在单个表达式中存储与加载临时计算变量。
st为写入函数:st([下标], [值]),返回值为参数中设置的值
ld为读取函数:ld([下标]),返回值为之前存储的值。
而上述函数中提到的下标,范围为[0,9],共10个,这有点像芯片中的寄存器。但是这个变量不能跨表达式。

那么对于上述例子中的问题具体如何解决呢?

x='if(eq(n,0),st(0,0),st(0,ld(0)+1);if(mod(ld(0),2),x,print(n);print(x+2)))'

上述逻辑的伪代码:

int tmp = 0;
function int expX(int n) {
if (n == 0) {
tmp = 0;
return 0;// 或者其它x坐标默认值
} else {
tmp++;
if (mod(tmp,2) == 0) {
return x + 2;
} else {
return x;
}
}
}
以下为不可控代码,滤镜内实现
int x; //x坐标值
// 滤镜内部处理帧循环
for (int n = 0; n <= 100; n++) {
x=expX(n);
x=expX(n);
}

上述表达式中,使用的临时变量为[0]

当第一帧时,该临时变量赋值:0,如果希望x坐标有初始值,也可以在这里设置;

当后续帧时,对该临时变量进行累加。如果此临时变量能被2整除,则说明是在同一帧下第二次被调用,此时可以进行业务的累加操作(x+2);

当临时变量不能被2整除时,说明是在同一帧下第一次被触发,此时保持x坐标不变即可。

相关地,在Drawtext滤镜和Overlay滤镜中,也有相关的逻辑
依据:​​​github: comment about the looks like a duplicate line​​


举报

相关推荐

0 条评论