视频特效-使用ffmpeg滤镜

前言

ffmpeg的滤镜分为简单滤镜和复杂滤镜。
复杂滤镜存在多个输入和多个输出如图:

在命令行中可以通过 -filter_complex-lavfi 来使用。
简单滤镜则只有一个输入和输出,如图:

在命令行中可以通过-vf来使用。
ffmpeg中的滤镜有很多,我们可以组合使用他们。举个例子,下图中就使用了多个滤镜(split , crop, vflip,overlay)。split是将输入数据拆分城两份,第一份为main,第二份为tmp。然后再通过crop 裁剪tmp,再通过vflip竖直反转得到flip。然后通过overlay将flip叠加到main上输出。
这些滤镜构成了一个图。每个滤镜都有输入和输出,上一个滤镜的输出是下一个滤镜的输入。

                [main]
input --> split ---------------------> overlay --> output|                             ^|[tmp]                  [flip]|+-----> crop --> vflip -------+

上面的这个滤镜图对应的命令为

ffmpeg -i INPUT -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" OUTPUT

其中中括号括起来的是对滤镜输出的命名,也可以不指定名称。分号隔开的是每个滤镜链。如果属于同一个链中的滤镜则只需要通过逗号隔开。

我们用张图片试一试上面的命令。原图:

执行命令:

ffmpeg -i lyf1.jpeg -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0,vflip [flip]; [main][flip] overlay=0:H/2"  output.jpeg


我们也可以直接通过ffplay直接使用滤镜看效果。

ffplay -i lyf1.jpeg -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0,vflip [flip]; [main][flip] overlay=0:H/2"

再来看一个颜色滤镜:hue,hue可以改变图片的色彩,它的参数h代表颜色,取值范围为0~360,它的s代表的是饱和度取值范围是-10~10,它的参数b代表的是亮度,取值范围为-10~10。这些参数所代表的颜色可以参考下图:

我们用命令试一下:

ffplay -i lyf1.jpeg -vf "hue=90:s=1:b=0"

原图变为了:

实践

上面的命令实现的功能,我们一定能用代码实现。
流程如下图:

创建滤镜

void ImageProcessor::initializeFilterGraph() {// 创建滤镜图实例this->filter_graph_ = avfilter_graph_alloc();// 获取输入滤镜的定义const auto avfilter_buffer_src = avfilter_get_by_name("buffer");if (avfilter_buffer_src == nullptr) {throw std::runtime_error("buffer avfilter get failed.");}// 获取输出滤镜的定义const auto avfilter_buffer_sink = avfilter_get_by_name("buffersink");if (avfilter_buffer_sink == nullptr) {throw std::runtime_error("buffersink avfilter get failed.");}// 创建输入实体this->filter_inputs_ = avfilter_inout_alloc();// 创建输出实体this->filter_outputs_ = avfilter_inout_alloc();if (this->filter_inputs_ == nullptr || this->filter_outputs_ == nullptr) {throw std::runtime_error("filter inputs is null");}char args[512]{};snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",this->width_,this->height_,1,1,avframe_origin_->format,avframe_origin_->sample_aspect_ratio.num,avframe_origin_->sample_aspect_ratio.den);// 根据参数创建输入滤镜的上下文,并添加到滤镜图int ret = avfilter_graph_create_filter(&this->buffer_src_ctx_, avfilter_buffer_src, "in",args, nullptr, this->filter_graph_);if (ret < 0) {throw std::runtime_error("create filter failed .code is " + std::to_string(ret));}// 根据参数创建输出滤镜的上下文,并添加到滤镜图ret = avfilter_graph_create_filter(&this->buffer_sink_ctx_, avfilter_buffer_sink, "out", nullptr,nullptr, this->filter_graph_);if (ret < 0) {throw std::runtime_error("create filter failed code is " + std::to_string(ret));}AVPixelFormat pix_fmts[] = {AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_NONE};ret = av_opt_set_int_list(buffer_sink_ctx_, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE,AV_OPT_SEARCH_CHILDREN);// 为输入滤镜关联滤镜名,滤镜上下文this->filter_outputs_->name = av_strdup("in");this->filter_outputs_->filter_ctx = this->buffer_src_ctx_;this->filter_outputs_->pad_idx = 0;this->filter_outputs_->next = nullptr;// 为输出滤镜关联滤镜名,滤镜上下文this->filter_inputs_->name = av_strdup("out");this->filter_inputs_->filter_ctx = this->buffer_sink_ctx_;this->filter_inputs_->pad_idx = 0;this->filter_inputs_->next = nullptr;// 根据传入的字符串解析出滤镜,并加入到滤镜图ret = avfilter_graph_parse_ptr(this->filter_graph_,this->filter_desc_.c_str(),&this->filter_inputs_,&this->filter_outputs_,nullptr);if (ret < 0) {throw std::runtime_error("avfilter graph parse failed .code is " + std::to_string(ret));}ret = avfilter_graph_config(this->filter_graph_, nullptr);if (ret < 0) {throw std::runtime_error("avfilter graph config failed.");}
}

