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源码分析:视频滤镜介绍(上)相关推荐

  1. FFmpeg源码分析:视频滤镜介绍(下)

    FFmpeg在libavfilter模块提供音视频滤镜.所有的视频滤镜都注册在libavfilter/allfilters.c.我们也可以使用ffmpeg -filters命令行来查看当前支持的所有滤 ...

  2. FFMPEG 源码分析

    FFMPEG基本概念: ffmpeg是一个开源的编解码框架,它提供了一个音视频录制,解码和编码库.FFMPEG是在linux下开发的,但也有windows下的编译版本. ffmpeg项目由以下几部分组 ...

  3. ffmpeg源码分析-parse_optgroup

    本系列 以 ffmpeg4.2 源码为准,下载地址:链接:百度网盘 提取码:g3k8 ffmpeg 源码分析系列以一条简单的命令开始,ffmpeg -i a.mp4 b.flv,分析其内部逻辑. a. ...

  4. ffmpeg源码分析-ffmpeg_parse_options

    本系列 以 ffmpeg4.2 源码为准,下载地址:链接:百度网盘 提取码:g3k8 ffmpeg 源码分析系列以一条简单的命令开始,ffmpeg -i a.mp4 b.flv,分析其内部逻辑. a. ...

  5. ffmpeg源码分析-transcode_step

    本系列 以 ffmpeg4.2 源码为准,下载地址:链接:百度网盘 提取码:g3k8 ffmpeg 源码分析系列以一条简单的命令开始,ffmpeg -i a.mp4 b.flv,分析其内部逻辑. a. ...

  6. FFMPEG源码分析(一)

    FFMPEG源码分析(一) ffmpeg之前公司项目中就使用过,但是多停留于应用层面,实现某个功能时,需要哪些结构体以及调用哪些函数.最近想系统的学习一下ffmpeg,于是开始看雷霄骅https:// ...

  7. FFMPEG源码分析(二)

    ffmpeg源码分析之数据流 本文主要介绍ffmpeg的数据流,在ffmpeg中主要分有三个主要用途用于媒体流的解码播放,媒体流的转换(解码之后再编码)和媒体流录制. 媒体流的解码播放 在ffmpeg ...

  8. 【SemiDrive源码分析】【X9芯片启动流程】08 - X9平台 lk 目录源码分析 之 目录介绍

    [SemiDrive源码分析][X9芯片启动流程]08 - X9平台 lk 目录源码分析 之 目录介绍 一./rtos/lk/ 目录结构分析 1.1 /rtos/lk_boot/ 目录结构分析 1.2 ...

  9. 集合底层源码分析之HashMap《上》(三)

    集合底层源码分析之HashMap<上>(三) 前言 源码分析 HashMap主要属性及构造方法分析 tableSizeFor()方法源码分析 Node类源码分析 TreeNode类源码分析 ...

  10. FFmpeg源码分析-直播延迟-内存泄漏

    FFmpeg源码分析-直播延迟-内存泄漏|FFmpeg源码分析方法|ffmpeg播放为什么容易产生延迟|解复用.解码内存泄漏分析 专注后台服务器开发,包括C/C++,Linux,Nginx,ZeroM ...

最新文章

  1. matlab 值法确定各指标权重,Matlab学习系列19. 熵值法确定权重
  2. 夏季学期软工综合实践小记(二)
  3. word文档被锁定无法编辑的解决方法
  4. python 3d绘图 汉字_完美解决Python matplotlib绘图时汉字显示不正常的问题
  5. centos7.3二进制安装mariadb10.2.8
  6. 对象——从现实世界的抽象(*)
  7. 阿里云oss对象存储:给图片添加(多行)文字图片水印
  8. null怎么insert oracle,Oracle:如果行不存在,如何INSERT
  9. 高德地图提前上线多条重要道路预通车机制不断成熟
  10. 支持向量机(SVM)原理小结(3)支持向量回归SVR
  11. 神州数码笔试题C语言,神州数码笔试
  12. python怎样分析文献综述_论文的文献综述有什么方法吗?
  13. operate new、delete new和placement new
  14. 手机里重力感应器和陀螺仪的区别
  15. 毕业设计So Easy:基于Java语言西餐厅点餐系统
  16. 从 PC 卸载 Office
  17. solr分组查询、统计功能详解
  18. react中英文切换一
  19. npm install 报错(npm ERR! errno: -4048, npm ERR! code: ‘EPERM‘, npm ERR! syscall: ‘unlink‘,)解决方法
  20. ABAQUS纤维增强复合材料层合板分层和界面损伤与扩展分析-想学习ABAQUS复合材料建模分析,那就来这里!

热门文章

  1. Uber火了!它改变了哪些营销游戏规则?
  2. 如何减少手机辐射?——七招减少手机对您的危害
  3. UIQ 3 概念认识
  4. 英雄杀-如何通关挑战
  5. 浅谈《英雄杀》5人局之反贼技巧
  6. 第八节_我的日记本开发手记(8)——sqlite数据库与c#
  7. 数字化变电站与智能化变电站的关系
  8. 安装和使用所见即所得WYSIWYG的 Web 创作软件 BlueGriffon
  9. vue 所见即所得_Vue html5编辑器:Vue的html5所见即所得编辑器
  10. 一文看懂IC芯片生产流程:从设计到制造与封装