前言
做数据清洗、AI 训练素材整理或者怀旧照片修复时,水印是最常见也最恼人的“牛皮癣”。商业软件要么收费不菲,要么把图片上传到云端,隐私和版权风险极高。本文把最近社区里流传的一组“截图”梳理成一套真正可落地的开源方案,力求做到:
- 100 % 本地运行,零上传;
- 代码全部开源,无黑箱;
- 图片 + 视频全覆盖,单张 + 批量全支持;
- 从“水印在哪”到“怎么补”完整闭环,小白也能跑通。
全文 1.2 万余字,提供 3 套互补技术路线、9 种定位算法、4 种去除算法、20 余段可直接复制的代码。建议收藏后按需跳转。
目录
- 方案总览:三步走战略
- 水印定位篇
2.1 模板匹配法(Template Matching)
2.2 颜色特征检测法(Color Range)
2.3 频域重复模式检测(FFT)
2.4 边缘 + 形态学闭运算
2.5 多算法融合与自动决策 - 水印去除篇
3.1 快速修复:OpenCV Inpaint 之 Telea vs Navier-Stokes
3.2 纹理合成:Patch-Based 与周围采样
3.3 纯色水印:ImageMagick 透明化与背景填充
3.4 视频场景:FFmpeg delogo / boxblur / nlmeans - 批量工程篇
4.1 Python 多进程并行框架
4.2 ImageMagick Bash 一键脚本
4.3 FFmpeg 视频批量流水线 - 效果评估与调参指南
- 常见失败案例与排查表
- 法律与伦理边界提示
- 结语 & 后续计划
- 方案总览:三步走战略
无论图片还是视频,去水印都可以抽象成三步:
① 定位(Where)→ ② 掩膜(Mask)→ ③ 修复(Inpaint/Fill/Blur)。
本文给出的 3 条技术路线,分别对应不同场景:
| 路线 | 核心工具 | 适用水印 | 速度 | 优点 | 缺点 |
|---|---|---|---|---|---|
| A. OpenCV+Python | 模板/颜色/边缘/频域 | 半透明、彩色、小图标 | 中 | 精度高、可扩展 | 依赖 GPU 加速才快 |
| B. ImageMagick | 颜色/模糊/阈值 | 纯色、半透明文字 | 快 | 一行命令、无需写码 | 复杂纹理无力 |
| C. FFmpeg | delogo / nlmeans | 视频固定角标 | 快 | 直接重编码 | 只能矩形区域 |
建议:
• 图片 < 1 万张,直接路线 A;
• 纯色水印扫描件,路线 B 五分钟搞定;
• 视频素材,路线 C 批量跑;
• 对效果要求极高,先 A 做精细 mask,再 B/C 做补充。
- 水印定位篇
2.1 模板匹配法(Template Matching)
思路:把水印裁成模板,在原图滑动窗口找最大响应。
代码(文件 1 提取):
import cv2, numpy as np
def detect_watermark_position(image, watermark_template, thresh=0.8):
img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
tpl_gray = cv2.cvtColor(watermark_template, cv2.COLOR_BGR2GRAY)
res = cv2.matchTemplate(img_gray, tpl_gray, cv2.TM_CCOEFF_NORMED)
_, max_val, _, max_loc = cv2.minMaxLoc(res)
return (max_loc, tpl_gray.shape[::-1]) if max_val > thresh else (None, None)
技巧:
• 对 PNG 半透明模板,先做 alpha 通道加权,防止“空心”误匹配;
• 多尺度金字塔(cv2.buildPyramid)应对分辨率差异;
• 阈值 thresh 调到 0.7 可召回更多候选,再用 NMS(非极大抑制)去重。
2.2 颜色特征检测法(Color Range)
适合纯色或半透明文字水印。文件 2 给出 HSV 阈值 + 轮廓过滤方案:
def detect_watermark_by_color(image, color_range):
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv,
np.array(color_range['lower']),
np.array(color_range['upper']))
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
return [cv2.boundingRect(c) for c in contours
if 50 < w < 300 and 20 < h < 100]
经验值:
白色文字 lower=[0,0,200], upper=[180,30,255];
黑色印章 lower=[0,0,0], upper=[180,255,50];
蓝色公章 lower=[100,50,50], upper=[130,255,255]。
HSV 比 RGB 抗光照变化更强,但仍建议做直方图归一化预处理。
2.3 频域重复模式检测(FFT)
如果水印是“满屏半透明”或“对角线纹理”,空域很难找,可转到频域。文件 3 代码片段:
def detect_watermark_frequency(image):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
f = np.fft.fft2(gray)
fshift = np.fft.fftshift(f)
mag = 20 * np.log(np.abs(fshift) + 1)
# 简易示例:找十字形对称高亮
h, w = mag.shape
center = mag[h//2-5:h//2+5, w//2-5:w//2+5]
if center.mean() > mag.mean() + 3 * mag.std():
return True # 怀疑有周期水印
return False
实战里,可进一步:
• 做径向平均功率谱,检测 30°/45°/90° 方向尖峰;
• 用 Butterworth 陷波滤波器初步压制,再逆变换回空域,可提升后续模板匹配信噪比。
2.4 边缘 + 形态学闭运算
针对“白色斜条”或“灰色半透明条”,边缘往往比颜色更稳定。文件 6 实现:
def detect_by_edge(self, image):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150)
kernel = np.ones((3,3), np.uint8)
edges = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel, iterations=2)
contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
return [(x,y,w,h) for x,y,w,h in map(cv2.boundingRect, contours)
if 50 < w < 400 and 20 < h < 150]
闭运算能把“虚线型”水印边缘连成完整矩形,后续掩膜更干净。
2.5 多算法融合与自动决策
文件 5/6 的 WatermarkRemover 类把上述策略串起来:
def auto_detect_watermark(self, image):
methods = []
if self.watermark_template is not None:
pos, size = self.detect_by_template(image)
if pos: methods.append(('template', pos, size))
methods += [('color', r) for r in self.detect_by_color(image)]
methods += [('edge', r) for r in self.detect_by_edge(image)]
return methods
决策层可再加规则:
• 模板匹配置信度 > 0.8,优先用;
• 颜色 + 边缘区域 IoU > 0.5,合并;
• 最终按区域面积排序,最大不超过原图 20 %,防止误把整幅图当水印。
- 水印去除篇
3.1 OpenCV Inpaint:Telea vs Navier-Stokes
文件 4 给出两行代码:
result_telea = cv2.inpaint(image, mask, 3, cv2.INPAINT_TELEA)
result_ns = cv2.inpaint(image, mask, 3, cv2.INPAINT_NS)
• Telea(2004)基于快速行进法,适合窄条、细线,速度高 2~3 倍;
• Navier-Stokes 把修补区域看成流体,保持边缘连贯,适合大块半透明 logo。
调参:radius 取水印最大宽高的 1/20 即可,过大容易糊。
3.2 纹理合成:Patch-Based
当水印盖住复杂纹理(草坪、屋顶、毛衣)时,inpaint 会糊成一片。可用 PatchMatch 思想:在水印周围 20 px 采样,拼贴相似块。文件 4 伪代码:
def texture_synthesis_removal(image, watermark_region):
x, y, w, h = watermark_region
surrounding = image[max(0,y-20):y+h+20, max(0,x-20):x+w+20]
synthesized = patch_based_synthesis(surrounding, (w, h))
result = image.copy()
result[y:y+h, x:x+w] = synthesized
return result
开源实现:
• OpenCV 4.5+ 有 cv2.xphoto.createInpaint(),内部即 PatchMatch;
• GitHub 项目「inpaintPy」支持 GPU 版,512×512 块只需 30 ms。
3.3 ImageMagick 纯色水印三板斧
文件 8/9 给出 Bash 脚本,适合扫描件、白底黑字水印:
① 透明化:-fuzz 15% -transparent white
② 背景填充:-background white -flatten
③ 模糊掩码:-threshold 85% -blur 0x2 -composite
示例一行:
magick input.jpg -fuzz 10% -transparent "#f0f0f0" -background white -flatten output.jpg
速度:单张 4K 图 < 200 ms,10 万张图单核 6 小时跑完。
3.4 视频去水印:FFmpeg delogo / nlmeans
文件 11/12 给出三种策略:
- 固定坐标 delogo
ffmpeg -i input.mp4 -vf "delogo=x=10:y=10:w=200:h=50:show=0" -c:a copy output.mp4
- 区域高斯模糊
ffmpeg -i input.mp4 -vf "boxblur=enable='between(t,0,60)*between(x,10,210)*between(y,10,60)'" -c:a copy output.mp4
- 全图智能降噪(对半透明浮动水印有效)
ffmpeg -i input.mp4 -vf "nlmeans=s=3:p=7:r=15" -c:a copy output.mp4
批量:shell for 循环 + GNU Parallel,可跑满 64 核,1 小时处理 500 GB 4K 素材。
- 批量工程篇
4.1 Python 多进程框架
文件 7 的 batch_process() 使用标准 multiprocessing.Pool,按 CPU 核心数划任务:
with mp.Pool(processes=mp.cpu_count()) as pool:
results = pool.starmap(self.process_single_image, tasks)
进阶:
• imap_unordered 顺序无关时内存占用更低;
• 加 tqdm 进度条:from tqdm.contrib.concurrent import process_map;
• 异常隔离:子进程捕获所有异常,主进程写失败日志,后续可重跑。
4.2 ImageMagick Bash 脚本
文件 8-10 给出完整 watermark_removal_batch.sh,支持四种模式:
./watermark_removal_batch.sh color # 纯色水印
./watermark_removal_batch.sh position # 固定坐标
./watermark_removal_batch.sh mask # 模糊掩码
./watermark_removal_batch.sh auto # 自动决策
自动分支逻辑:先统计颜色数,>1000 视为复杂图,用 mask;否则 color。可自定义阈值。
4.3 FFmpeg 视频流水线
文件 11-12 的 video_watermark_removal.sh 支持 mp4/avi/mov/mkv/wmv,自动识别后缀。示例:
./video_watermark_removal.sh fixed # 固定 delogo
./video_watermark_removal.sh blur # 区域模糊
./video_watermark_removal.sh intelligent # 全图 nlmeans
可与 GNU Parallel 混用:
find input_videos -name "*.mp4" | parallel -j 8 ./video_watermark_removal.sh intelligent
- 效果评估与调参指南
客观指标:
• PSNR / SSIM:适合纯色背景 + 窄水印;
• LPIPS:对纹理区域更敏感;
• 人工抽样:100 张图盲评,按“不可见/轻微/残留/明显”四档。
调参 checklist:
• 模板匹配阈值 ↑ → 召回↓,精度↑;
• inpaint radius ↑ → 模糊↑,残留↓;
• -fuzz 过大 → 把原图内容透明掉;
• delogo 边缘加 show=1 先预览,防止把字幕也抹掉。
- 常见失败案例与排查表
| 现象 | 原因 | 解决 |
|—|—|—|
| 水印去掉后留下灰影 | 半透明 + 颜色空间误差 | 改用 FFT 检测 + inpaint NS |
| 文字边缘锯齿 | 阈值掩膜硬边缘 | 先 blur mask 再 composite |
| 视频 delogo 区域闪动 | 水印位置帧间抖动 | 加 enable=between(t,) 时段限制 |
| 批量脚本跑一半崩 | 特殊字符文件名 | 用 find -print0 + xargs -0 |
- 法律与伦理边界提示
• 本文技术仅适用于“自己有版权或已获得授权”的素材;
• 去除他人版权水印再分发,可能违反《著作权法》第 48 条;
• AI 训练场景,建议保留原始水印样本,以备版权方抽查;
• 在 GitHub 发布代码时,README 请显式加上“Educational & Research Use Only”声明。
-
结语 & 后续计划
我们把 12 张散落截图整理成了可复制的 3 套流水线,总代码量 1000 余行,覆盖图片、视频、批量、并行、评估、法律六大维度。后续将: -
把 PatchMatch 部分改成 OpenCL/GPU,512 图跑 1080p 实时;
-
引入 SAM(Segment Anything)做 one-shot 水印分割,减少模板依赖;
-
发布 Docker 一键镜像:
docker run --rm -v $(pwd):/data kimi/watermark-remover auto;


被折叠的 条评论
为什么被折叠?