使用滤镜

    // send to filter graph.int ret = av_buffersrc_add_frame(this->buffer_src_ctx_, this->avframe_origin_);if (ret < 0) {throw std::runtime_error("add frame to filter failed code is " + std::to_string(ret));}ret = av_buffersink_get_frame(this->buffer_sink_ctx_, this->avframe_dst_);if (ret < 0) {throw std::runtime_error("get avframe from sink failed code is " + std::to_string(ret));}

其中avframe_orgin_为输入frame,avframe_dst_为输出frame。

完整代码

ImageProcessor.h

#ifndef FFMPEG_DECODER_JPEG_IMAGEPROCESSOR_H
#define FFMPEG_DECODER_JPEG_IMAGEPROCESSOR_H#include <string>extern "C" {#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>#include <libavfilter/avfilter.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>#include <libavutil/avutil.h>
#include <libavutil/opt.h>
}using namespace std;class ImageProcessor {public:ImageProcessor();~ImageProcessor();void setInput(string input);void setOutput(string output);void setFilterString(string filter_desc);void initialize();void process();private:void initializeFilterGraph();string input_path_;string output_path_;string filter_desc_;AVFormatContext *avformat_ctx_;AVCodecContext *avcodec_decoder_ctx_;AVCodecContext *avcodec_encoder_ctx_;const AVCodec *avcodec_encoder_;AVPacket *avpacket_origin_;AVFrame *avframe_origin_;AVFrame *avframe_dst_;AVPacket *avpacket_dst_;int width_;int height_;AVFilterContext *buffer_src_ctx_;AVFilterContext *buffer_sink_ctx_;AVFilterGraph *filter_graph_;AVFilterInOut * filter_inputs_;AVFilterInOut * filter_outputs_;};#endif //FFMPEG_DECODER_JPEG_IMAGEPROCESSOR_H

ImageProcess.cpp

