通过ffmpeg实现视频背景色替换
最近遇到一个需求,希望可以将素材视频的绿幕背景替换为指定的颜色,然后通过裁剪,拼接等处理制作一个新的视频。所以替换背景色成为了重要的一环,看能否通过ffmpeg来实现。通过一番搜索尝试,发现方案可行。下面我整理一下实现方法。
功能实现
本文的测试视频我在B站上随便找了一个,菜虚坤拍篮球绿幕视频素材。截图如下:

首先需要将视频中的绿色改为透明,类似把人物抠出来,这样才能便于修改背景颜色。因为mov格式视频支持透明通道,所以第一步需要在去除背景色的同时将视频保存为mov格式。所以需要使用到chromakey滤镜。
ffmpeg -i input.mp4 -vf "chromakey=#3fff08:0.1:0.04" -c:v qtrle -c:a copy output.mov
#3fff08是绿幕的颜色,也就是需要替换为透明的颜色。0.1是相似度(similarity)参数。这个参数决定了颜色匹配的严格程度。值越小,匹配的颜色范围越窄,也就是说,只有非常接近指定颜色的像素才会被视为透明。值越大,匹配的颜色范围越宽,也就是说,即使颜色和指定颜色有一些差距,也会被视为透明。0.04是混合度(blend)参数。这个参数决定了边缘像素的处理方式。值越小,边缘像素的处理越严格,可能会导致边缘部分出现锐利的边缘。值越大,边缘像素的处理越宽松,可能会导致边缘部分出现柔和的过渡。
然后修改颜色:
ffmpeg -i output.mov -vf "color=color=#2B2D30:size=1920x1080 [bg]; [bg][0:v] overlay=shortest=1" output2.mp4
#2B2D30是需要修改的视频背景色。- 1920x1080是视频的分辨率,也就是给视频一个这么大的背景。
我们以上面0.1和0.04的参数处理后,效果如下:

因为指定的相似度精度高,所以人物边缘绿色未去除。因为边缘色值或许不是#3fff08。所以我尝试将0.1改为0.18,效果如下:

效果好了许多,按照这个思路,我尝试到0.3,感觉效果就已经比较好了。

需要注意的是,这两个参数不是越大越好,过高的值会匹配更多的颜色,会导致整个视频都透明了。比如我试了0.3和0.1的组合,效果如下:

