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

Posted Jack_Chai

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FFmpeg中剪裁crop绘制文字drawtext叠加overlay滤镜在关于x坐标计算时的问题相关的知识,希望对你有一定的参考价值。

本文出处:http://blog.csdn.net/chaijunkun/article/details/111336236,转载请注明。由于本人不定期会整理相关博文,会对相应内容作出完善。因此强烈建议在原始出处查看此文。

背景

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


但是在计算过程中,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

以上是关于FFmpeg中剪裁crop绘制文字drawtext叠加overlay滤镜在关于x坐标计算时的问题的主要内容,如果未能解决你的问题,请参考以下文章

ffmpeg # 为视频添加文字drawtext

自定义View drawText()绘制文字

Canvas drawText文字垂直居中方案

[RN] React Native 使用开源库 react-native-image-crop-picker 实现图片选择图片剪裁

如何使用Canvas绘制drawText旋转角度

drawText注意事项