#include "ImageProcessor.h"
#include <fstream>ImageProcessor::ImageProcessor() {}ImageProcessor::~ImageProcessor() {avformat_free_context(avformat_ctx_);av_packet_free(&avpacket_origin_);av_packet_free(&avpacket_dst_);av_frame_free(&avframe_origin_);av_frame_free(&avframe_dst_);avfilter_inout_free(&filter_inputs_);avfilter_inout_free(&filter_outputs_);if (buffer_src_ctx_) {avfilter_free(buffer_src_ctx_);}if (buffer_sink_ctx_) {avfilter_free(buffer_sink_ctx_);}avfilter_graph_free(&filter_graph_);
}void ImageProcessor::setInput(string input) {this->input_path_ = std::move(input);
}void ImageProcessor::setOutput(string output) {this->output_path_ = std::move(output);
}void ImageProcessor::setFilterString(string filter_desc) {this->filter_desc_ = std::move(filter_desc);
}/*** Before initialize you should setInput,setOutput,setFilterString first.*/
void ImageProcessor::initialize() {avpacket_origin_ = av_packet_alloc();avpacket_dst_ = av_packet_alloc();avframe_origin_ = av_frame_alloc();avframe_dst_ = av_frame_alloc();avformat_ctx_ = avformat_alloc_context();//  open formatint ret = avformat_open_input(&avformat_ctx_, this->input_path_.c_str(), nullptr, nullptr);if (ret < 0) {throw std::runtime_error("avformat open input error code is : " + std::to_string(ret));}// init decoderconst auto codec_decoder = avcodec_find_decoder(avformat_ctx_->streams[0]->codecpar->codec_id);if (codec_decoder == nullptr) {throw std::runtime_error("avcodec find decoder failed.");}avcodec_decoder_ctx_ = avcodec_alloc_context3(codec_decoder);ret = avcodec_parameters_to_context(avcodec_decoder_ctx_, avformat_ctx_->streams[0]->codecpar);if (ret < 0) {throw std::runtime_error("avcodec_parameters_to_context failed code is " + std::to_string(ret));}// read frameret = av_read_frame(avformat_ctx_, avpacket_origin_);if (ret < 0) {throw std::runtime_error("av read frame " + std::to_string(ret));}// open decoderret = avcodec_open2(avcodec_decoder_ctx_, codec_decoder, nullptr);if (ret < 0) {throw std::runtime_error("avcodec open failed. codes is " + std::to_string(ret));}// decode packet to frameret = avcodec_send_packet(avcodec_decoder_ctx_, avpacket_origin_);if (ret < 0) {throw std::runtime_error("avcodec send packet failed. code is " + std::to_string(ret));}ret = avcodec_receive_frame(avcodec_decoder_ctx_, avframe_origin_);if (ret < 0) {throw std::runtime_error("avcodec_receive_frame failed. code is " + std::to_string(ret));}this->width_ = avframe_origin_->width;this->height_ = avframe_origin_->height;// init encoderavcodec_encoder_ = avcodec_find_encoder_by_name("mjpeg");if (avcodec_encoder_ == nullptr) {throw std::runtime_error("find encoder by name failed.");}avcodec_encoder_ctx_ = avcodec_alloc_context3(avcodec_encoder_);if (avcodec_encoder_ctx_ == nullptr) {throw std::runtime_error("avcodec alloc failed");}avcodec_encoder_ctx_->width = this->width_;avcodec_encoder_ctx_->height = this->height_;avcodec_encoder_ctx_->pix_fmt = AVPixelFormat(avframe_origin_->format);avcodec_encoder_ctx_->time_base = {1, 1};initializeFilterGraph();
}void ImageProcessor::process() {// send to filter graph.int ret = av_buffersrc_add_frame(this->buffer_src_ctx_, this->avframe_origin_);if (ret < 0) {throw std::runtime_error("add frame to filter failed code is " + std::to_string(ret));}ret = av_buffersink_get_frame(this->buffer_sink_ctx_, this->avframe_dst_);if (ret < 0) {throw std::runtime_error("get avframe from sink failed code is " + std::to_string(ret));}ret = avcodec_open2(avcodec_encoder_ctx_, avcodec_encoder_, nullptr);if (ret < 0) {throw std::runtime_error("open encode failed code is " + std::to_string(ret));}ret = avcodec_send_frame(avcodec_encoder_ctx_, this->avframe_dst_);if (ret < 0) {throw std::runtime_error("encoder failed,code is " + std::to_string(ret));}ret = avcodec_receive_packet(avcodec_encoder_ctx_, avpacket_dst_);if (ret < 0) {throw std::runtime_error("avcodec receive packet failed code is " + std::to_string(ret));}std::ofstream output_file(this->output_path_);output_file.write(reinterpret_cast<const char *>(this->avpacket_dst_->data), avpacket_dst_->size);
}void ImageProcessor::initializeFilterGraph() {// 创建滤镜图实例this->filter_graph_ = avfilter_graph_alloc();// 获取输入滤镜的定义const auto avfilter_buffer_src = avfilter_get_by_name("buffer");if (avfilter_buffer_src == nullptr) {throw std::runtime_error("buffer avfilter get failed.");}// 获取输出滤镜的定义const auto avfilter_buffer_sink = avfilter_get_by_name("buffersink");if (avfilter_buffer_sink == nullptr) {throw std::runtime_error("buffersink avfilter get failed.");}// 创建输入实体this->filter_inputs_ = avfilter_inout_alloc();// 创建输出实体this->filter_outputs_ = avfilter_inout_alloc();if (this->filter_inputs_ == nullptr || this->filter_outputs_ == nullptr) {throw std::runtime_error("filter inputs is null");}char args[512]{};snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",this->width_,this->height_,1,1,avframe_origin_->format,avframe_origin_->sample_aspect_ratio.num,avframe_origin_->sample_aspect_ratio.den);// 根据参数创建输入滤镜的上下文,并添加到滤镜图int ret = avfilter_graph_create_filter(&this->buffer_src_ctx_, avfilter_buffer_src, "in",args, nullptr, this->filter_graph_);if (ret < 0) {throw std::runtime_error("create filter failed .code is " + std::to_string(ret));}// 根据参数创建输出滤镜的上下文,并添加到滤镜图ret = avfilter_graph_create_filter(&this->buffer_sink_ctx_, avfilter_buffer_sink, "out", nullptr,nullptr, this->filter_graph_);if (ret < 0) {throw std::runtime_error("create filter failed code is " + std::to_string(ret));}AVPixelFormat pix_fmts[] = {AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_NONE};ret = av_opt_set_int_list(buffer_sink_ctx_, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE,AV_OPT_SEARCH_CHILDREN);// 为输入滤镜关联滤镜名,滤镜上下文this->filter_outputs_->name = av_strdup("in");this->filter_outputs_->filter_ctx = this->buffer_src_ctx_;this->filter_outputs_->pad_idx = 0;this->filter_outputs_->next = nullptr;// 为输出滤镜关联滤镜名,滤镜上下文this->filter_inputs_->name = av_strdup("out");this->filter_inputs_->filter_ctx = this->buffer_sink_ctx_;this->filter_inputs_->pad_idx = 0;this->filter_inputs_->next = nullptr;// 根据传入的字符串解析出滤镜,并加入到滤镜图ret = avfilter_graph_parse_ptr(this->filter_graph_,this->filter_desc_.c_str(),&this->filter_inputs_,&this->filter_outputs_,nullptr);if (ret < 0) {throw std::runtime_error("avfilter graph parse failed .code is " + std::to_string(ret));}ret = avfilter_graph_config(this->filter_graph_, nullptr);if (ret < 0) {throw std::runtime_error("avfilter graph config failed.");}
}

