简介:本资源面向希望在人像动画生成方向落地的开发者,提供使用onnxruntime部署LivePortrait的完整程序,同时给出C++与Python两套实现路径,适合具备一定深度学习推理基础、想在本地或工程环境中集成人像驱动能力的读者参考。压缩包共14个文件,约459KB,包含4个cpp与3个h源文件构成C++推理主流程,2个py脚本负责Python侧调用与裁剪处理,另有txt、md说明文档及mp4、jpg、png等示例素材,便于对照理解输入输出。目前已有204人学习下载。资源涵盖人脸检测、裁剪对齐、动画生成等模块的代码组织,读者可据此掌握onnxruntime加载模型、前后处理与跨语言调用的排错思路,并借助示例素材快速验证效果,适合作为人像动画工程化部署的起步模板。
1. 拿到 liveportrait-onnxrun-main 之后:这套 C++/Python 双栈人像动画程序到底能跑出什么
上周有个做短视频工具的朋友甩给我一个压缩包,名字很长——使用onnxruntime部署LivePortrait人像动画生成(C++和Python)程序.zip。他问得很直接:这东西能不能脱离 PyTorch 那套重依赖,在普通 Windows 机器上把一张静态人像照片驱动成一段说话视频?我解压跑了一遍,答案是能,而且它把推理后端换成了 onnxruntime,Python 和 C++ 两条路都给了。
LivePortrait 本身是快手开源的肖像动画框架,核心思路是从源图像里提取外观特征和关键点运动信息,再结合驱动视频的表情、姿态、视线变化,逐帧渲染出目标人像的动画。原版依赖 PyTorch 和一堆 CUDA 算子,部署门槛不低。这个包做的事,是把模型转成 ONNX 格式后用 onnxruntime 加载推理,同时提供了 Python 脚本和一套完整的 C++ 工程(含 CMakeLists.txt、liveportrait.h/cpp、faceanalysis.h/cpp、utils_crop.h/cpp、main.cpp),等于把「快速验证」和「集成进现有 C++ 项目」两条路都铺好了。
适合谁用?如果你手头有 C++ 的桌面端或服务端工程,想把人像动画能力嵌进去,又不想拖一个 PyTorch 运行时,这套东西值得拆开看。如果你只是想先跑通效果,Python 那条线几分钟就能出第一段视频。下面按我实际操作的顺序,把两条路都走一遍。
2. 环境准备与模型文件落位:onnxruntime 动态库、Python 依赖和目录结构
2.1 先看清包里的目录骨架
解压后根目录是liveportrait-onnxrun-main,里面大致分三块:Python 侧有main.py、utils_crop.py,以及0.jpg、mask_template.png、d0.mp4这几个示例素材;C++ 侧在cpp目录下,包含CMakeLists.txt、liveportrait.h、liveportrait.cpp、faceanalysis.h、faceanalysis.cpp、utils_crop.h、utils_crop.cpp、main.cpp;根目录还有一份README.md。
模型文件(.onnx)通常需要单独下载后放到指定目录,包里不会塞几百兆的权重。常见做法是在根目录建一个models或onnx文件夹,把外观提取、运动提取、生成器、关键点检测这几类 ONNX 文件按 README 里的命名放进去。这一步别偷懒,路径和文件名对不上,后面报错会很难定位。
2.2 Python 侧依赖安装
Python 这条线依赖比较轻,核心就是 onnxruntime、opencv、numpy、pillow 这几个。我一般用虚拟环境隔离,避免和系统里的 PyTorch 打架:
# 建虚拟环境,Python 3.8 及以上都行 python -m venv venv_lp # Windows 激活 venv_lp\Scripts\activate # Linux/macOS 激活 source venv_lp/bin/activate # 装核心依赖,CPU 版 onnxruntime pip install onnxruntime opencv-python numpy pillow # 如果有 NVIDIA 显卡且想用 GPU 推理,换成 GPU 版 # pip install onnxruntime-gpu这里有个参数选择:onnxruntime和onnxruntime-gpu只能装一个,装混了会出现 provider 找不到的情况。CPU 版默认用CPUExecutionProvider,GPU 版需要 CUDA 和 cuDNN 版本匹配,具体版本对照去 onnxruntime 官方文档查,别凭感觉装。
2.3 C++ 侧依赖与 onnxruntime 动态库
C++ 这条线要准备三样东西:onnxruntime 的 C++ 库(头文件 + 动态库)、OpenCV、CMake。onnxruntime 官方提供预编译包,下载对应平台的压缩包后,把include和lib目录路径记下来,等会儿在 CMakeLists.txt 里指过去。
Windows 上还需要注意运行库:Microsoft Visual C++ 2015-2022 Redistributable (x64)建议先装上,否则 onnxruntime 的动态库加载可能直接失败,报找不到vcruntime140_1.dll之类的错。这个坑我踩过不止一次,装完重启一下最稳。
提示:onnxruntime 动态库的版本要和头文件版本一致,混用不同版本的 dll 和 lib 会出现符号找不到的链接错误。
3. Python 跑通第一段动画:main.py 的参数、裁剪脚本与推理链路
3.1 先跑 utils_crop.py 做素材预处理
LivePortrait 对输入图像有裁剪和对齐要求,直接丢一张原图进去,人脸区域比例不对,出来的动画会歪或者抖动。包里给了utils_crop.py,作用是把源图像裁剪到合适的人脸区域并生成对齐后的图。
# 对示例图 0.jpg 做裁剪,输出对齐后的人脸图 python utils_crop.py 0.jpg这个脚本内部一般会调用人脸检测,拿到关键点后做仿射变换,把脸摆正并缩放到模型期望的输入尺寸。执行完会生成一张裁剪后的图,记下输出文件名,下一步要作为参数传进去。如果脚本报检测不到人脸,先检查图片是不是太暗、人脸太小或者侧脸角度过大——检测模型对这三类情况比较敏感。
3.2 main.py 的核心参数逐个说清
跑主推理脚本时,参数顺序和含义必须搞对,否则要么读错文件要么输出空视频。典型调用长这样:
# 源图像 + 驱动视频 + 输出路径 python main.py --source 0_crop.jpg --driving d0.mp4 --output result.mp4如果包里的 main.py 用的是位置参数而非命名参数,就按 README 里的顺序传。几个关键点:
source:裁剪对齐后的源人像图,不是原图。传原图可能也能跑,但效果打折。driving:驱动视频,示例给了d0.mp4。驱动视频里的人脸要清晰,帧率稳定,否则运动信号提取会抖。output:输出视频路径,编码格式一般跟随 OpenCV 的 VideoWriter 设置,常见是 mp4v 或 avc1。
推理链路大致是:源图过外观提取网络拿到特征和关键点 → 驱动视频逐帧过运动提取网络拿到表情和姿态 → 生成器把两者融合渲染 → 写回视频帧。整个过程在 onnxruntime 里就是一次次session.run(),没有 PyTorch 的动态图开销。
3.3 输出视频的编码与帧率对齐
跑完第一遍,最容易翻车的不是模型,是视频编码。OpenCV 写视频时如果编码器选得不对,输出文件要么打不开,要么只有几帧。我一般会显式指定:
# 在 main.py 里找到 VideoWriter 那段,确认编码和帧率 fourcc = cv2.VideoWriter_fourcc(*'mp4v') out = cv2.VideoWriter(output_path, fourcc, fps, (w, h))fps要和驱动视频一致,宽高要和生成帧一致。如果输出视频播放速度不对,八成是 fps 传错了。另外mask_template.png是配合遮罩用的,某些后处理步骤会拿它做区域融合,别删。
4. C++ 工程编译与集成:CMakeLists 配置、faceanalysis 与 liveportrait 模块拆解
4.1 CMakeLists.txt 里必须改的三处路径
C++ 工程能不能编过,九成看 CMakeLists.txt 里的路径对不对。打开后重点找这三类:
# 1. onnxruntime 头文件和库路径 set(ONNXRUNTIME_ROOT "你的路径/onnxruntime") include_directories(${ONNXRUNTIME_ROOT}/include) link_directories(${ONNXRUNTIME_ROOT}/lib) # 2. OpenCV find_package(OpenCV REQUIRED) include_directories(${OpenCV_INCLUDE_DIRS}) # 3. 链接目标 target_link_libraries(your_target ${OpenCV_LIBS} onnxruntime)Windows 下链接的库名通常是onnxruntime.lib,Linux 下是libonnxruntime.so。如果编译时报LNK2019未解析外部符号,先确认库路径和库名没写错,再确认 32/64 位一致。
4.2 faceanalysis 模块在做什么
faceanalysis.h/cpp负责人脸检测和关键点提取,是整条链路的前置。它一般封装了一个轻量检测模型(比如 SCRFD 或类似结构)的 onnxruntime 推理,输入一张图,输出人脸框和五点/多点关键点。这些关键点后面要用来做对齐和运动参考。
看这个模块时重点关注输入输出的张量形状和归一化方式。检测模型的输入通常是1x3xHxW,像素值归一化到 0~1 或减均值除方差,具体看模型导出时的设定。归一化搞错,检测框会飘。
4.3 liveportrait 模块的推理封装
liveportrait.h/cpp是核心,把外观提取、运动提取、生成器几个 onnxruntime session 串起来。典型结构是类里持有多个Ort::Session对象,提供init()加载模型、run()执行推理的接口。
// 伪代码示意,实际以包内实现为准 class LivePortrait { public: bool init(const std::string& model_dir); cv::Mat animate(const cv::Mat& source, const cv::Mat& driving_frame); private: Ort::Session appearance_session_; Ort::Session motion_session_; Ort::Session generator_session_; };main.cpp则是把读视频、逐帧调用、写视频串起来的主流程。编译前确认main.cpp里的模型路径、输入视频路径都改成你本地的实际路径。
4.4 编译命令与常见链接错误
cd cpp mkdir build && cd build cmake .. cmake --build . --config ReleaseRelease 模式很重要,Debug 下 onnxruntime 推理会慢好几倍。如果 cmake 阶段报找不到 onnxruntime,检查ONNXRUNTIME_ROOT是否指向了正确的解压目录;如果链接阶段报错,检查库文件名和平台后缀。Linux 下还可能需要把 onnxruntime 的 lib 目录加进LD_LIBRARY_PATH,否则运行时报找不到共享库。
5. 避坑与排查:模型路径、显存、关键点抖动这些翻车点
5.1 报错找不到 onnx 模型文件
现象:程序启动即退出,日志显示Failed to load model或No such file。 原因:模型文件没下载,或者路径拼接时多了/少了一层目录,Windows 下反斜杠和正斜杠混用也可能出问题。 解决:打印出程序实际拼接的完整路径,和文件系统里的真实路径逐字符比对。统一用正斜杠或std::filesystem::path拼接。
5.2 GPU 推理时显存不足
现象:跑几帧后崩溃,报CUDA out of memory或 onnxruntime 的 allocation 失败。 原因:驱动视频分辨率太高,或者同时加载了多个 session 没释放中间张量。 解决:先把驱动视频缩到 512 或 256 宽度试跑,确认能通再逐步放大。onnxruntime 的 GPU 内存池可以通过 session options 限制,别让它无限涨。
5.3 输出视频人脸区域抖动或跳变
现象:生成的动画在某些帧突然歪一下或者闪。 原因:驱动视频里人脸检测在某几帧失败,关键点跳到了背景上,运动信号就脏了。 解决:对驱动视频先做一遍人脸检测过滤,检测置信度低的帧做插值或者跳过。也可以在运动信号上加一点时序平滑,常见做法是滑动平均。
5.4 C++ 编译过但运行时报 DLL 缺失
现象:exe 双击没反应,或者命令行报找不到onnxruntime.dll。 原因:动态库没放到 exe 同目录,或者系统 PATH 里没有。 解决:把 onnxruntime 的 dll 复制到 exe 旁边,最省事。Windows 下也可以用dumpbin /dependents看依赖了哪些 dll,逐个补齐。
5.5 Python 和 C++ 结果不一致
现象:同一张源图同一个驱动视频,两条路输出效果有差异。 原因:预处理(裁剪、归一化、颜色通道顺序)实现不一致,或者模型版本不同。 解决:先确认两边用的是同一批 onnx 文件,再逐行比对预处理代码。OpenCV 默认 BGR,很多模型要 RGB,这个转换漏了颜色就会偏。
6. 进阶技巧:用 C++ 接口做批量处理和实时预览的取舍
跑通单条之后,真正要落地通常会碰到两个需求:批量处理一堆源图,或者做实时预览。这两件事在 C++ 里的做法不太一样。
批量处理的关键是把模型加载和推理分开。init()只调一次,把几个 session 常驻内存,然后循环读图、推理、写文件。我一般会写一个简单的任务队列:
// 初始化一次,复用 session LivePortrait lp; lp.init(model_dir); for (const auto& img_path : image_list) { cv::Mat src = cv::imread(img_path); cv::Mat driving = cv::imread(driving_frame_path); cv::Mat out = lp.animate(src, driving); cv::imwrite("out_" + img_path, out); }这样比每次重新 init 快一个数量级,因为省掉了模型加载和显存分配的开销。
实时预览则是另一回事。onnxruntime 在 CPU 上跑一帧 LivePortrait 通常要几百毫秒,做不到 30fps。想实时,要么上 GPU 并把分辨率压到 256 以内,要么做帧间缓存——只对关键帧跑完整推理,中间帧用光流或者简单形变过渡。这个取舍没有标准答案,看你的场景能接受多少延迟。
验证结果是否正常,我有个笨办法但很管用:拿同一张源图,分别用原始驱动视频和一段静止画面去驱动。静止画面驱动出来的结果应该几乎不动,如果还在大幅晃,说明运动信号提取或者时序处理有问题。这个对照测试能快速区分是模型问题还是输入问题。
从那以后我每次拿到这类 onnxruntime 部署包,都先跑一遍静止驱动对照,再跑正常驱动,两步都过了才往工程里集成。希望帮到你。
本文还有配套的精品资源,点击获取