Python自动去除水印

这是一个Python脚本,用于自动或半自动去除图片水印,支持白色、颜色、Top-hat等多种检测模式。

```python

-*- coding: utf-8 -*-

"""

watermark_remover.py ------ 自动 / 半自动去除图片水印

依赖: pip install opencv-python numpy

用法示例:

1) 白色水印,限定在右下角区域自动检测

python watermark_remover.py -i in.jpg -o out.jpg --mode white --thresh 200 \

--roi 0.55 0.75 1.0 1.0

2) 用自己画好的 mask(白色=水印区域)

python watermark_remover.py -i in.jpg -o out.jpg --mask mask.png

3) 彩色水印,指定 HSV 范围

python watermark_remover.py -i in.jpg -o out.jpg --mode color \

--hsv 0 0 200 180 40 255

4) 浅色细线半透明水印

python watermark_remover.py -i in.jpg -o out.jpg --mode tophat --thresh 25

"""

import argparse

import cv2

import numpy as np

---------------------------------------------------------------------------

1. 生成 mask 的几种方式

---------------------------------------------------------------------------

def parse_roi(img, roi):

"""roi = x1, y1, x2, y2(相对坐标 0~1) -> 像素坐标 (x1, y1, x2, y2)"""

h, w = img.shape:2

x1, y1, x2, y2 = roi

return int(x1 * w), int(y1 * h), int(x2 * w), int(y2 * h)

def limit_to_roi(mask, roi_px):

"""把 mask 限制在 ROI 之内,避免误伤画面其他区域"""

if roi_px is None:

return mask

x1, y1, x2, y2 = roi_px

out = np.zeros_like(mask)

outy1:y2, x1:x2 = masky1:y2, x1:x2

return out

def mask_by_white(img, thresh=200, roi_px=None):

"""亮度过阈值 ------ 适合白色 / 半透明白色水印"""

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

_, m = cv2.threshold(gray, thresh, 255, cv2.THRESH_BINARY)

return limit_to_roi(m, roi_px)

def mask_by_color(img, lower, upper, roi_px=None):

"""HSV 颜色范围 ------ 适合固定颜色的水印"""

hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

m = cv2.inRange(hsv, np.array(lower, np.uint8), np.array(upper, np.uint8))

return limit_to_roi(m, roi_px)

def mask_by_tophat(img, ksize=15, thresh=25, roi_px=None):

"""

形态学 Top-hat:提取"比周围更亮的细小结构",

对半透明的浅色线条 / 文字水印比较有效。

"""

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

k = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (ksize, ksize))

top = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, k)

_, m = cv2.threshold(top, thresh, 255, cv2.THRESH_BINARY)

return limit_to_roi(m, roi_px)

def filter_components(mask, min_area=6, max_area_ratio=0.30):

"""按连通域面积过滤:去掉噪点,以及大到不可能是水印的亮块"""

num, labels, stats, _ = cv2.connectedComponentsWithStats(mask, 8)

h, w = mask.shape

out = np.zeros_like(mask)

for i in range(1, num):

area = statsi, cv2.CC_STAT_AREA

if area < min_area or area > max_area_ratio * h * w:

continue

outlabels == i = 255

return out

---------------------------------------------------------------------------

2. 核心:用 mask 修复图像

---------------------------------------------------------------------------

def remove_watermark(img, mask,

radius=3, method="telea",

dilate=3, use_cc_filter=False):

"""

img : BGR 原图

mask : 单通道,非 0 = 需要修复的区域

radius : inpaint 邻域半径,水印越粗越大

method : "telea"(快) 或 "ns"(平滑)

dilate : 对 mask 膨胀的核大小,保证把水印边缘也覆盖到

"""

if mask.ndim == 3:

mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)

_, mask = cv2.threshold(mask, 127, 255, cv2.THRESH_BINARY)

if use_cc_filter:

mask = filter_components(mask)

if dilate and dilate > 0:

k = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (dilate, dilate))

mask = cv2.dilate(mask, k, iterations=1)

flag = cv2.INPAINT_TELEA if method.lower() == "telea" else cv2.INPAINT_NS

return cv2.inpaint(img, mask, radius, flag)

---------------------------------------------------------------------------

3. 命令行入口

---------------------------------------------------------------------------

def main():

ap = argparse.ArgumentParser(description="自动 / 半自动去除图片水印")

ap.add_argument("-i", "--input", required=True, help="输入图片")

ap.add_argument("-o", "--output", default="output.jpg", help="输出图片")

ap.add_argument("--mask", help="已有 mask 图(白色=水印)")

ap.add_argument("--mode", choices="white", "color", "tophat", "none",

default="white", help="自动检测方式")

ap.add_argument("--roi", nargs=4, type=float,

metavar=("X1", "Y1", "X2", "Y2"),

help="水印所在区域的相对坐标 0~1,强烈建议指定")

ap.add_argument("--thresh", type=int, default=200,

help="white / tophat 模式的阈值")

ap.add_argument("--hsv", nargs=6, type=int,

metavar=("H1", "S1", "V1", "H2", "S2", "V2"),

help="color 模式的 HSV 上下界")

ap.add_argument("--ksize", type=int, default=15, help="tophat 结构元大小")

ap.add_argument("--radius", type=int, default=3, help="inpaint 半径")

ap.add_argument("--method", choices="telea", "ns", default="telea")

ap.add_argument("--dilate", type=int, default=3, help="mask 膨胀核大小")

ap.add_argument("--filter-cc", action="store_true", help="按连通域面积过滤 mask")

ap.add_argument("--show-mask", action="store_true", help="输出 mask 便于调参")

args = ap.parse_args()

img = cv2.imread(args.input, cv2.IMREAD_COLOR)

if img is None:

raise SystemExit(f"错误 无法读取图片: {args.input}")

roi_px = parse_roi(img, args.roi) if args.roi else None

---- 生成 mask ----

if args.mask:

mask = cv2.imread(args.mask, cv2.IMREAD_GRAYSCALE)

if mask is None:

raise SystemExit(f"错误 无法读取 mask: {args.mask}")

if mask.shape:2 != img.shape:2:

mask = cv2.resize(mask, (img.shape1, img.shape0),

interpolation=cv2.INTER_NEAREST)

mask = limit_to_roi(mask, roi_px) if roi_px else mask

elif args.mode == "white":

mask = mask_by_white(img, args.thresh, roi_px)

elif args.mode == "color":

if not args.hsv:

raise SystemExit("错误 color 模式需要 --hsv 参数")

mask = mask_by_color(img, args.hsv:3, args.hsv3:, roi_px)

elif args.mode == "tophat":

mask = mask_by_tophat(img, args.ksize, args.thresh, roi_px)

else:

mask = np.zeros(img.shape:2, np.uint8)

if args.show_mask:

cv2.imwrite("mask_debug.png", mask)

print("提示 mask 已保存为 mask_debug.png")

---- 修复 ----

result = remove_watermark(img, mask,

radius=args.radius,

method=args.method,

dilate=args.dilate,

use_cc_filter=args.filter_cc)

cv2.imwrite(args.output, result)

print(f"完成 已保存到 {args.output}")

if name == "main":

main()

```

