滤波(filter):去除不想要的干扰,噪声,颜色等。ffmpeg 大大扩展了filter, post-process这两个概念。
后处理去方块效应
problem of block artifacts
smooth slowing-changing regiongs -->
ramp-like gradient, piece-wise constant
平坦区域的亮度跳跃。
跨边界出现锯齿。
reason:
DCT retain only one subband, the DC coeff constant signal
平坦相邻宏块的交界处,因量化步长不同导致本来很接近的像素值
重构后产生较大差异。
跨边界相邻宏块:dct高频系数被量化为0。
运动补偿加据了这两种情形。
去块效应滤波,就是使边界平滑,
h264处理办法:假设真实边界两侧的像素值梯度比量化误差造成的虚假边界
要大。跟据经验测试数据,定义两个门限alpha, beta,
设竖直边界两侧为p3,p2,p1,p0|q0,q1,q2,q3。
若|p0-q0| < alpha, |p1-q1| < beta, |q1-q0| < beta,
则认为应当进行滤波,详见h264_template.c:h264_loop_filter_luma_intra()
h264_loopfilter.c还计算了块边界强度bS,用来决定滤波强度,
即多少个像素参与。平坦区域滤波可以强,复杂区域为保留细节,滤波要弱。
文字描述可参考[3]
ffmpeg处理:
制作一个低质量的图片:
ffmpeg -i whale2.jpg -q:v 31 -s 400x300 low-quality.jpg
指定最高质量也有方块:
ffmpeg -i low-quality.jpg -q:v 1 -y xx.jpg
加上方块过滤器:
ffmpeg -i low-quality.jpg -q:v 1
-vf pp=hb/vb -y yy.jpg
对比如下图:
decode_video ffmpeg.c:1983 -->
av_buffersrc_add_frame_flags-->av_buffersrc_add_frame_internal-->
request_frame-->ff_filter_frame-->ff_filter_frame_framed-->
filter_frame-->ff_filter_frame-->ff_filter_frame_framed-->
1.pp_postprocess里面默认只对Y分量postProcess-->postProcess_SSE2(postprocess_template.c:3251)
水平方向9-tap filter,可参考[2]。
把图片放在3x3格子中央:
ffplay cat.jpg -vf 'pad=3*iw:3*ih:iw:ih:blue'
并排显示两张图片:
ffmpeg -i cat.jpg -i bee.jpg -filter_complex '[0:v]scale=100:100, pad=2*iw:ih:0:0:blue[a]; [1:v]scale=100:100[b]; [a][b]overlay=102:0' -y 2.jpg
一个大视频叠加上一个小视频就是
Picture In Picture:
ffmpeg -i 0Cannon.f4v -i slamtv60.264 -i slamtv60.264 -filter_complex '[1:v]scale=100:100[b]; [0:v][b]overlay=80:80' -y xx.mp4
ffmpeg -i master_video.mp4 -vf "movie=smaller_inner_video.mp4, fade=out:300:30:alpha=1 [inner];
[in][inner] overlay=70:70 [out]" completed.mp4
为了测试video filter 可以采用 ffmpeg -vf ... - | ffplay - 的方式。
但是windows 悲催的管道是文本的。
代码分析
int filter_frame(AVFilterLink *inlink, AVFrame *in)
in -- 输入数据
inlink->dst->outputs[0] --out link
crop filter核心操作只是修改一下in->data指针而已,应该算是最简单的filter。
下列两种情形,
transcode_init()。
decode_video() avcodec_decode_video2之后,如果采样率变了。
则调用
configure_filtergraph-->avfilter_graph_parse2
场景1:
b vf_
crop.c:filter_frame
r -i r.png -vf crop=400:300:30:30 -y r2.png
(gdb) bt
#0 filter_frame vf_crop.c:244 最后调ff_filter_frame()递归下去
#1 ff_filter_frame_framed
#2 ff_filter_frame avfilter.c:1161
#3 request_frame buffersrc.c:499
#4 av_buffersrc_add_frame_internal buffersrc.c:181
#5 av_buffersrc_add_frame_flags buffersrc.c:106
#6 decode_video ffmpeg.c:1974 for(ist->filters[])多个源
#7 output_packet
#8 process_input
#9 transcode_step
#10 transcode
transcode_step-->
process_input
reap_filter
av_buffersink_get_frame_flags(filter) while(1)方式遍历每个输出
do_video_out-->avcodec_encode_video2
do_wideo_out 断点,
(gdb) p in_picture->linesize
$4 = {1376, 0, 0, 0, 0, 0, 0, 0}
源图片宽度是1366, 这样就可以解释为什么crop里面没有设置linesize了。
(gdb) p in_picture->width
$5 = 400
(gdb) p in_picture->height
$6 = 300
在pngenc.c:encode_frame里面,引用height和linesize[0],png_get_interlaced_row。
场景2(
倒影:多条filter chains)
r -i r2.png -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" -y r3.png
每个filter_frame都断点, 执行顺序:
split-->overlay-->crop-->vflip-->overlay
最后一个断点的栈帧:
#0 filter_frame vf_overlay.c:578
#1 ff_filter_frame_framed ff_filter_frame
#3 filter_frame vf_scale.c:530
#4 ff_filter_frame_framed ff_filter_frame
#6 filter_frame gvf_vflip.c:84
#7 ff_filter_frame_framed ff_filter_frame
#9 filter_frame vf_crop.c:297
#10 ff_filter_frame_framed ff_filter_frame
#12 filter_frame split.c:86
#13 ff_filter_frame_framed ff_filter_frame
#15 request_frame av_buffersrc_add_frame_internal
#17 av_buffersrc_add_frame_flags
#18 decode_video output_packet process_input
info break 确认overlay被执行了两次,其他都一次。
可以理解为有向无环图的dfs遍历:
split-->overlay
-->crop-->vflip-->scale-->overlay
在split.c:filter_frame()里面ctx->nb_outputs=2开始分支的。
如何知道一个link结束了?只要看link->dstpad->filter_frame是空就表明结束了,交给
deault_filter_frame-->ff_filter_frame-->ff_filter_frame_framed
-->filter_frame buffersink.c:104 -->add_buffer_ref(ctx, frame);
可以理解为:buffersink是filter graph的虚拟汇点,而request_frame是它的虚拟源点;
分别为总出口和总入口。这个叙述忽略了process_input之前的transcode_from_filter,
我没有完全理解它的意义,只知道像vf_overlay只在process_input栈帧里面。
avfilter_process_command 应该跟-vf sendcmd有关,未验证。
其他一些有趣的filter
o Build a 5.1 output from 6 single-channel streams.
o Make music with quiet and loud passages suitable for noisy environment
o boxblur --模糊的我的近视眼分不清数字了,可以用来测OCR。
o negate -- 不仅仅是黑白颠倒。
o noise=alls=20:allf=t+u --老电影,还是彩色的喔。
o tile=2x2 --监控大屏幕
o edgedetect -- 铅笔手绘卡通风格
Ref
[1] https://www.ffmpeg.org/ffmpeg-filters.html#pp
[2] http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/
[3] 陈靖等,深入理解视频编解码技术--基于H.264标准