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坐标计算时的问题的主要内容,如果未能解决你的问题,请参考以下文章
[RN] React Native 使用开源库 react-native-image-crop-picker 实现图片选择图片剪裁