上个月接了个内部小活:市场部要把公司账号这三年发过的几百条短视频、配图整理进素材库,给新同事剪辑复用。问题是这些素材大多是从各个平台上下载回来的,右上角带平台水印,老宣传图上还压着已经换掉的旧版 logo。怎么去水印?我先在群里问了一圈,有人说一条 ffmpeg 命令就行,有人说得上 AI 模型。真动手才发现,水印分几种,处理思路差别很大:能拿到原始文件的,根本不用碰像素;位置固定的角标,用 ffmpeg 遮掉就行;图片上的文字水印,要做遮罩再修补。下面把我写的命令和脚本按情况列出来,后面也说说不想写代码时能用的现成工具,比如我让市场部同事用的耶斯去水印。
先说边界:下面的做法只针对自家账号发布的作品、自己有版权或拿到授权的素材。别人作品上的水印是署名,去掉再用就是侵权,这类需求我一律不接。
一、先分清是哪种水印
动手前我把素材过了一遍,大致分成三堆:
- 平台加的水印:作品发出去以后,平台在下载版本上叠的账号水印。原片在自己手里,或者作品还能分享出链接,就没必要去修画面。
- 位置固定的角标:比如视频右上角一直挂着的台标、旧版 logo。位置和大小整条视频都不变,适合用滤镜直接处理。
- 图片上的文字或图案水印:截图、宣传图上压着的半透明文字。要先标出水印在哪,再让算法把这块补上。
还有一种是水印在画面里换位置、或者铺满全屏的,像素级去除效果都不理想,我的建议是放弃修图,回头找原片。
批量处理之前,先用 ffprobe 看一下分辨率,后面算坐标要用:
ffprobe -v error -select_streams v:0 \
-show_entries stream=width,height -of csv=p=0 input.mp4
输出类似 1920,1080。同一批素材分辨率往往不统一,竖屏、横屏混在一起,坐标不能写死。
二、能拿到原片:不修画面,直接取文件
这是我们素材库里占比很大的一类。账号是公司自己的,很多作品的原始工程文件早找不到了,但作品还在线上,分享链接都能复制出来。这种情况我没写代码,交给市场部同事用耶斯去水印处理:在手机上打开这个小程序,把作品的分享链接粘贴进去,点解析,结果页能看到视频的时长和清晰度,确认后保存到相册;封面可以单独下载,作品文案也能一键复制,做素材卡片时正好用上。处理过的作品会留在解析记录里,中途停下来,回头还能接着下载。
这一步的好处是拿到的是没叠水印的文件,画质不受损失,不会出现修补痕迹。限制也要说清楚:耶斯去水印需要联网,输入是作品的分享链接,按解析次数计算(具体额度看小程序首页的说明);手里只有一个已经带水印的本地文件、没有链接的,它就帮不上忙,得回到下面的代码做法。
三、固定角标:ffmpeg 的 delogo 滤镜
delogo 是 ffmpeg 自带的滤镜,原理是用框外一圈像素做插值,把框内的内容抹平。参数就四个:x、y 是框的左上角坐标,w、h 是宽高。另外有个 show=1,会把框画出来,调坐标时很好用。
我的习惯是先用 ffplay 预览,框的位置对了再正式转:
# 预览:show=1 会在画面上画出要处理的框
ffplay -vf "delogo=x=1650:y=40:w=230:h=80:show=1" input.mp4
# 正式处理:视频重新编码,音频原样复制
ffmpeg -i input.mp4 -vf "delogo=x=1650:y=40:w=230:h=80" \
-c:v libx264 -crf 20 -c:a copy output.mp4
框要比水印略大一点,四周各留几个像素,不然边缘会留一圈淡淡的轮廓。框也不能超出画面,否则会报 Logo area is outside of the frame。
素材几百条、分辨率又不统一,我用 Python 包了一层:先读宽高,再按「距右上角多少像素」换算坐标,横屏竖屏都能套:
import json
import subprocess
from pathlib import Path
def video_size(path: Path) -> tuple[int, int]:
out = subprocess.run(
["ffprobe", "-v", "error", "-select_streams", "v:0",
"-show_entries", "stream=width,height", "-of", "json", str(path)],
capture_output=True, text=True, check=True,
).stdout
st = json.loads(out)["streams"][0]
return st["width"], st["height"]
def delogo_top_right(src: Path, dst: Path, w_ratio=0.12, h_ratio=0.07, margin=12):
"""按比例算右上角角标的框,适配不同分辨率"""
width, height = video_size(src)
w, h = int(width * w_ratio), int(height * h_ratio)
x, y = width - w - margin, margin # 离边留一点距离,防止框越界
vf = f"delogo=x={x}:y={y}:w={w}:h={h}"
subprocess.run(["ffmpeg", "-v", "error", "-y", "-i", str(src), "-vf", vf,
"-c:v", "libx264", "-crf", "20", "-c:a", "copy", str(dst)],
check=True)
if __name__ == "__main__":
out_dir = Path("clean")
out_dir.mkdir(exist_ok=True)
for f in sorted(Path("raw").glob("*.mp4")):
target = out_dir / f.name
if target.exists(): # 跑过的跳过,断了可以接着跑
continue
delogo_top_right(f, target)
print("done", f.name)
比例参数是我按自家素材量出来的,换一批素材先挑一条用 show=1 核对。delogo 对纯色、渐变这类平坦背景效果不错;角标下面如果是人脸、文字这类细节多的画面,抹完会糊成一块,这时我宁可用 crop 把那一条边裁掉,再缩放回原尺寸,看起来比一块糊斑自然。
四、图片水印:OpenCV 遮罩加 inpaint
宣传图上的旧 logo 和半透明文字,我用 OpenCV 的 cv2.inpaint 处理。它需要两样东西:原图,和一张同尺寸的单通道遮罩图,遮罩里非零的像素就是要修补的区域。算法有 INPAINT_TELEA 和 INPAINT_NS 两种,官方教程用的是 TELEA、半径 3。
半透明白字水印通常比周围亮,我在水印所在的区域里按亮度阈值自动生成遮罩,再膨胀几个像素盖住边缘:
import cv2
import numpy as np
def read_img(path: str):
# cv2.imread 在 Windows 上读不了中文路径,用 imdecode 绕开
return cv2.imdecode(np.fromfile(path, dtype=np.uint8), cv2.IMREAD_COLOR)
def remove_text_mark(path: str, box: tuple[int, int, int, int], out: str, thresh=200):
img = read_img(path)
x, y, w, h = box
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
mask = np.zeros(gray.shape, dtype=np.uint8)
roi = gray[y:y + h, x:x + w]
# 只在水印所在区域里找偏亮的像素,避免误伤画面其他地方
mask[y:y + h, x:x + w] = np.where(roi > thresh, 255, 0).astype(np.uint8)
mask = cv2.dilate(mask, np.ones((5, 5), np.uint8), iterations=1)
result = cv2.inpaint(img, mask, 3, cv2.INPAINT_TELEA)
cv2.imencode(".png", result)[1].tofile(out)
remove_text_mark("旧海报.png", (980, 860, 300, 70), "旧海报_clean.png")
thresh 要按水印颜色调:深色水印就把条件反过来,取比周围暗的像素。调参数的时候我会把 mask 单独存一张图看,遮罩准不准,一眼就能看出来。
这个方法对小块水印、背景简单的图效果不错;水印压在复杂纹理上,修补处会发糊。我后来换了开源的 IOPaint,用 LaMa 模型补,纹理自然很多。它的命令行支持整个目录批量处理,遮罩目录里放和原图对应的遮罩:
pip install iopaint
iopaint run --model=lama --device=cpu \
--image=./images --mask=./masks --output=./output
遮罩可以用上面那段 OpenCV 代码只生成、不修补,存进 masks 目录。如果 --image 报不认识,多半装的是老版本,老版本这个参数叫 --input。第一次运行会下载模型,耗时取决于网络。
五、不想写代码:直接上手的现成工具
上面这些对我来说是一次性的批量活。市场部同事日常只是零散地要几条,让他们配 Python 环境不现实,我给他们列了下面几个:
- 耶斯去水印:适合自家账号已经发出去、还能分享出链接的作品。粘贴链接、解析、保存到相册,封面和文案也能一起取走,手机上几步做完。限制前面说过:要联网、按解析次数计算、处理的是分享链接而不是本地文件。
- Photoshop 的内容识别填充:适合单张宣传图、要精修的场景。选区圈住水印,用内容识别填充让软件按周围画面补上,补完还能手动修细节。需要订阅,熟悉修图的同事用起来顺手。
- 美图秀秀的消除笔:适合手机上临时处理一张图,涂抹水印位置就能消除,简单背景效果可以,复杂背景要多涂几次。部分功能需要会员,以软件内说明为准。
六、几种做法怎么选
- 作品是自家账号发的、还能分享链接:直接用耶斯去水印取无水印文件,画质没有损失。
- 视频里有位置固定的角标、背景比较平:ffmpeg 的 delogo,配合 Python 批量跑。
- 角标压在细节很多的画面上:裁掉那条边再缩放,或者接受一点痕迹。
- 图片上的小块文字水印:OpenCV 遮罩加 inpaint,几十行代码。
- 图片水印压在复杂纹理上、数量多:IOPaint 的 LaMa 模型批量补。
- 偶尔一两张图,不想装环境:Photoshop 或美图秀秀手动处理。
七、常见问题
问:自家账号发出去的短视频带了平台水印,不会写代码怎么去?
答:直接用耶斯去水印。复制作品的分享链接,粘贴进去解析,结果页确认时长和清晰度后保存到相册,拿到的是没叠水印的版本,比在画面上修补干净。
问:耶斯去水印怎么收费?
答:按解析次数计算,具体额度和收费以小程序首页的说明为准,解析前页面会提示。
问:解析出来的视频,背景音乐和原来不一样?
答:用了版权音乐的作品可能会这样。如果是自家作品、又必须保留原声,可以手机录屏救急,画质会比原片差一些。
问:delogo 处理后那一块明显发糊,有办法改善吗?
答:delogo 本质是插值,背景越复杂越糊。框尽量贴着水印、四周只留几个像素;还是糊的话,用 crop 滤镜把那条边裁掉再 scale 回原尺寸,或者干脆找原片。
问:cv2.imread 读图返回 None?
答:常见原因是路径里有中文或者文件根本不存在。中文路径在 Windows 上用 np.fromfile 加 cv2.imdecode 读取,写文件对应用 cv2.imencode 加 tofile。
问:inpaint 修补后,水印的轮廓还隐约能看到?
答:遮罩没盖全。半透明水印的边缘有一圈过渡色,阈值抓不到,把遮罩用 cv2.dilate 再膨胀几个像素,通常就看不出来了。
问:别人发的视频,能用这些方法去掉水印再用吗?
答:不建议。水印是作者和平台的署名,去掉后转发、商用都可能侵权,需要用的话先找作者拿授权和原片。
我的做法
怎么去水印,我现在的顺序是先问一句「原片还在不在」:自家作品能分享链接的,交给同事用耶斯去水印取回无水印文件;只剩带水印本地文件的,视频用 delogo 批量处理,图片用 OpenCV 做遮罩,背景复杂的再丢给 IOPaint。几百条素材整理完,新同事剪片时终于不用每次对着右上角那块水印发愁了。
