FFmpeg源码分析:视频滤镜介绍(上)
FFmpeg在libavfilter模块提供音视频滤镜。所有的视频滤镜都注册在libavfilter/allfilters.c。我们也可以使用ffmpeg -filters命令行来查看当前支持的所有滤镜,前面-v代表视频。本篇文章主要介绍视频滤镜,包括:黑色检测、视频叠加、色彩均衡、去除水印、抗抖动、矩形标注、九宫格。
关于视频滤镜的详细介绍,可查看官方文档:视频滤镜。音频滤镜可参考前面两篇文章:音频滤镜介绍(上)和音频滤镜介绍(下)。
1、blackdetect
黑色检测,用于检测纯黑的视频间隔时间。此滤波器将其分析结果输出到日志和元数据。如果找到至少具有指定最小持续时间的黑色片段,则打印一行日志,其中包含开始和结束时间戳以及持续时间。参数选项如下:
- black_min_duration, d:最短的检测黑色时长,单位为s,默认为2.0
- picture_black_ratio_th, pic_th:设置黑色图像的比率,默认为0.98
- pixel_black_th, pix_th:设置黑色像素的阈值,默认为0.10
2、blend
混合,把两个视频的所有帧混合在一起,又称为视频叠加。第一个视频在顶层,第二个视频在底层,默认为最长的视频时长作为输出时长。
- 2.1 从顶层到底层的线性水平过渡:
blend=all_expr='A*(X/W)+B*(1-X/W)'
- 2.2 从右到左覆盖,可用于转场动画过渡效果:
blend=all_expr='if(gte(N*SW+X,W),A,B)'
- 2.3 从上到下覆盖,可用于转场动画过渡效果:
blend=all_expr='if(gte(Y-N*SH,0),A,B)'
- 2.4 沿对角线分割视频,两边分别显示顶层与底层 :
blend=all_expr='if(gt(X,Y*(W/H)),A,B)'
视频混合的代码位于libavfilter/vf_blend.c,主要是遍历像素矩阵,计算顶层像素乘以一个透明度与底层像素乘以透明度的相反数之和,关键代码如下:
static void blend_normal_8bit(const uint8_t *top, ptrdiff_t top_linesize,const uint8_t *bottom, ptrdiff_t bottom_linesize,uint8_t *dst, ptrdiff_t dst_linesize,ptrdiff_t width, ptrdiff_t height,FilterParams *param, double *values, int starty)
{const double opacity = param->opacity;int i, j;for (i = 0; i < height; i++) {for (j = 0; j < width; j++) {dst[j] = top[j] * opacity + bottom[j] * (1. - opacity);}dst += dst_linesize;top += top_linesize;bottom += bottom_linesize;}
}
完整命令如下:
ffmpeg -i hello.mp4 -i world.mp4 -filter_complex blend=all_expr='A*(X/W)+B*(1-X/W)' blend.mp4
两个视频混合效果如下图所示:
3、colorbalance
色彩均衡,调整视频帧的RGB分量占比。此滤波器允许在阴影、中间色调或高光区域调整输入帧,以获得红青色、洋红或蓝黄平衡效果。正调整值将平衡移向原色,负调整值则移向补色。参数选项如下:
- rs、gs、bs:调整红、绿、蓝阴影 (最暗像素)
- rm、gm、bm:调整红、绿、蓝基调 (中间像素)
- rh、gh、bh:调整红、绿、蓝高亮(最亮像素)
4、delogo
去除水印,通过对周围像素的简单插值来抑制水印。只需要设置一个覆盖水印的矩形。矩形绘制在最外面的像素上,该像素将被替换为插值。每个方向紧靠矩形外的下一个像素的值,用于计算矩形内的插值像素值。参数如下:
- x、y:水印的左上角坐标
- w、h:水印的宽高
show:是否显示覆盖水印区域,默认为0
去除水印代码位于libavfilter/vf_delogo.c,核心代码如下:
static void apply_delogo(uint8_t *dst, int dst_linesize,uint8_t *src, int src_linesize,int w, int h, AVRational sar,int logo_x, int logo_y, int logo_w, int logo_h,unsigned int band, int show, int direct)
{int x, y;uint64_t interp, weightl, weightr, weightt, weightb, weight;uint8_t *xdst, *xsrc;uint8_t *topleft, *botleft, *topright;unsigned int left_sample, right_sample;int xclipl, xclipr, yclipt, yclipb;int logo_x1, logo_x2, logo_y1, logo_y2;xclipl = FFMAX(-logo_x, 0);xclipr = FFMAX(logo_x+logo_w-w, 0);yclipt = FFMAX(-logo_y, 0);yclipb = FFMAX(logo_y+logo_h-h, 0);logo_x1 = logo_x + xclipl;logo_x2 = logo_x + logo_w - xclipr - 1;logo_y1 = logo_y + yclipt;logo_y2 = logo_y + logo_h - yclipb - 1;topleft = src+logo_y1 * src_linesize+logo_x1;topright = src+logo_y1 * src_linesize+logo_x2;botleft = src+logo_y2 * src_linesize+logo_x1;if (!direct)av_image_copy_plane(dst, dst_linesize, src, src_linesize, w, h);dst += (logo_y1 + 1) * dst_linesize;src += (logo_y1 + 1) * src_linesize;for (y = logo_y1+1; y < logo_y2; y++) {left_sample = topleft[src_linesize*(y-logo_y1)] +topleft[src_linesize*(y-logo_y1-1)] +topleft[src_linesize*(y-logo_y1+1)];right_sample = topright[src_linesize*(y-logo_y1)] +topright[src_linesize*(y-logo_y1-1)] +topright[src_linesize*(y-logo_y1+1)];for (x = logo_x1+1,xdst = dst+logo_x1+1,xsrc = src+logo_x1+1; x < logo_x2; x++, xdst++, xsrc++) {if (show && (y == logo_y1+1 || y == logo_y2-1 ||x == logo_x1+1 || x == logo_x2-1)) {*xdst = 0;continue;}// 基于像素的相对距离进行权重插值,考虑SARweightl = (uint64_t)(logo_x2-x) * (y-logo_y1) * (logo_y2-y) * sar.den;weightr = (uint64_t)(x-logo_x1) * (y-logo_y1) * (logo_y2-y) * sar.den;weightt = (uint64_t)(x-logo_x1) * (logo_x2-x) * (logo_y2-y) * sar.num;weightb = (uint64_t)(x-logo_x1) * (logo_x2-x) * (y-logo_y1) * sar.num;interp =left_sample * weightl+right_sample * weightr+(topleft[x-logo_x1] +topleft[x-logo_x1-1] +topleft[x-logo_x1+1]) * weightt+(botleft[x-logo_x1] +botleft[x-logo_x1-1] +botleft[x-logo_x1+1]) * weightb;weight = (weightl + weightr + weightt + weightb) * 3U;interp = (interp + (weight >> 1)) / weight;// 判断是否在水印区域内if (y >= logo_y+band && y < logo_y+logo_h-band &&x >= logo_x+band && x < logo_x+logo_w-band) {*xdst = interp;} else {unsigned dist = 0;if (x < logo_x+band)dist = FFMAX(dist, logo_x-x+band);else if (x >= logo_x+logo_w-band)dist = FFMAX(dist, x-(logo_x+logo_w-1-band));if (y < logo_y+band)dist = FFMAX(dist, logo_y-y+band);else if (y >= logo_y+logo_h-band)dist = FFMAX(dist, y-(logo_y+logo_h-1-band));*xdst = (*xsrc*dist + interp*(band-dist))/band;}}dst += dst_linesize;src += src_linesize;}
}
去水印前后效果,如下图所示:
5、drawbox
绘制矩形,在视频画面绘制矩形框,可用于标注ROI兴趣区域。在人脸检测与人脸识别场景,检测到人脸时会用矩形框标注出来。参数选项如下:
- x、y:矩形的xy坐标点
- width, w、height, h:矩形的宽高
- color, c:矩形边框的颜色
- thickness, t:矩形边框的厚度,默认为3
指定xy坐标、矩形宽高、边框颜色为红色且透明度为50%,命令如下:
drawbox=x=10:y=20:w=200:h=60:color=red@0.5
6、drawgrid
绘制x宫格,可用于绘制四宫格、九宫格,模拟画面拼接,或者画面分割。参数选项如下:
- x、y:九宫格的xy坐标点
- width, w、height, h:每行宫格的宽高
- color, c:九宫格边框颜色
- thickness, t:九宫格边框厚度,默认为1
- iw、ih:输入宽高
绘制3x3的九宫格、边框厚度为2、颜色为蓝色且透明度50%,命令如下:
drawgrid=w=iw/3:h=ih/3:t=2:c=blue@0.5
7、lut、lutyuv和lutrgb
调整yuv或rgb,调整过程:计算查找表,用于绑定每个像素输入值到 输出值,并将其应用到输入视频。相关代码位于vf_lut.c,分为四种类型进行处理:packed 8bits、packed 16bits、planar 8bits、planar 16bits,关键代码如下:
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
{......if (av_frame_is_writable(in)) {direct = 1;out = in;} else {// 从缓冲区获取视频帧数据out = ff_get_video_buffer(outlink, outlink->w, outlink->h);if (!out) {av_frame_free(&in);return AVERROR(ENOMEM);}av_frame_copy_props(out, in);}// 分为packed 8bits、packed 16bits、planar 8bits、planar 16bitsif (s->is_rgb && s->is_16bit && !s->is_planar) {/* packed, 16-bits */PACKED_THREAD_DATActx->internal->execute(ctx, lut_packed_16bits, &td, NULL,FFMIN(in->height, ff_filter_get_nb_threads(ctx)));} else if (s->is_rgb && !s->is_planar) {/* packed 8 bits */PACKED_THREAD_DATActx->internal->execute(ctx, lut_packed_8bits, &td, NULL,FFMIN(in->height, ff_filter_get_nb_threads(ctx)));} else if (s->is_16bit) {/* planar 16 bits depth */PLANAR_THREAD_DATActx->internal->execute(ctx, lut_planar_16bits, &td, NULL,FFMIN(in->height, ff_filter_get_nb_threads(ctx)));} else {/* planar 8bits depth */PLANAR_THREAD_DATActx->internal->execute(ctx, lut_planar_8bits, &td, NULL,FFMIN(in->height, ff_filter_get_nb_threads(ctx)));}if (!direct)av_frame_free(&in);return ff_filter_frame(outlink, out);
}
将彩色视频转换为黑白视频,设置U和V分量为128,参考命令如下:
ffmpeg -i in.mp4 -vf lutyuv='u=128:v=128' gray.mp4
黑白视频的效果如下图所示:
对音视频感兴趣的伙伴,可以到GitHub学习:https://github.com/xufuji456/FFmpegAndroid
FFmpeg源码分析:视频滤镜介绍(上)相关推荐
- FFmpeg源码分析:视频滤镜介绍(下)
FFmpeg在libavfilter模块提供音视频滤镜.所有的视频滤镜都注册在libavfilter/allfilters.c.我们也可以使用ffmpeg -filters命令行来查看当前支持的所有滤 ...
- FFMPEG 源码分析
FFMPEG基本概念: ffmpeg是一个开源的编解码框架,它提供了一个音视频录制,解码和编码库.FFMPEG是在linux下开发的,但也有windows下的编译版本. ffmpeg项目由以下几部分组 ...
- ffmpeg源码分析-parse_optgroup
本系列 以 ffmpeg4.2 源码为准,下载地址:链接:百度网盘 提取码:g3k8 ffmpeg 源码分析系列以一条简单的命令开始,ffmpeg -i a.mp4 b.flv,分析其内部逻辑. a. ...
- ffmpeg源码分析-ffmpeg_parse_options
本系列 以 ffmpeg4.2 源码为准,下载地址:链接:百度网盘 提取码:g3k8 ffmpeg 源码分析系列以一条简单的命令开始,ffmpeg -i a.mp4 b.flv,分析其内部逻辑. a. ...
- ffmpeg源码分析-transcode_step
本系列 以 ffmpeg4.2 源码为准,下载地址:链接:百度网盘 提取码:g3k8 ffmpeg 源码分析系列以一条简单的命令开始,ffmpeg -i a.mp4 b.flv,分析其内部逻辑. a. ...
- FFMPEG源码分析(一)
FFMPEG源码分析(一) ffmpeg之前公司项目中就使用过,但是多停留于应用层面,实现某个功能时,需要哪些结构体以及调用哪些函数.最近想系统的学习一下ffmpeg,于是开始看雷霄骅https:// ...
- FFMPEG源码分析(二)
ffmpeg源码分析之数据流 本文主要介绍ffmpeg的数据流,在ffmpeg中主要分有三个主要用途用于媒体流的解码播放,媒体流的转换(解码之后再编码)和媒体流录制. 媒体流的解码播放 在ffmpeg ...
- 【SemiDrive源码分析】【X9芯片启动流程】08 - X9平台 lk 目录源码分析 之 目录介绍
[SemiDrive源码分析][X9芯片启动流程]08 - X9平台 lk 目录源码分析 之 目录介绍 一./rtos/lk/ 目录结构分析 1.1 /rtos/lk_boot/ 目录结构分析 1.2 ...
- 集合底层源码分析之HashMap《上》(三)
集合底层源码分析之HashMap<上>(三) 前言 源码分析 HashMap主要属性及构造方法分析 tableSizeFor()方法源码分析 Node类源码分析 TreeNode类源码分析 ...
- FFmpeg源码分析-直播延迟-内存泄漏
FFmpeg源码分析-直播延迟-内存泄漏|FFmpeg源码分析方法|ffmpeg播放为什么容易产生延迟|解复用.解码内存泄漏分析 专注后台服务器开发,包括C/C++,Linux,Nginx,ZeroM ...
最新文章
- matlab 值法确定各指标权重,Matlab学习系列19. 熵值法确定权重
- 夏季学期软工综合实践小记(二)
- word文档被锁定无法编辑的解决方法
- python 3d绘图 汉字_完美解决Python matplotlib绘图时汉字显示不正常的问题
- centos7.3二进制安装mariadb10.2.8
- 对象——从现实世界的抽象(*)
- 阿里云oss对象存储:给图片添加(多行)文字图片水印
- null怎么insert oracle,Oracle:如果行不存在,如何INSERT
- 高德地图提前上线多条重要道路预通车机制不断成熟
- 支持向量机(SVM)原理小结(3)支持向量回归SVR
- 神州数码笔试题C语言,神州数码笔试
- python怎样分析文献综述_论文的文献综述有什么方法吗?
- operate new、delete new和placement new
- 手机里重力感应器和陀螺仪的区别
- 毕业设计So Easy:基于Java语言西餐厅点餐系统
- 从 PC 卸载 Office
- solr分组查询、统计功能详解
- react中英文切换一
- npm install 报错(npm ERR! errno: -4048, npm ERR! code: ‘EPERM‘, npm ERR! syscall: ‘unlink‘,)解决方法
- ABAQUS纤维增强复合材料层合板分层和界面损伤与扩展分析-想学习ABAQUS复合材料建模分析,那就来这里!