用 Python + OpenCV + ffmpeg 做视频去水印:掩膜修复与音视频合成踩坑记
·
做桌面端视频去水印工具时,踩过不少坑。本文把核心流程(抽帧 → 标注掩膜 → 修复 → 合成回视频)和几个关键坑位讲清楚,代码可直接复用。
一、整体流程
去水印不是"一键滤镜",本质是**图像修复(inpainting)**逐帧处理:
- 用 ffmpeg 把视频按帧抽成图片序列;
- 在首帧上标注水印区域,生成二值掩膜(mask);
- 对每一帧用
cv2.inpaint按掩膜修复; - 修复后的帧序列用 ffmpeg 重新编码,并保留原音轨。
二、抽帧与掩膜
import cv2, subprocess, os
# 抽帧(用 ffmpeg 比 OpenCV 读流更稳,不丢帧)
os.makedirs("frames", exist_ok=True)
subprocess.run([
"ffmpeg", "-i", "input.mp4",
"-vsync", "0", "frames/f_%05d.png"
], check=True)
掩膜用一张和帧同尺寸的黑白图:水印区域填 255,其余填 0。标注可以用鼠标框选,这里省略交互,直接给坐标示例:
import numpy as np
h, w = 720, 1280
mask = np.zeros((h, w), np.uint8)
mask[40:110, w-360:w-20] = 255 # 右下角水印矩形
三、逐帧修复(核心坑:中文路径)
def inpaint_dir(frames_dir, mask, out_dir):
os.makedirs(out_dir, exist_ok=True)
for name in sorted(os.listdir(frames_dir)):
# 坑1:cv2.imread 读中文路径返回 None
path = os.path.join(frames_dir, name)
img = cv2.imdecode(np.fromfile(path, dtype=np.uint8), cv2.IMREAD_COLOR)
if img is None:
continue
res = cv2.inpaint(img, mask, 3, cv2.INPAINT_TELEA)
# 坑2:同样要用 imencode 写中文路径
cv2.imencode(".png", res)[1].tofile(os.path.join(out_dir, name))
两个高频坑:
- 中文路径:
cv2.imread/imwrite在 Windows 下对中文路径直接失效,必须走imdecode(np.fromfile(...))和imencode(...)[1].tofile(...)。 - 算法选择:
INPAINT_TELEA快但边缘易糊,INPAINT_NS质量更好但慢。水印区域大时优先 NS。
四、合成回视频并保留音轨(关键坑)
帧合成视频时最容易把声音弄丢:
ffmpeg -i out/f_%05d.png -i input.mp4 \
-map 0:v:0 -map 1:a:0 \
-c:v libx264 -crf 18 -c:a copy \
-shortest output.mp4
-map 1:a:0 把原视频音轨映射到输出,-shortest 防止帧数和音轨长度不一致时无限等待。
五、性能与体验
逐帧 Python 修复在 1080p 长视频上很慢。实际做成桌面工具时,建议:
- 用
concurrent.futures多进程并行修复帧; - 抽帧/合成走 ffmpeg 子进程,避免 Python 逐帧编码;
- GUI 用 customtkinter,标注掩膜做成"画框即生成 mask"。
把这套流程封装进工具后,用户只要框选水印、点开始,剩下的抽帧—修复—合成全自动。这也是我们做"去水印"这个小而美工具的产品思路:把工程上的坑提前踩完,留给用户的只有最少的几步操作。
如果你也在做类似的音视频处理工具,欢迎交流 ffmpeg 管道和 cv2 的中文路径处理经验。
火山引擎视频云技术社区,是面向 AI 音视频开发者的技术交流平台。这里汇聚源自抖音、豆包等亿级 DAU 产品的 RTC、直播、点播、AI 媒体处理、音视频互动技术,提供接入指南、最佳实践、性能调优、场景案例、Demo 代码、开源项目、白皮书和 API 文档。社区汇聚官方工程师与一线开发者,为 AI 视频通话、数字人、AI 视频处理等应用的开发与落地提供技术支持。
更多推荐
所有评论(0)