水印去除的几种检测模式与修复流程

脚本把去水印拆成两步:先定位水印区域,再让算法把这块区域"补"回来。

mask 生成策略

· white 模式按亮度阈值抓取高亮区域,适合白色或半透明白色水印。

· color 模式通过 HSV 颜色范围匹配固定颜色的水印。

· tophat 模式利用形态学顶帽运算,提取比周围更亮的细线或半透明文字。

· 也可以用 --mask 直接传入自己画好的掩膜图。

区域限制与过滤

· --roi 参数把检测范围限定在水印所在区域,避免误伤画面其他部分。

· --filter-cc 会按连通域面积过滤噪点,同时排除面积过大的亮块。

图像修复

· 核心用 OpenCV 的 cv2.inpaint 做修复,支持 telea 和 ns 两种算法。

· --dilate 会先膨胀 mask,确保水印边缘也被覆盖,避免残留。

· --radius 控制修复邻域大小,水印越粗就调得越大。


优化建议: 如果水印位置固定,建议先用 --roi 框定大致范围;白色水印可尝试调整 --thresh,半透明细线水印可微调 --ksize 和 --thresh,并加 --filter-cc 减少噪点误判。

此文章仅供参考借鉴。

相关推荐
大圣编蚕1 小时前
C语言入门:基本语法汇总
c语言·开发语言
czq_26867194871 小时前
Python打卡第31天
开发语言·python
invicinble1 小时前
python 编程语言 认识维度
开发语言·数据库·python
AC赳赳老秦1 小时前
数据采集全链路审计留痕:用 OpenClaw 实现合规审计与追溯
开发语言·汇编·python·php·swift·deepseek·openclaw
czhc11400756631 小时前
退出防呆:用“总闸“收口所有退出入口,以及三个并发语法坑
开发语言·c#
张铁霖1 小时前
深度解析:AI三次浪潮与机器学习三种范式,
python
学也学不废1 小时前
论文阅读:Advancing Semantic Textual Similarity Modeling
python
悠仁さん2 小时前
【C++】vector 类
开发语言·c++
风早爽太2 小时前
Python 开发笔记:配置生产环境中 Celery Worker 的独立进程启动方式
python·fastapi