使用

#include "ImageProcessor.h"int main(int argc, const char *argv[]) {ImageProcessor processor;processor.setInput("../res/lyf1.jpeg");processor.setOutput("../res/output.jpeg");// 根据想要的效果传入滤镜描述字符串processor.setFilterString("hue=120:s=1");
//    processor.setFilterString("split [main][tmp]; [tmp] crop=iw:ih/2:0:0,vflip [flip]; [main][flip] overlay=0:H/2");processor.initialize();processor.process();return 0;
}

根据调用侧传入的滤镜描述字符,我们就可以得到跟命令行使用一样的效果。

参考

https://www.ffmpeg.org/ffmpeg.html#Simple-filtergraphs
https://www.ffmpeg.org/ffmpeg-filters.html

视频特效-使用ffmpeg滤镜相关推荐

  1. 视频特效滤镜 via Media Foundation Transform (MFT)

    视频特效滤镜 via Media Foundation Transform 视频特效定义 Media Foundation Transform IMFTransform::GetInputStream ...

  2. LiveVideoStackCon 2018技术培训 — 从FFmpeg视频编码到抖音式视频特效实现

    LiveVideoStackCon 2018技术培训,"从FFmpeg视频编码到抖音式视频特效实现".我们拒绝"纸上谈兵",坚持技术讲解与实战训练相结合.门票火 ...

  3. 音视频ffmpeg——ffmpeg 滤镜(添加水印)

    ffmpeg 滤镜 概念 所谓的滤镜其实就是在原始的视频或者图像上增加filter 按照处理数据的类型,通常多媒体的 filter 分为: ⚫ 音频 filter ⚫ 视频 filter ⚫ 字幕 f ...

  4. OpenGL实现物体动画和视频特效(视频水印、美白、滤镜等)

    1.OpenGL实现视频的水印.滤镜?OpenGL实现视频的剪裁.旋转? 2.2D/3D物体的 旋转,平移,缩放? OpenGL图片滤镜与视频滤镜? 矩阵(Matrix)是一个按照长方阵列排列的复数或 ...

  5. Premiere视频特效滤镜功能速查

    Premiere视频特效滤镜功能速查<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office&q ...

  6. ffmpeg滤镜调整颜色明艳和亮度

    1.亮度 eq 设置亮度.对比度.饱和度和近似伽马(gamma)调整 滤镜支持下面选项: contrast 设置contrast表达式,值必须是一个-2.0-2.0间的浮点数,默认为0 brightn ...

  7. 手把手实现火爆全网的视频特效 “蚂蚁呀嘿”,太魔性了

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 导读 Hi,大家好,今天是周末,今天给各位读者分享最近很火的视频特效. 将会依据现有的demo,一步步 ...

  8. FFmpeg滤镜使用指南

    from:http://blog.chinaunix.net/uid-26000296-id-4282537.html FFmpeg添加了很多滤镜,查看哪些滤镜有效可用命令: # ./ffmpeg - ...

  9. 短视频APP开发:短视频特效SDK功能火爆来袭!

    为什么短视频这么火呢?因为它符合了用户碎片化时间的需求,既娱乐了大众,又不会浪费用户太多时间. 短视频APP开发以互联网技术为核心,在原有的基础上不断进行创新,进而拥有多种强大的拍摄功能,让用户可以快 ...

