视频绿幕抠像应用:用FastDeploy部署RVM拯救视频剪辑师的发量
本项目为自动抠像,不管背景是什么,都可以抠像成绿幕视频。上传视频或图片,一键抠图抠像!将绿幕视频下载后,就可以用视频编辑软件进行透底与合成操作了。
苹果系统下可以使用系统自带的iMovie进行编辑,Windows下更有多种软件可以进行编辑,请自行搜索下载。当然还可以使用PE等专业视频编辑软件。
遗憾之处是AIStudio新版的应用部署只能用cpu,这样7秒的视频需要渲染7分钟(高端版cpu版本需要3分钟),有点慢。优点是不用运行项目,不需要编程知识,只要在应用页面上传视频并“提交”即可。如果想加快速度(比如在调试项目的时候,需要多次调用执行),就到notebook里面使用GPU来处理吧。
技术选型,使用FastDeploy部署,抠像模型选择明星产品RVM(未来会尝试PP-Matting模型)。
关于RVM模型具体部署,参考文档:https://github.com/PaddlePaddle/FastDeploy/tree/develop/examples/vision/matting/rvm/python
步骤分解,整个工作分为两部分:
- 1、 在notebook下调通
- 2、 在应用中部署调通
效果图:






一、在notebook下调通RVM视频抠像
FastDeploy是一款全场景、易用灵活、极致高效的AI推理部署工具。提供📦开箱即用的云边端部署体验, 支持超过 🔥150+ Text, Vision, Speech和跨模态模型,并实现🔚端到端的推理性能优化。包括图像分类、物体检测、图像分割、人脸检测、人脸识别、关键点检测、抠图、OCR、NLP、TTS等任务,满足开发者多场景、多硬件、多平台的产业部署需求。github地址:https://github.com/PaddlePaddle/FastDeploy 【AI快车道-FastDeploy推理部署套件系列直播课】:https://aistudio.baidu.com/aistudio/course/introduce/27800
1、安装FastDeploy环境
FastDeploy提供各平台预编译库,供开发者直接下载安装使用,不过对cpu和GPU平台,最快捷的方法是直接使用pip install进行安装。最终“应用部署”AIStudio提供的是cpu环境,不过调试的时候为了加快速度,可以使用gou版本,
!pip install fastdeploy-gpu-python -f https://www.paddlepaddle.org.cn/whl/fastdeploy.html
# !pip install fastdeploy-python -f https://www.paddlepaddle.org.cn/whl/fastdeploy.html
2、运行官方RVM抠像测试代码
RobustVideoMatting是明星抠像模型,具体效果可以参见我以前的项目拍电影没有绿幕,AI给我们造!超强的稳定视频抠像RVM ,FastDeply 部署文档:
https://github.com/PaddlePaddle/FastDeploy/blob/develop/examples/vision/matting/rvm/python/README.md
- 首先下载RobustVideoMatting模型文件、测试图片以及视频。
将文件存放在~/work目录
%cd work
# 下载RobustVideoMatting模型文件和测试图片以及视频
## 原版ONNX模型
!wget https://bj.bcebos.com/paddlehub/fastdeploy/rvm_mobilenetv3_fp32.onnx
## 为加载TRT特殊处理ONNX模型
!wget https://bj.bcebos.com/paddlehub/fastdeploy/rvm_mobilenetv3_trt.onnx
!wget https://bj.bcebos.com/paddlehub/fastdeploy/matting_input.jpg
!wget https://bj.bcebos.com/paddlehub/fastdeploy/matting_bgr.jpg
!wget https://bj.bcebos.com/paddlehub/fastdeploy/video.mp4
%cd ~/
- 下载FastDeploy源代码,主要是需要使用这个文件
https://github.com/PaddlePaddle/FastDeploy/blob/develop/examples/vision/matting/rvm/python/infer.py
本项目已经将抠像使用的infer.py文件放入~/work目录,可省去下载源代码这步。
最终在应用部署中使用的是修改后的代码,存为~/work/rvm.py文件。
# !git clone https://github.com/PaddlePaddle/FastDeploy
# !git clone https://gitee.com/PaddlePaddle/FastDeploy
3、RobustVideoMatting Python部署示例
测试时发现自己拍的视频无法正常抠像,表现为生成的视频文件只有几百个字节。经测试和读源码,发现视频抠像的输入限定大小为1080 * 1920的竖构图格式,请使用的时候注意。大部分手机录视频都支持这个格式,选好即可。大家快拿起手机,发挥自己的创意吧!
推理成功后,会在work目录生成alpha.mp4和composition.mp4两个文件,一个是绿幕视频,一个是叠加背景图的视频。
%cd ~/work/
# CPU推理
## 图片
!python infer.py --model rvm_mobilenetv3_fp32.onnx --image matting_input.jpg --bg matting_bgr.jpg --device cpu
## 视频 因cpu下速度较慢,不进行测试
# !python infer.py --model rvm_mobilenetv3_fp32.onnx --video video.mp4 --bg matting_bgr.jpg --device cpu
# GPU推理
## 图片
!python infer.py --model rvm_mobilenetv3_fp32.onnx --image matting_input.jpg --bg matting_bgr.jpg --device gpu
## 视频
!python infer.py --model rvm_mobilenetv3_fp32.onnx --video video.mp4 --bg matting_bgr.jpg --device gpu
# TRT推理
## 图片
!python infer.py --model rvm_mobilenetv3_trt.onnx --image matting_input.jpg --bg matting_bgr.jpg --device gpu --use_trt True
## 视频
!python infer.py --model rvm_mobilenetv3_trt.onnx --video video.mp4 --bg matting_bgr.jpg --device gpu --use_trt True
%cd ~/
# gpu 29s
# !cd ~/work && python infer.py --model rvm_mobilenetv3_fp32.onnx --video video.mp4 --bg matting_bgr.jpg --device gpu
# 测试手机视频抠像
# !cd ~/work && python infer.py --model rvm_mobilenetv3_fp32.onnx --video test.mp4 --bg matting_bgr.jpg --device gpu
# cpu174秒
# !cd ~/work && python infer.py --model rvm_mobilenetv3_fp32.onnx --video video.mp4 --bg matting_bgr.jpg --device cpu
4、将整个应用在notebook下跑通
这里其实走弯路了,一开始只完成了前三步,就直接在“应用”中进行调试了,而没有在notebook下全面调试代码。导致的问题就是:
- 没有拆解问题,导致“应用中”问题层出不穷
- 在“应用中”调试耗时耗力
- 一些代码问题和“应用多线程”问题交织在一起,干扰了问题的定位
在走过一些弯路后,才明白,应该首先在notebook下将所有函数和流程全部调通,也就是除streamlit图像、视频展示和上传文件等必须在“应用”中调试之外,其它所有代码都可以在notebook阶段调通并且应该在notebook阶段调通,否则就会很浪费时间。
具体就是如下几部分代码调通:
- 将官方RVM主代码infer.py文件修改为可调用函数。
def rvmmatting(video=None, image=None, bg=None, device="cpu", use_openvino=False ):
初期主要是写rvmmatting功能函数,后期主要是解决rvmmatting函数在多线程下存储文件的问题。
- 多线程存储文件问题,初期采用固定文件名会有问题(这个会在“应用”调试时暴露比较彻底),后期采用tmpfile函数辅助
video_alpha = tempfile.NamedTemporaryFile(delete=True)
video_composition = tempfile.NamedTemporaryFile(delete=True)
video_alpha_name = video_alpha.name + ".mp4"
video_composition_name = video_composition.name + ".mp4"
cap = cv2.VideoCapture(video)
fps =int(cap.get(cv2.CAP_PROP_FPS))
# Define the codec and create VideoWriter object
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
composition = cv2.VideoWriter(video_composition_name, fourcc, fps,
(1080, 1920))
alpha = cv2.VideoWriter(video_alpha_name, fourcc, fps, (1080, 1920))
- 使用ffmpeg进行编码转变,以解决Chrome不支持非h264编码的问题。
video_alpha = tempfile.NamedTemporaryFile(delete=False)
video_alpha_name = video_alpha.name + ".mp4"
os.system(f'ffmpeg -y -i {out["video_alpha_name"]} -vcodec libx264 -strict -2 {video_alpha_name}')
首先考虑的是直接用opencv进行格式转变,但是默认安装的opencv不支持h264编码,最终使用ffmpeg进行编码转变。
二、应用部署
BML CodeLab提供用户创建应用、调试应用和部署应用等功能。相关功能以Streamlit作为底层技术,为用户提供快速搭建交互式图形化界面的能力,只需要几行代码即可搭建一个炫酷的图形化界面。
部署核心可分为三步:
- BML CodeLab启动页新增应用创建工具选项。点击该选项,即在文件列表自动创建一个带有皇冠标识的应用文件,文件默认命名为untitled.streamlit.py
- 调试应用,双击应用文件,即可开始编写并调试应用代码。
- 部署应用
实际操作中,在部署前还需要先将代码块在notebook中调通,以提高后面的部署效率。
1、首先修改infer.py文件,以便在应用中使用
如前面所述,这步应该在notebook里面先调通。
主要是将infer.py文件的主体部分改写到rvm.py文件rvmmatting函数中,并加入if __name__ == "__main__" :
语句,这样可以通过直接执行python rvm.py测试程序体是否正常,并方便下一步在应用中调用。
测试rvm.py文件执行是否正常
使用python rvm.py进行测试。测试通过。
后来进行了速度测试,比较观看openvino是否能加速。
运行速度cpu 181秒 openvino 398秒
GPU 27秒
发现openvino不仅没快,反而更慢了,个人认为是自己配置不对,或者AIStudio没有支持openvino导致,或者AIStudio的虚拟化影响了openvino的性能。
经过这次测试,决定在应用中,老老实实用cpu推理就行了。
!cd ~/work && python rvm.py
测试调用rvmmatting函数
通过设置video和image参数,可以分别测试抠图和抠像部分。两个参数均设置,则抠图和抠像一起执行。
测试成功
%cd ~/work
from rvm import rvmmatting
# import cv2
input_image= "matting_input.jpg"
argsbg = "matting_bgr.jpg"
argsvideo = "video.mp4"
rvmmatting( video=argsvideo, image=input_image, bg=argsbg )
# rvmmatting( video=argsvideo, bg=argsbg )
# rvmmatting(image=input_image, bg=argsbg, device="gpu")
%cd ~/
在“应用”多线程的情况下,原来官方代码固定存盘文件名会导致文件冲突的问题,所以要使用tmpfile库来解决该问题。
回过头来看,这步如果提前在notebook下调通,将能节省大量的时间!
2、用“应用创建工具”创建应用
参考文档:https://ai.baidu.com/ai-doc/AISTUDIO/Gktuwqf1x#应用
主要思路就是按照执行顺序,写好:视频上传、视频处理和输出展示3个部分。其中视频处理主要就是前面将的rvm.py文件,而视频上传和输出展示,本身是挺简单的,只需要几句话就行,但是在多线程的情况下,视频需要用tmpfile来实现非固定式名字,以免同文件名导致冲突。在实践中,发现“应用”的文件空间是共享的,所以图片的存储也需要不同的文件名,否则也会发生冲突,导致出现生成图片并不是“自己上传图片”的怪事。
前面讲过,Chrome只支持mp4里面的h264编码,怎么进行编码转换,这里花了较多的时间调试和排查。
具体可以查看源文件:~/work/app.streamlit.py
BML CodeLab启动页新增应用创建工具选项。
点击该选项,即在文件列表自动创建一个带有皇冠标识的应用文件,文件默认命名为untitled.streamlit.py,在本项目中修改名字为app.streamlit.py,当然这都不重要。
调试应用
双击应用文件,即可开始编写并调试应用代码。
如前面所述,除了图片视频上传、显示等,需要在“应用”中调试,其它代码都可以在notebook下调试。
需要注意的的几点:
- 本地运行和“浏览器打开”的时候,需要提前安装好相关库,对本项目来说,需要提前在notebook或控制台安装好FastDeploy库。
- 本地运行和“浏览器打开”的时候,不管app.streamlit.py文件在哪个目录,运行环境的根目录都是在项目根目录~/
也就是如果大家fork了这个项目,在本地运行“应用”的时候,需要把work目录里的文件cp到~/根目录,否则会报错文件找不到。
部署应用
部署的时候,需要在requirements.txt文件中写入需要安装的库文件,比如fastdeploy-python -f https://www.paddlepaddle.org.cn/whl/fastdeploy.html
部署之后,运行环境的根目录就是部署时设定的那个目录。
因为部署采用了多线程,所以普通的文件存盘可能会发生问题,解决的方法就是用tmpfile,也就是随机文件名并放入/tmp目录中。
好了,大家也来应用里面试用下吧,点击上传,一键抠图抠像,妈妈再也不用担心我的头发啦!
调试
Matting的文档比较简单
想偷懒,直接用FastDeploy封装的一句话搞定,但是光看文档,不知道该怎么办,看API文档,也太简略了,连例子都没有。最终解决的方法,看infer.py源文件,并进行适当修改。
视频抠图的时候输出很多无用信息
WARNING:root:DEPRECATED: fastdeploy.vision.swap_background_matting is deprecated, please use fastdeploy.vision.swap_background function instead.
根据提示,把fastdeploy.vision.swap_background_matting
替换成fastdeploy.vision.swap_background
即可。
自己上传的视频无法转换成功
发现自己的视频格式是:编解码器:AAC,H.264
成功的那个视频格式是:MPEG-4 Video
再仔细看,发现是自己的背景图720.jpeg,代码里一直写的720.jpg,怪不得一直报错。
修改过文件名之后,经测试,不同大小的背景图都ok。但h.264格式视频还是无法抠像。
最终发现是需要固定1280 * 1920大小的视频才能转换成功。而背景图的大小不受限。至于编码格式,H.264 和MPEG-4 Video都是可以的。
在应用中同时写两个上传控件报错:
原来如果写两个上传控件,需要设置一个key参数,如:
per_image = st.file_uploader("上传图片", type=['png', 'jpg', 'jpeg'], label_visibility='hidden', key="x1")
per_image_bgr = st.file_uploader("上传图片", type=['png', 'jpg', 'jpeg'], label_visibility='hidden', key="x2")
在引用浏览器测试的时候,碰到图像斜向扭曲
好像以前也碰到过,但是忘记是怎么回事了? 好像横纵比例不对导致的?点击图片在新页面打开,发现是好的。偶尔会碰到,不去管它了。
项目调试报错:ModuleNotFoundError: No module named ‘fastdeploy’
明明已经在requirements.txt文件中写入预安装Fastdeploy库了,但是应用启动后就是报没有这个库的错。
我感觉可能是我文件名字写错了,或者有拼写错误。
发现问题了,原来是requirements.txt文件名写错了,真是醉了,
应该是:requirements.txt,写成了requirements.py
问:如何切换模型推理后端
答:FastDeploy中各视觉模型可支持多种后端,包括
- OpenVINO (支持Paddle/ONNX两种格式模型, 仅支持CPU上推理)
- ONNX Runtime (支持Paddle/ONNX两种格式模型, 支持CPU/GPU)
- TensorRT (支持Paddle/ONNX两种格式模型,仅支持GPU上推理)
- Paddle Inference(支持Paddle格式模型, 支持CPU/GPU)
import fastdeploy as fd
option = fd.RuntimeOption()
# 切换使用CPU/GPU
option.use_cpu()
option.use_gpu()
# 切换不同后端
option.use_paddle_backend() # Paddle Inference
option.use_trt_backend() # TensorRT
option.use_openvino_backend() # OpenVINO
option.use_ort_backend() # ONNX Runtime
应用在一个浏览器Safari正常另一个Chrome浏览器不正常
刚开始误以为是非作者账户下应用有问题,绕了很大的弯路,才确定原因:Chrome浏览器不显示MPEG-4 Video编码格式视频。
Chrome浏览器只支持H264格式,本想在python代码里直接使用cv代码生成该格式,后来发现需要重新编译cv或ffmpeg,这个在AIStudio,尤其是在“应用里面”不现实。解决的方法为:使用ffmpeg转换格式,具体见后面。
具体来说,我测试了如下格式:
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
fourcc = cv2.VideoWriter_fourcc(*'H264')
fourcc = 0x00000021
# fourcc = cv2.VideoWriter_fourcc(*"MP42")
# fourcc = cv2.VideoWriter_fourcc(*'H265')
# fourcc = cv2.VideoWriter_fourcc(*'DAVC')
# fourcc = cv2.VideoWriter_fourcc(*'I420')
# fourcc = cv2.VideoWriter_fourcc('H', 'E', 'V', 'C')
获得的新知识就是;
- fourcc就是4字符编码配置,可以分开写,也可以用* 号加字符串;要全部大写或小写
- fourcc编码配置还需要ffmpeg支持,若不支持会报错
- 谷歌Chrome只支持h264(我的理解应该是mpg4里只支持这一种编码)
编码h264报错为:
OpenCV: FFMPEG: tag 0x34363248/'H264' is not supported with codec id 27 and format 'mp4 / MP4 (MPEG-4 Part 14)'
OpenCV: FFMPEG: fallback to use tag 0x31637661/'avc1'
[ERROR:0@3614.785] global /io/opencv/modules/videoio/src/cap_ffmpeg_impl.hpp (2927) open Could not find encoder for codec_id=27, error: Encoder not found
[ERROR:0@3614.785] global /io/opencv/modules/videoio/src/cap_ffmpeg_impl.hpp (3002) open VIDEOIO/FFMPEG: Failed to initialize VideoWriter
Chrome浏览器不支持h264以外编码的解决方法
将视频其它编码转为h264编码。
解决的方法,可以只用这句话:ffmpeg -i out.mp4 -vcodec libx264 -acodec aac output.mp4
在应用中,在python代码中,可以使用os.system()执行上面转换语句。
报错could not find a writer for the specified extension in function ‘imwrite_’
解决方法:保存图片记得加上拓展名字bmg/img/jpg均可
应用中调用rvmmatting函数,发现生成的视频文件不见了
主要是的问题是修改rvm.py文件后,在应用中并没有及时体现,也就是好像代码被缓存了似的。
因为“代码被缓存”,导致调试的时候走了很多弯路,最终也没有确定是否是delete=True
参数导致的文件没有存下来,不过后来使用了加后缀名的方法,使文件能够长久保存。当然后面还要解决过期后删除的问题。
痛定思痛,还是要优化“应用”的开发和调试最优路径
应用开发中,需要经历如下几个步骤:notebook调通,“应用运行”调通,“在浏览器打开”调通,以及“应用部署”调通,“应用部署”后非作者账户调通等五个步骤,且应用部署和之后的调试,都不支持gpu,在处理需要较长时间的时候,尤其是处理视频的时候。
最优开发路径
- notebook调通,所有的函数全调通。除了streamlit图像、视频展示和上传文件等,其它操作可以全部在notebook阶段调通
- 应用运行调通。要注意运行的目录,不管xx.streamlit.py文件在哪个目录执行,根目录都是~/目录。相关库需要在notebook或终端pip安装。
- “在浏览器打开”调通,相关库需要写入requirements.txt文件中,根目录在~/目录,可以使用gpu。
- “应用部署”调通,相关库需要写入requirements.txt文件中,根目录为部署时“部署包目录”,临时文件建议使用python tempfile方法创建,这个在第一步notebook环境下测试较好,否则直接往当前目录写,可能会(文件乱套会让人怀疑人生)存在问题。只能使用cpu。
- “应用部署”后非作者账户调试,本步骤可以忽略,但确实可以发现不同浏览器的问题,比如Chrome不支持h264编码之外的mp4视频。
排除掉不确定因素之后,应用还是挺容易调通的。
不确定因素有:执行根目录问题;依赖库requirements.txt问题,临时文件读写问题等。
应用中测试时发现ffmpeg转换的文件不在
手工测试,在命令行测试的时候,发现报错
[aac @ 0x1ad4820] The encoder 'aac' is experimental but experimental codecs are not enabled, add '-strict -2' if you want to use it.
于是加上该选项。另外如果有同名文件存在的话,它会问是否覆盖,估计这也是前面测试一直失败的原因。
同时还找到另外一个问题,就是上传的测试视频是1920 * 1080的横构图(一直误以为它是1080 * 1920 ),这样抠像就会失败,不会输出视频,进而导致ffmpeg转换的时候报错。虽然是一个小疏忽,但是调试中简直怀疑人生!
应用测试中发现切换图片后报错
一开始代码中并没有针对切换图片就行相应修改,导致上传图片后参与抠图的图片也不会变。
修改代码后,发现上传图片报错:
AttributeError: 'NoneType' object has no attribute 'read'
Traceback:
File "/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/streamlit/runtime/scriptrunner/script_runner.py", line 562, in _run_script
exec(code, module.__dict__)
File "/home/aistudio/work/app.streamlit.py", line 47, in <module>
tfileper_image_bgr.write(per_image.read())
将tfileper_image_bgr = tempfile.NamedTemporaryFile(delete=True)
修改为tfileper_image_bgr = tempfile.NamedTemporaryFile(delete=False)
上面报错问题解决,出现新的报错
报错AttributeError: ‘NoneType’ object has no attribute ‘read’
AttributeError: 'NoneType' object has no attribute 'read'
Traceback:
File "/usr/local/lib/python3.7/dist-packages/streamlit/runtime/scriptrunner/script_runner.py", line 564, in _run_script
exec(code, module.__dict__)
File "/home/serving/a1671975443390/app.streamlit.py", line 46, in <module>
tfileper_image_bgr.write(per_image.read())
查看创建文件语句,感觉这里还是要加后缀啊!
tfileper_image = tempfile.NamedTemporaryFile(delete=False) # 创建可删除文件
tfileper_image.write(per_image.read())
uploadimage = tfileper_image.name
全部加上后缀,问题解决:
if per_image:
st.image(per_image)
tfileper_image = tempfile.NamedTemporaryFile(delete=True)
uploadimage = tfileper_image.name+per_image.name
cur_img = Image.open(per_image)
cur_img.save(uploadimage)
想加上定时删除功能,失败
想像普通程序那样使用time.time()来定时删除临时图片和视频文件,但是没有成功。
我认为原因是那个时间基数在streamlit里也在不停的在走啊。
即类似这样的语句在应用中失效:
# 超过1小时后,删除视频文件
deletetime = time.time()
if time.time()-deletetime > 30:
for i in out:
os.system(f"rm {out[i]}")
版本
0.11 2022.12.25日,终于调试通过,可以在Chrome浏览器下正常使用了!
结束语
让我们荡起双桨,在AI的海洋乘风破浪!
飞桨官网:https://www.paddlepaddle.org.cn
因为水平有限,难免有不足之处,还请大家多多帮助。
作者:段春华, 网名skywalk 或 天马行空,济宁市极快软件科技有限公司的AI架构师,百度飞桨PPDE。
我在AI Studio上获得至尊等级,点亮10个徽章,来互关呀~ https://aistudio.baidu.com/aistudio/personalcenter/thirdview/141218
此文章为搬运
原项目链接