文章目录
-
- 仓库概述
- 项目改进
-
- [解决命令行环境下无法手动框选 ROI 的问题](#解决命令行环境下无法手动框选 ROI 的问题)
-
- [1. 修改命令行参数解析(`parse_args` 函数)](#1. 修改命令行参数解析(
parse_args函数)) - [2. 修改 `WatermarkDetector` 类以支持命令行ROI](#2. 修改
WatermarkDetector类以支持命令行ROI) - [3. 在主程序中传递命令行ROI参数](#3. 在主程序中传递命令行ROI参数)
- [4. 禁用命令行模式下的图形化操作](#4. 禁用命令行模式下的图形化操作)
- 使用方法
- [1. 修改命令行参数解析(`parse_args` 函数)](#1. 修改命令行参数解析(
- 强制将roi设为待修复区域
-
- [原项目逻辑拆解:ROI ≠ 最终修复区域](#原项目逻辑拆解:ROI ≠ 最终修复区域)
- 解决方案:跳过检测,强制修复整个ROI
仓库基于 WatermarkRemover,由于我是在命令行环境,没有图形化界面,所以 扩展了这个仓库的功能,可以直接手动输入roi的位置 。此外,原项目在roi界面内还有一层水印检测逻辑,由于逻辑过于简单,有时会遗留一些水印,所以我 加入了一层逻辑,直接把roi区域打上mask 。
仓库概述
这个仓库是 WatermarkRemover-local,是一个基于 LAMA 模型的视频水印移除工具,主要用于批量清除视频中的固定水印。以下是其核心信息介绍:
核心功能
- 视频水印移除:基于 LAMA 模型实现对视频中固定水印的自动检测和移除,输出处理后的 MP4 格式视频。
- 本地运行支持:解决原项目依赖模型自动从 Hugging Face Hub 下载的问题,支持离线环境运行(需提前准备模型文件)。
- 命令行适配:无 GUI 界面,支持通过命令行参数手动指定水印区域(ROI)的坐标(左上角 x/y、宽度、高度),适合 Linux 命令行环境使用。
关键特点
-
本地模型部署 :
依赖的 LAMA 模型(
big-lama.pt)需手动放置到默认路径~/.cache/huggingface/hub/,无需在线下载,适配离线环境。 -
灵活的 ROI 选择:
- 支持图形化界面框选 ROI(有 GUI 环境时);
- 命令行环境下可通过
--roi-x、--roi-y、--roi-width、--roi-height参数指定水印区域,解决无界面场景下的操作问题。
-
批量处理:可处理指定目录下的多个视频文件,输出到指定的输出目录。

安装与依赖
- 系统要求:Python 3.10。
- 安装步骤 :
- 克隆仓库并进入目录;
- 可选:创建并激活虚拟环境;
- 安装基础依赖:
pip install -r requirements.txt; - 安装 PyTorch(CPU 或 GPU 版本,GPU 需配合 NVIDIA 显卡、CUDA 和 cuDNN)。
工作流程
- 水印区域选择:通过命令行参数指定或图形化框选水印区域;
- 效果预览(可选):显示处理效果,确认后继续或取消;
- 视频处理:使用 LAMA 模型对视频帧中的水印区域进行修复;
- 输出结果:处理后的视频保存到指定输出目录。
仓库结构
- 核心代码:
watermark_remover.py(实现水印检测、ROI 处理、模型调用等逻辑); - 依赖清单:
requirements.txt(包含lama_cleaner、moviepy、opencv-python等); - 示例图片:
image/目录下的origin.jpg(原始帧)和no_watermark.jpg(去水印后效果); - 配置文件:
.gitignore(忽略虚拟环境、输出目录等)、LICENSE(许可证文本)。
项目改进
- 命令行环境下直接输入roi区域
- 强制将roi设为待修复区域,不进行水印检测
解决命令行环境下无法手动框选 ROI 的问题
以下是基于 watermark_remover.py 现有代码结构的具体修改方案,解决命令行环境无法手动框选ROI的问题:
1. 修改命令行参数解析(parse_args 函数)
在现有参数基础上添加ROI坐标参数,用于指定水印区域的左上角坐标和宽高:
python
def parse_args():
parser = argparse.ArgumentParser(description="Video Watermark Remover")
parser.add_argument("--input", "-i", type=str, default=".", help="Input directory containing videos")
parser.add_argument("--output", "-o", type=str, default="output", help="Output directory")
parser.add_argument("--preview", "-p", action="store_true", help="Preview effect before processing")
# 新增ROI参数
parser.add_argument("--roi-x", type=int, help="ROI左上角x坐标 (命令行模式必传)")
parser.add_argument("--roi-y", type=int, help="ROI左上角y坐标 (命令行模式必传)")
parser.add_argument("--roi-width", type=int, help="ROI宽度 (命令行模式必传)")
parser.add_argument("--roi-height", type=int, help="ROI高度 (命令行模式必传)")
return parser.parse_args()
2. 修改 WatermarkDetector 类以支持命令行ROI
修改 __init__ 方法接收ROI参数,并调整 select_roi 方法优先使用命令行参数:
python
class WatermarkDetector:
# 新增roi参数,默认None
def __init__(self, num_sample_frames=10, min_frame_count=7, dilation_kernel_size=5, roi=None):
self.num_sample_frames = num_sample_frames
self.min_frame_count = min_frame_count
self.dilation_kernel_size = dilation_kernel_size
self.roi = roi # 从外部传入的ROI(命令行参数)
# 修改select_roi方法,优先使用已传入的roi
def select_roi(self, video_clip):
# 如果已通过命令行指定ROI,直接返回
if self.roi is not None:
return self.roi
# 原有图形化框选逻辑(仅在有GUI的环境下生效)
frame = self.get_first_valid_frame(video_clip)
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
display_height = 720
scale_factor = display_height / frame.shape[0]
display_width = int(frame.shape[1] * scale_factor)
display_frame = cv2.resize(frame, (display_width, display_height))
instructions = "Select ROI and press SPACE or ENTER"
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(display_frame, instructions, (10, 30), font, 1, (255, 255, 255), 2, cv2.LINE_AA)
r = cv2.selectROI(display_frame)
cv2.destroyAllWindows()
self.roi = (
int(r[0] / scale_factor),
int(r[1] / scale_factor),
int(r[2] / scale_factor),
int(r[3] / scale_factor)
)
return self.roi
3. 在主程序中传递命令行ROI参数
在 main 函数中解析ROI参数,并传递给 WatermarkDetector:
python
if __name__ == "__main__":
args = parse_args()
# 校验命令行ROI参数(必须同时提供4个参数)
roi = None
if any([args.roi_x, args.roi_y, args.roi_width, args.roi_height]):
if not all([args.roi_x, args.roi_y, args.roi_width, args.roi_height]):
print("Error: --roi-x, --roi-y, --roi-width, --roi-height must be provided together")
sys.exit(1)
roi = (args.roi_x, args.roi_y, args.roi_width, args.roi_height)
print(f"Using command line ROI: {roi}")
# 初始化水印检测器时传入ROI
watermark_detector = WatermarkDetector(roi=roi)
watermark_mask = None
# 后续逻辑不变...
lama_model, lama_config = initialize_lama(device=use_device)
for video in videos:
print(f"Processing {video}")
video_clip = VideoFileClip(video)
# 生成掩码时会自动使用命令行传入的ROI(无需手动框选)
if watermark_mask is None:
watermark_mask = watermark_detector.generate_mask(video_clip)
# 禁用预览(命令行环境无法显示窗口)
if preview_enabled:
print("Warning: Preview is disabled in command line ROI mode")
preview_enabled = False
# 后续处理逻辑不变...
4. 禁用命令行模式下的图形化操作
在命令行环境下,cv2.imshow 和 cv2.selectROI 会导致程序崩溃,需确保命令行模式下不执行这些操作:
- 当通过
--roi-*参数指定ROI时,自动禁用--preview(如上述代码中强制preview_enabled = False) - 若用户同时传入
--preview和--roi-*,打印警告并忽略预览
使用方法
通过命令行直接指定ROI坐标运行(无需图形界面):
bash
python watermark_remover.py \
--input /path/to/videos \
--output /path/to/output \
--roi-x 100 \ # 水印区域左上角x坐标
--roi-y 200 \ # 水印区域左上角y坐标
--roi-width 300 \ # 水印区域宽度
--roi-height 150 # 水印区域高度
强制将roi设为待修复区域
从代码逻辑来看,原脚本并非直接对整个选中的ROI区域进行无差别修改,而是在ROI范围内增加了一层"水印检测"逻辑------通过分析采样帧中的像素特征,动态生成"水印掩码"(只标记真正的水印区域),最终只对掩码覆盖的部分进行修复。这就是为什么即使ROI完全覆盖水印,仍可能出现修复不彻底的情况。
原项目逻辑拆解:ROI ≠ 最终修复区域
脚本的核心流程是:
用户指定ROI → 在ROI内检测水印 → 生成"水印掩码" → 只修复掩码覆盖的区域
WatermarkDetector类的generate_mask方法做了这些事:
- 采样帧分析:从视频中抽取10帧(默认),聚焦到用户指定的ROI区域;
- 水印检测 :对每帧的ROI区域做二值化处理(
cv2.threshold),区分"可能是水印"的亮/暗区域(假设水印与背景有明暗差异); - 掩码生成 :只有在至少7帧(默认min_frame_count=7) 中都被检测为"水印"的区域,才会被纳入最终的修复掩码(其他区域即使在ROI内,也不会被修复)。
具体来说:
- 脚本依赖
cv2.threshold(..., cv2.THRESH_BINARY + cv2.THRESH_OTSU)自动区分水印和背景,但如果水印与背景的对比度低(比如半透明水印、颜色接近背景),二值化可能会"漏检"部分帧中的水印; - 即使实际每一帧都有水印,但如果采样的10帧中有3帧以上未被检测到(比如因光线变化导致某几帧水印模糊),这部分水印区域会因"出现次数不足7次"被排除在掩码外,最终未被修复。
解决方案:跳过检测,强制修复整个ROI
如果确认ROI内只有水印(无其他重要内容),可以跳过检测逻辑,直接用整个ROI作为掩码。修改方法如下:
- 修改
generate_mask方法:删除动态检测逻辑,直接生成覆盖整个ROI的掩码
python
def generate_mask(self, video_clip):
if self.roi is None:
self.select_roi(video_clip)
# 直接生成覆盖整个ROI的掩码(全白,代表整个ROI都需要修复)
mask = np.zeros((video_clip.h, video_clip.w), dtype=np.uint8)
x, y, w, h = self.roi
mask[y:y+h, x:x+w] = 255 # 整个ROI区域标记为需要修复
# 保持原有的膨胀操作,让修复边缘更平滑
kernel = np.ones((self.dilation_kernel_size, self.dilation_kernel_size), np.uint8)
dilated_mask = cv2.dilate(mask, kernel, iterations=2)
return dilated_mask
- 效果:无论水印与背景的对比度如何,整个ROI区域都会被LAMA模型修复,避免因检测漏检导致的残留。
- roi确认工具 :系统自带「画图」(Paint)。无需安装,随系统自带,适合快速查看。用「画图」打开图片,鼠标移动到目标位置,状态栏左下角会显示当前坐标(格式:X: xxx Y: xxx)。