发现画面整个变暗了,因为背景是灰色,混合度过高,所以就像是蒙了一层灰色。所以这两个参数的具体值取决于视频和绿幕的特定情况。需要根据实际效果进行调整,以获得最佳的绿幕去除效果。
工具制作
如果只是功能实现,那么上面的两条命令基本已经够了。但是要将这一功能做成工具,就需要更近一步。
首先命令中的参数都需要动态获取。
- 获取原视频的背景色。
- 获取原视频的分辨率,帧率。
- 两个阈值参数可以输入。
为什么需要获取帧率,因为转换后视频默认转为了25帧,如果你不想影响原视频帧率,就需要指定帧率,例如指定30帧:
ffmpeg -i output.mov -vf "color=color=#2B2D30:size=1920x1080 [bg]; [bg][0:v] overlay=shortest=1" -r 30 output2.mp4
另外,码率也是类似。
获取背景色
ffmpeg -ss 0.1 -i input.mp4 -vframes 1 output.jpg
首先通过命令获取一张视频的截图,这里取0.1s的位置。
然后获取图片中颜色最多的色值。我这里是用flutter实现的,代码如下:
/// 获取图片数据Future<ui.Image?> loadImage(File file) async {final Completer<ui.Image> completer = Completer();ImageProvider imageProvider = FileImage(file);ImageStreamListener listener = ImageStreamListener((info, _) async {completer.complete(info.image);});final ImageStream stream = imageProvider.resolve(const ImageConfiguration());stream.addListener(listener);try {await completer.future;} catch (e) {debugPrint("Error loading image: $e");} finally {stream.removeListener(listener);}return completer.isCompleted ? completer.future : null;}/// 获取图片中颜色最多的色值Future<Color?> getMostCommonColor(ui.Image? image) async {if (image == null) {return null;}Uint8List bytes = await image.toByteData().then((data) => data!.buffer.asUint8List());final colorCount = <Color, int>{};for (int i = 0; i < bytes.lengthInBytes; i += 4) {final red = bytes[i];final green = bytes[i + 1];final blue = bytes[i + 2];final alpha = bytes[i + 3];final key = Color.fromARGB(alpha, red, green, blue);if (colorCount.containsKey(key)) {colorCount[key] = colorCount[key]! + 1;} else {colorCount[key] = 1;}}Color? mostCommonColor;int maxCount = 0;colorCount.forEach((color, count) {if (count > maxCount) {mostCommonColor = color;maxCount = count;}});return mostCommonColor;}
这是一种思路,当然也可以获取指定位置的颜色,毕竟背景色都是一致的,如果不一致,那么替换的效果也会打折扣,所以这种方法相对比较简单一些。
Future<Color?> getPixelColor(ui.Image? image, int x, int y) async {if (image == null) {return null;}final byteData = await image.toByteData();if (byteData == null) {return null;}final width = image.width;final pixelOffset = (y * width + x) * 4;final r = byteData.getUint8(pixelOffset);final g = byteData.getUint8(pixelOffset + 1);final b = byteData.getUint8(pixelOffset + 2);final a = byteData.getUint8(pixelOffset + 3);return Color.fromARGB(a, r, g, b);}
这里我获取到的色值是16进制的,例如Color(0xff14ff09),我需要转成字符#14ff09。
Color color = Color(0xff14ff09);
String colorStr = '#${color.value.toRadixString(16).substring(2)}';
获取视频的分辨率,帧率
获取命令:
ffprobe -v error -select_streams v:0 -show_entries stream=width,height,r_frame_rate -of csv=p=0 input.mp4
这里使用csv=p=0让结果用逗号拼接返回,例如:1920,1080,30/1
然后我们用代码处理这个字符串,获取最终想要的 1920x1080和30就行了,这里就贴代码了。
阈值参数
最后页面上加两个输入框,输入这两个阈值参数就万事具备了。
最后结合上面的两条核心命令,将这些参数传入进去就可以了。看似一句话的需求,实际上细节还是比较多的。
参考
- ffmpeg绿幕抠图原理解析
相关文章:
通过ffmpeg实现视频背景色替换
最近遇到一个需求,希望可以将素材视频的绿幕背景替换为指定的颜色,然后通过裁剪,拼接等处理制作一个新的视频。所以替换背景色成为了重要的一环,看能否通过ffmpeg来实现。通过一番搜索尝试,发现方案可行。下面我整理一…...
后轮位置反馈控制与算法仿真实现
文章目录 1. 后轮反馈控制2. 算法原理3. 算法和仿真实现 1. 后轮反馈控制 后轮反馈控制(Rear wheel feedback)算法是利用后轮中心的跟踪偏差来进行转向控制量计算的方法,属于Frenet坐标系的一个应用。通过选择合适的李雅普诺夫函数设计控制率…...
实战 vue3 使用百度编辑器ueditor
前言 在开发项目由于需求vue自带对编辑器不能满足使用,所以改为百度编辑器,但是在网上搜索发现都讲得非常乱,所以写一篇使用流程的文章 提示:以下是本篇文章正文内容,下面案例可供参考 一、下载ueditor编辑器 一个“…...
N种方法解决1(CTF)
这里遇到的问题:一开始采用的base64解码平台有问题;默认解密出的格式为GBK格式;直接复制粘贴发现无法还原图片;又尝试了其他编码的;发现只有hex格式可以保证图片正常还原; 图片是以二进制存储的࿱…...
Istio实战:Istio Kiali部署与验证
目录 前言一、Istio安装小插曲 注意事项 二、Kiali安装三、Istio测试参考资料 前言 前几天我就开始捣腾Istio。前几天在执行istioctl install --set profiledemo -y 的时候老是在第二步就报错了,开始我用的istio版本是1.6.8。 后面查看k8s与istio的版本对应关系后发…...
ASPxGridView中使用PopupEditForm表单字段联动填充
c#中devexpress的控件ASPxGridView中使用PopupEditForm表单字段联动填充 //选择项目名称,自动填充项目编号 <Columns><dx:GridViewDataTextColumn FieldName"id" ReadOnly"True" VisibleIndex"0" Visible"False"…...
基于Pytorch的猫狗图片分类【深度学习CNN】
猫狗分类来源于Kaggle上的一个入门竞赛——Dogs vs Cats。为了加深对CNN的理解,基于Pytorch复现了LeNet,AlexNet,ResNet等经典CNN模型,源代码放在GitHub上,地址传送点击此处。项目大纲如下: 文章目录 一、问题描述二、数据集处理…...
flutter sliver 多种滚动组合开发指南
flutter sliver 多种滚动组合开发指南 视频 https://youtu.be/4mho1kZ_YQU https://www.bilibili.com/video/BV1WW4y1d7ZC/ 前言 有不少同学工作中遇到需要把几个不同滚动行为组件(顶部 appBar、内容固定块、tabBar 切换、tabBarView视图、自适应高度、横向滚动&a…...
kafka生产者2
1.数据可靠 • 0:生产者发送过来的数据,不需要等数据落盘应答。 风险:leader挂了之后,follower还没有收到消息。。。。 • 1:生产者发送过来的数据,Leader收到数据后应答。 风险:leader应答…...
【LNMP】云导航项目部署及环境搭建(复杂)
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、项目介绍1.1项目环境架构LNMP1.2项目代码说明 二、项目环境搭建2.1 Nginx安装2.2 php安装2.3 nginx配置和php配置2.3.1 修改nginx文件2.3.2 修改vim /etc/p…...
nginx之状态页 日志分割 自定义图表 证书
5.1 网页的状态页 基于nginx 模块 ngx_http_stub_status_module 实现,在编译安装nginx的时候需要添加编译参数 --with-http_stub_status_module,否则配置完成之后监测会是提示语法错误注意: 状态页显示的是整个服务器的状态,而非虚拟主机的状态 server{…...
数字人的未来:数字人对话系统 Linly-Talker + 克隆语音 GPT-SoVITS
🚀数字人的未来:数字人对话系统 Linly-Talker 克隆语音 GPT-SoVITS https://github.com/Kedreamix/Linly-Talker 2023.12 更新 📆 用户可以上传任意图片进行对话 2024.01 更新 📆 令人兴奋的消息!我现在已经将强…...
SpringMVC 学习(五)之域对象
目录 1 域对象介绍 2 向 request 域对象共享数据 2.1 通过 ServletAPI (HttpServletRequest) 向 request 域对象共享数据 2.2 通过 ModelAndView 向 request 域对象共享数据 2.3 通过 Model 向 request 域对象共享数据 2.4 通过 map 向 request 域对象共享数据 2.5 通过…...
✅技术社区项目—JWT身份验证
通用的JWT鉴权方案 JWT鉴权流程 基本流程分三步: ● 用户登录成功之后,后端将生成的jwt返回给前端,然后前端将其保存在本地缓存; ● 之后前端与后端的交互时,都将iwt放在请求头中,比如可以将其放在Http的身份认证的请求头 Author…...
5.2 Ajax 数据爬取实战
目录 1. 实战内容 2、Ajax 分析 3、爬取内容 4、存入MySQL 数据库 4.1 创建相关表 4.2 数据插入表中 5、总代码与结果 1. 实战内容 爬取Scrape | Movie的所有电影详情页的电影名、类别、时长、上映地及时间、简介、评分,并将这些内容存入MySQL数据库中。 2、…...
276.【华为OD机试真题】矩阵匹配(二分法—JavaPythonC++JS实现)
🚀点击这里可直接跳转到本专栏,可查阅顶置最新的华为OD机试宝典~ 本专栏所有题目均包含优质解题思路,高质量解题代码(Java&Python&C++&JS分别实现),详细代码讲解,助你深入学习,深度掌握! 文章目录 一. 题目-矩阵匹配二.解题思路三.题解代码Python题解代码…...
java——多线程基础
目录 线程的概述多线程的创建方式一:继承Thread类方式二:实现Runnable接口方式三:利用Callable接口、FutureTask类来实现。Thread常用的方法 线程安全问题线程安全问题概述线程安全问题案例取钱案例描述模拟代码如下:执行结果 线程…...
Python服务器监测测试策略与工具:确保应用的高可用性!
在构建高可用性的应用程序时,服务器监测测试是至关重要的一环。Python作为一种强大的编程语言,提供了丰富的工具和库来帮助我们进行服务器监测测试。本文将介绍一些关键的策略和工具,帮助你确保应用的高可用性。 1. 监测策略的制定ÿ…...
Spring Security源码学习
Spring Security本质是一个过滤器链 过滤器链本质是责任链设计模型 1. HttpSecurity 【第五篇】深入理解HttpSecurity的设计-腾讯云开发者社区-腾讯云 在以前spring security也是采用xml配置的方式,在<http>标签中配置http请求相关的配置,如用户…...
大数据面试总结三
1、hdfs作为分布式存储系统,底层的实现的方式(可能不正确) 1、底层是一个分布式存储的,底层会将数据进行切分多个block块(128M),并存储在不同的节点上面,这种分布式方式有助于提高数…...
微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
鸿蒙(HarmonyOS5)实现跳一跳小游戏
下面我将介绍如何使用鸿蒙的ArkUI框架,实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...
嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)
目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 编辑编辑 UDP的特征 socke函数 bind函数 recvfrom函数(接收函数) sendto函数(发送函数) 五、网络编程之 UDP 用…...