最新文章

  1. Android应用程序窗口(Activity)的视图对象(View)的创建过程分析
  2. java 特殊符号正则_java利用正则表达式处理特殊字符的方法实例
  3. Spring-framework应用程序启动loadtime源码分析笔记(二)——@Transactional
  4. SVN代码回滚命令之---svn up ./ -r 版本号---OK
  5. java 布局实例,HarmonyOS Java UI之StackLayout布局示例
  6. registry:NoSuchMethodError zookeeper.server.quorum.flexible.QuorumMaj
  7. 二分查找法(Java实现)
  8. POJ3359 UVA1209 LA3173 Wordfish【Ad Hoc】
  9. mysql本地数据库地址_mysql数据库本地地址
  10. js读取文件的内置方法之 FileReader
  11. GD32F10x的窗口看门狗定时器
  12. 输入半径 求球的表面积和体积
  13. unity 打包APK 应用未安装
  14. zynq嵌入式linux显示logo,如何定制嵌入式linux 启动logo(小企鹅)
  15. Nginx 配置中nginx和alias的区别分析
  16. win10商店打不开_win10 64位系统打不开美图秀秀是啥原因呢
  17. Python数据分析入门笔记6——数据清理案例练习
  18. Visual Studio Code编写html代码实现立方体相册,表白相册
  19. SharpDevelop学习笔记(4)——SharpDevelop的核心
  20. ubuntu 查看usb设备

热门文章

  1. SEO黑帽技术 - 蜘蛛池原理
  2. ZYNQ Linux 逻辑端(PL)中断demo
  3. SSO的实现和参考工具类
  4. [量子计算]量子计算的最佳应用(The Best Applications for Quantum Computing)
  5. 企业绿色创新转型-2002-2020工业企业科技创新指标汇总
  6. sdffdsfsdfdfssfsfsfsdfsdffds/sdfsDSFsd
  7. 探究阿里云物联网开发板1-Haas 100
  8. 在 iPad和 iPhone的浏览器上查看网页源代码
  9. placement new理解
  10. hadoop2.3.0 Windows (nutch)爬虫使用