import os import cv2 import numpy as np from PIL import Image, ImageTk import tkinter as tk from tkinter import filedialog, messagebox, ttk import threading import time class WatermarkGUI: def __init__(self, root, analyzer): self.root = root self.analyzer = analyzer self.source_folder = "" self.output_folder = "" self.current_image = None self.current_preview = None self.image_files = [] self.current_index = 0 self.processing = False self.analyzing = False # 设置中文字体 self.font_family = "SimHei" # Windows系统默认中文字体 if os.name == "posix": # macOS/Linux self.font_family = "WenQuanYi Micro Hei" self.create_widgets() def create_widgets(self): """创建GUI组件""" # 主框架 main_frame = ttk.Frame(self.root, padding="20") main_frame.pack(fill=tk.BOTH, expand=True) # 顶部信息栏 info_frame = ttk.Frame(main_frame) info_frame.pack(fill=tk.X, pady=(0, 10)) ttk.Label(info_frame, text="图片水印分析与去除工具 V9", font=(self.font_family, 16, "bold")).pack(side=tk.LEFT) # 文件夹选择区域 folder_frame = ttk.LabelFrame(main_frame, text="文件夹设置", padding="10") folder_frame.pack(fill=tk.X, pady=10) # 源文件夹 ttk.Label(folder_frame, text="源文件夹:", font=(self.font_family, 10)).grid(row=0, column=0, sticky=tk.W, pady=5) self.source_entry = ttk.Entry(folder_frame, width=60) self.source_entry.grid(row=0, column=1, padx=5, pady=5) ttk.Button(folder_frame, text="浏览...", command=self.browse_source_folder, style='Accent.TButton').grid(row=0, column=2, padx=5, pady=5) # 输出文件夹 ttk.Label(folder_frame, text="输出文件夹:", font=(self.font_family, 10)).grid(row=1, column=0, sticky=tk.W, pady=5) self.output_entry = ttk.Entry(folder_frame, width=60) self.output_entry.grid(row=1, column=1, padx=5, pady=5) ttk.Button(folder_frame, text="浏览...", command=self.browse_output_folder, style='Accent.TButton').grid(row=1, column=2, padx=5, pady=5) # 分析和处理区域 process_frame = ttk.Frame(main_frame) process_frame.pack(fill=tk.X, pady=10) # 左侧分析区域 analysis_frame = ttk.LabelFrame(process_frame, text="水印分析", padding="10") analysis_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 5)) self.analyze_button = ttk.Button(analysis_frame, text="分析当前图片", command=self.analyze_watermark, style='Accent.TButton') self.analyze_button.pack(side=tk.LEFT, padx=5, pady=5) self.analysis_status = ttk.Label(analysis_frame, text="未分析水印", font=(self.font_family, 10)) self.analysis_status.pack(side=tk.LEFT, padx=10, pady=5) # 右侧处理区域 process_button_frame = ttk.LabelFrame(process_frame, text="处理", padding="10") process_button_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=(5, 0)) ttk.Button(process_button_frame, text="去除选中水印", command=self.process_current_image, style='Accent.TButton').pack(side=tk.LEFT, padx=5, pady=5) ttk.Button(process_button_frame, text="批量处理所有图片", command=self.process_all_images, style='Accent.TButton').pack(side=tk.RIGHT, padx=5, pady=5) # 水印选择区域 selection_frame = ttk.LabelFrame(main_frame, text="选择要去除的水印", padding="10") selection_frame.pack(fill=tk.X, pady=10) # 创建水印列表 columns = ("type", "x", "y", "width", "height", "confidence", "select") self.watermark_tree = ttk.Treeview(selection_frame, columns=columns, show="headings", height=4) # 设置列标题 self.watermark_tree.heading("type", text="类型") self.watermark_tree.heading("x", text="X坐标") self.watermark_tree.heading("y", text="Y坐标") self.watermark_tree.heading("width", text="宽度") self.watermark_tree.heading("height", text="高度") self.watermark_tree.heading("confidence", text="置信度") self.watermark_tree.heading("select", text="选择") # 设置列宽 self.watermark_tree.column("type", width=80, anchor=tk.CENTER) self.watermark_tree.column("x", width=60, anchor=tk.CENTER) self.watermark_tree.column("y", width=60, anchor=tk.CENTER) self.watermark_tree.column("width", width=60, anchor=tk.CENTER) self.watermark_tree.column("height", width=60, anchor=tk.CENTER) self.watermark_tree.column("confidence", width=80, anchor=tk.CENTER) self.watermark_tree.column("select", width=60, anchor=tk.CENTER) self.watermark_tree.pack(fill=tk.X, pady=5) # 选择按钮 button_frame = ttk.Frame(selection_frame) button_frame.pack(fill=tk.X, pady=5) ttk.Button(button_frame, text="选择所有", command=self.select_all_watermarks).pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="取消选择", command=self.deselect_all_watermarks).pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="应用选择", command=self.apply_watermark_selection).pack(side=tk.RIGHT, padx=5) # 检测参数设置 params_frame = ttk.LabelFrame(main_frame, text="检测参数设置", padding="10") params_frame.pack(fill=tk.X, pady=10) # 第一行参数 row1_frame = ttk.Frame(params_frame) row1_frame.pack(fill=tk.X, pady=5) # 文字敏感度 ttk.Label(row1_frame, text="文字敏感度:").grid(row=0, column=0, sticky=tk.W, pady=5, padx=5) self.text_sensitivity = tk.IntVar(value=30) text_scale = ttk.Scale(row1_frame, from_=10, to=100, orient=tk.HORIZONTAL, variable=self.text_sensitivity, length=200, command=self.on_text_sensitivity_change) text_scale.grid(row=0, column=1, padx=5, pady=5) self.text_sensitivity_value = ttk.Label(row1_frame, text=str(self.text_sensitivity.get())) self.text_sensitivity_value.grid(row=0, column=2, padx=5, pady=5) # 印章敏感度 ttk.Label(row1_frame, text="印章敏感度:").grid(row=0, column=3, sticky=tk.W, pady=5, padx=5) self.stamp_sensitivity = tk.IntVar(value=30) stamp_scale = ttk.Scale(row1_frame, from_=10, to=100, orient=tk.HORIZONTAL, variable=self.stamp_sensitivity, length=200, command=self.on_stamp_sensitivity_change) stamp_scale.grid(row=0, column=4, padx=5, pady=5) self.stamp_sensitivity_value = ttk.Label(row1_frame, text=str(self.stamp_sensitivity.get())) self.stamp_sensitivity_value.grid(row=0, column=5, padx=5, pady=5) # 第二行参数 row2_frame = ttk.Frame(params_frame) row2_frame.pack(fill=tk.X, pady=5) # 显著性阈值 ttk.Label(row2_frame, text="显著性阈值:").grid(row=0, column=0, sticky=tk.W, pady=5, padx=5) self.saliency_threshold = tk.DoubleVar(value=self.analyzer.saliency_threshold) saliency_scale = ttk.Scale(row2_frame, from_=0.1, to=0.9, orient=tk.HORIZONTAL, variable=self.saliency_threshold, length=200, command=lambda s: self.saliency_threshold.set(round(float(s), 1))) saliency_scale.grid(row=0, column=1, padx=5, pady=5) self.saliency_value = ttk.Label(row2_frame, text=str(self.saliency_threshold.get())) self.saliency_value.grid(row=0, column=2, padx=5, pady=5) # 边缘阈值 ttk.Label(row2_frame, text="边缘阈值:").grid(row=0, column=3, sticky=tk.W, pady=5, padx=5) self.edge_threshold = tk.IntVar(value=self.analyzer.edge_threshold) edge_scale = ttk.Scale(row2_frame, from_=50, to=200, orient=tk.HORIZONTAL, variable=self.edge_threshold, length=200, command=self.on_edge_threshold_change) edge_scale.grid(row=0, column=4, padx=5, pady=5) self.edge_value = ttk.Label(row2_frame, text=str(self.edge_threshold.get())) self.edge_value.grid(row=0, column=5, padx=5, pady=5) # 第三行参数 - 检测方法选择 row3_frame = ttk.Frame(params_frame) row3_frame.pack(fill=tk.X, pady=5) self.use_color_filter = tk.BooleanVar(value=self.analyzer.use_color_filter) ttk.Checkbutton(row3_frame, text="颜色过滤", variable=self.use_color_filter, command=self.on_detection_method_change).grid(row=0, column=0, sticky=tk.W, pady=5, padx=5) self.use_mser = tk.BooleanVar(value=self.analyzer.use_mser) ttk.Checkbutton(row3_frame, text="文字检测(MSER)", variable=self.use_mser, command=self.on_detection_method_change).grid(row=0, column=1, sticky=tk.W, pady=5, padx=5) self.use_texture_analysis = tk.BooleanVar(value=self.analyzer.use_texture_analysis) ttk.Checkbutton(row3_frame, text="纹理分析", variable=self.use_texture_analysis, command=self.on_detection_method_change).grid(row=0, column=2, sticky=tk.W, pady=5, padx=5) # 修复参数设置 repair_frame = ttk.LabelFrame(main_frame, text="修复参数设置", padding="10") repair_frame.pack(fill=tk.X, pady=10) # 第一行修复参数 repair_row1_frame = ttk.Frame(repair_frame) repair_row1_frame.pack(fill=tk.X, pady=5) # 修复半径 ttk.Label(repair_row1_frame, text="修复半径:").grid(row=0, column=0, sticky=tk.W, pady=5, padx=5) self.repair_radius = tk.IntVar(value=self.analyzer.inpaint_radius) repair_scale = ttk.Scale(repair_row1_frame, from_=1, to=15, orient=tk.HORIZONTAL, variable=self.repair_radius, length=200, command=self.on_repair_radius_change) repair_scale.grid(row=0, column=1, padx=5, pady=5) self.repair_radius_value = ttk.Label(repair_row1_frame, text=str(self.repair_radius.get())) self.repair_radius_value.grid(row=0, column=2, padx=5, pady=5) # 修复算法 ttk.Label(repair_row1_frame, text="修复算法:").grid(row=0, column=3, sticky=tk.W, pady=5, padx=5) self.repair_algorithm = tk.StringVar(value="TELEA") algorithm_combo = ttk.Combobox(repair_row1_frame, textvariable=self.repair_algorithm, values=["TELEA", "NS"], state="readonly", width=10) algorithm_combo.grid(row=0, column=4, padx=5, pady=5) algorithm_combo.bind("<>", self.on_repair_algorithm_change) # 图像预览区域 preview_frame = ttk.LabelFrame(main_frame, text="图像预览", padding="10") preview_frame.pack(fill=tk.BOTH, expand=True, pady=10) # 创建画布 self.canvas_frame = ttk.Frame(preview_frame) self.canvas_frame.pack(fill=tk.BOTH, expand=True) self.canvas = tk.Canvas(self.canvas_frame, bg="white") self.canvas.pack(fill=tk.BOTH, expand=True) # 添加滚动条 self.h_scrollbar = ttk.Scrollbar(self.canvas_frame, orient=tk.HORIZONTAL, command=self.canvas.xview) self.v_scrollbar = ttk.Scrollbar(self.canvas_frame, orient=tk.VERTICAL, command=self.canvas.yview) self.canvas.configure(xscrollcommand=self.h_scrollbar.set, yscrollcommand=self.v_scrollbar.set) self.h_scrollbar.pack(side=tk.BOTTOM, fill=tk.X) self.v_scrollbar.pack(side=tk.RIGHT, fill=tk.Y) # 图像导航 nav_frame = ttk.Frame(main_frame) nav_frame.pack(fill=tk.X, pady=10) ttk.Button(nav_frame, text="上一张", command=self.prev_image).pack(side=tk.LEFT, padx=5) self.image_info = ttk.Label(nav_frame, text="未加载图片", font=(self.font_family, 10)) self.image_info.pack(side=tk.LEFT, padx=20) ttk.Button(nav_frame, text="下一张", command=self.next_image).pack(side=tk.RIGHT, padx=5) # 状态栏 self.status_bar = ttk.Label(self.root, text="就绪", relief=tk.SUNKEN, anchor=tk.W) self.status_bar.pack(side=tk.BOTTOM, fill=tk.X) # 绑定鼠标滚轮事件 self.canvas.bind("", self.on_mousewheel) # Windows self.canvas.bind("", self.on_mousewheel) # Linux self.canvas.bind("", self.on_mousewheel) # Linux # 绑定画布大小变化事件 self.canvas.bind("", self.on_canvas_configure) def browse_source_folder(self): """浏览并选择源文件夹""" folder = filedialog.askdirectory(title="选择源文件夹") if folder: self.source_folder = folder self.source_entry.delete(0, tk.END) self.source_entry.insert(0, folder) self.load_images() def browse_output_folder(self): """浏览并选择输出文件夹""" folder = filedialog.askdirectory(title="选择输出文件夹") if folder: self.output_folder = folder self.output_entry.delete(0, tk.END) self.output_entry.insert(0, folder) def load_images(self): """加载文件夹中的所有图像""" if not self.source_folder: return self.image_files = [] valid_extensions = ['.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.gif'] for file in os.listdir(self.source_folder): if any(file.lower().endswith(ext) for ext in valid_extensions): self.image_files.append(os.path.join(self.source_folder, file)) if self.image_files: self.current_index = 0 self.load_current_image() self.update_image_info() else: messagebox.showinfo("提示", "所选文件夹中没有找到图像文件") def load_current_image(self): """加载当前选中的图像""" if not self.image_files: return try: self.current_image = cv2.imread(self.image_files[self.current_index]) if self.current_image is None: messagebox.showerror("错误", f"无法加载图像: {self.image_files[self.current_index]}") return # 重置水印分析结果 self.analyzer.watermark_regions = [] self.update_watermark_tree() self.analysis_status.config(text="未分析水印") # 显示图像 self.display_image() except Exception as e: messagebox.showerror("错误", f"加载图像时出错: {str(e)}") def display_image(self): """在画布上显示当前图像""" if self.current_image is None: return # 复制图像用于显示 display_img = self.current_image.copy() # 如果有选中的水印区域,在预览图上标记 for region in self.analyzer.selected_regions: x, y, w, h = region['position'] color = (0, 255, 0) # 绿色边框 cv2.rectangle(display_img, (x, y), (x + w, y + h), color, 2) # 转换为PIL格式 rgb_img = cv2.cvtColor(display_img, cv2.COLOR_BGR2RGB) pil_img = Image.fromarray(rgb_img) # 调整图像大小以适应画布 canvas_width = self.canvas.winfo_width() - 20 canvas_height = self.canvas.winfo_height() - 20 if canvas_width <= 0 or canvas_height <= 0: return img_width, img_height = pil_img.size # 计算缩放比例 scale = min(canvas_width / img_width, canvas_height / img_height) if scale < 1: pil_img = pil_img.resize((int(img_width * scale), int(img_height * scale)), Image.LANCZOS) # 转换为Tkinter可用的格式 self.current_preview = ImageTk.PhotoImage(image=pil_img) # 在画布上显示图像 self.canvas.delete("all") self.canvas.create_image(10, 10, anchor=tk.NW, image=self.current_preview) # 更新画布滚动区域 self.canvas.configure(scrollregion=self.canvas.bbox("all")) def update_image_info(self): """更新图像信息标签""" if not self.image_files: self.image_info.config(text="未加载图片") return self.image_info.config( text=f"图片 {self.current_index + 1}/{len(self.image_files)}: {os.path.basename(self.image_files[self.current_index])}") def prev_image(self): """显示上一张图片""" if not self.image_files: return self.current_index = (self.current_index - 1) % len(self.image_files) self.load_current_image() self.update_image_info() def next_image(self): """显示下一张图片""" if not self.image_files: return self.current_index = (self.current_index + 1) % len(self.image_files) self.load_current_image() self.update_image_info() def analyze_watermark(self): """分析当前图片中的水印""" if self.current_image is None: messagebox.showinfo("提示", "请先加载图片") return if self.analyzing: messagebox.showinfo("提示", "正在进行水印分析,请稍候...") return # 禁用分析按钮 self.analyze_button.config(state=tk.DISABLED) self.analysis_status.config(text="分析中...") self.analyzing = True # 更新分析参数 self.update_analysis_parameters() # 在单独的线程中执行水印分析 analysis_thread = threading.Thread(target=self._analyze_watermark_thread) analysis_thread.daemon = True analysis_thread.start() def _analyze_watermark_thread(self): """在后台线程中执行水印分析""" try: # 执行水印分析 start_time = time.time() self.analyzer.analyze(self.current_image) analysis_time = time.time() - start_time # 更新UI self.root.after(0, self._update_after_analysis, analysis_time) except Exception as e: self.root.after(0, messagebox.showerror, "错误", f"分析水印时出错: {str(e)}") self.root.after(0, self._reset_analysis_state) def _update_after_analysis(self, analysis_time): """分析完成后更新UI""" # 更新水印列表 self.update_watermark_tree() # 更新状态 if self.analyzer.watermark_regions: self.analysis_status.config( text=f"已检测到 {len(self.analyzer.watermark_regions)} 个水印区域 (耗时: {analysis_time:.2f}秒)") else: self.analysis_status.config(text="未检测到水印 (耗时: {analysis_time:.2f}秒)") # 重新显示图像(包含水印标记) self.display_image() # 重置分析状态 self._reset_analysis_state() def _reset_analysis_state(self): """重置分析状态""" self.analyze_button.config(state=tk.NORMAL) self.analyzing = False def update_watermark_tree(self): """更新水印树状视图""" # 清空现有项 for item in self.watermark_tree.get_children(): self.watermark_tree.delete(item) # 添加新项 for i, region in enumerate(self.analyzer.watermark_regions): x, y, w, h = region['position'] confidence = region['confidence'] r_type = region['type'] # 判断是否选中 is_selected = region in self.analyzer.selected_regions select_text = "✔" if is_selected else "" self.watermark_tree.insert("", tk.END, values=( r_type, x, y, w, h, f"{confidence:.2f}", select_text )) def select_all_watermarks(self): """选择所有水印区域""" self.analyzer.selected_regions = self.analyzer.watermark_regions.copy() self.update_watermark_tree() self.display_image() def deselect_all_watermarks(self): """取消选择所有水印区域""" self.analyzer.selected_regions = [] self.update_watermark_tree() self.display_image() def apply_watermark_selection(self): """应用水印选择(基于用户在表格中的选择)""" selected_regions = [] for item_id in self.watermark_tree.selection(): item = self.watermark_tree.item(item_id) values = item['values'] if not values: continue # 查找对应的区域 for region in self.analyzer.watermark_regions: x, y, w, h = region['position'] if (int(values[1]) == x and int(values[2]) == y and int(values[3]) == w and int(values[4]) == h): selected_regions.append(region) break self.analyzer.selected_regions = selected_regions self.update_watermark_tree() self.display_image() def process_current_image(self): """处理当前图片,去除选中的水印""" if self.current_image is None: messagebox.showinfo("提示", "请先加载图片") return if not self.analyzer.selected_regions: messagebox.showinfo("提示", "请先选择要去除的水印区域") return if self.processing: messagebox.showinfo("提示", "正在处理图片,请稍候...") return # 更新修复参数 self.update_repair_parameters() # 禁用处理按钮 self.processing = True self.status_bar.config(text="处理中...") # 在单独的线程中执行图片处理 process_thread = threading.Thread(target=self._process_current_image_thread) process_thread.daemon = True process_thread.start() def _process_current_image_thread(self): """在后台线程中处理当前图片""" try: # 处理图片 result = self.analyzer.remove_watermarks(self.current_image) # 保存结果 if self.output_folder: output_path = os.path.join(self.output_folder, f"processed_{os.path.basename(self.image_files[self.current_index])}") cv2.imwrite(output_path, result) self.status_bar.config(text=f"已保存处理后的图片: {output_path}") else: self.status_bar.config(text="处理完成,但未指定输出文件夹,结果未保存") # 更新当前图片为处理后的结果 self.current_image = result # 更新UI self.root.after(0, self.display_image) self.root.after(0, self._reset_processing_state) except Exception as e: self.root.after(0, messagebox.showerror, "错误", f"处理图片时出错: {str(e)}") self.root.after(0, self._reset_processing_state) def _reset_processing_state(self): """重置处理状态""" self.processing = False self.status_bar.config(text="就绪") def process_all_images(self): """批量处理所有图片""" if not self.image_files: messagebox.showinfo("提示", "请先加载图片") return if not self.output_folder: messagebox.showinfo("提示", "请先选择输出文件夹") return if self.processing: messagebox.showinfo("提示", "正在处理图片,请稍候...") return # 更新修复参数 self.update_repair_parameters() # 确认对话框 result = messagebox.askyesno("确认", f"确定要批量处理所有 {len(self.image_files)} 张图片吗?") if not result: return total = len(self.image_files) processed = 0 errors = 0 for i, img_path in enumerate(self.image_files): # 更新状态栏 self.root.after(0, lambda i=i: self.status_bar.config(text=f"处理中 ({i + 1}/{total})...")) try: # 读取图片 img = cv2.imread(img_path) if img is None: errors += 1 continue # 分析水印 self.analyzer.analyze(img) # 处理图片 if self.analyzer.watermark_regions: result = self.analyzer.remove_watermarks(img) # 保存结果 output_path = os.path.join(self.output_folder, f"processed_{os.path.basename(img_path)}") cv2.imwrite(output_path, result) processed += 1 except Exception as e: print(f"处理图片 {img_path} 时出错: {str(e)}") errors += 1 # 更新UI self.root.after(0, lambda: messagebox.showinfo("完成", f"批量处理完成!\n成功: {processed}\n失败: {errors}")) self.root.after(0, lambda: self.status_bar.config(text=f"批量处理完成: 成功 {processed}, 失败 {errors}")) self.root.after(0, self._reset_processing_state) except Exception as e: self.root.after(0, messagebox.showerror, "错误", f"批量处理时出错: {str(e)}") self.root.after(0, self._reset_processing_state) def update_analysis_parameters(self): """更新分析参数""" self.analyzer.text_threshold = self.text_sensitivity.get() self.analyzer.stamp_threshold = self.stamp_sensitivity.get() self.analyzer.saliency_threshold = self.saliency_threshold.get() self.analyzer.edge_threshold = self.edge_threshold.get() self.analyzer.use_color_filter = self.use_color_filter.get() self.analyzer.use_mser = self.use_mser.get() self.analyzer.use_texture_analysis = self.use_texture_analysis.get() def update_repair_parameters(self): """更新修复参数""" self.analyzer.inpaint_radius = self.repair_radius.get() if self.repair_algorithm.get() == "TELEA": self.analyzer.inpaint_algorithm = cv2.INPAINT_TELEA else: self.analyzer.inpaint_algorithm = cv2.INPAINT_NS def on_text_sensitivity_change(self, event): """文字敏感度滑块变化事件""" value = self.text_sensitivity.get() self.text_sensitivity_value.config(text=str(value)) def on_stamp_sensitivity_change(self, event): """印章敏感度滑块变化事件""" value = self.stamp_sensitivity.get() self.stamp_sensitivity_value.config(text=str(value)) def on_edge_threshold_change(self, event): """边缘阈值滑块变化事件""" value = self.edge_threshold.get() self.edge_value.config(text=str(value)) def on_repair_radius_change(self, event): """修复半径滑块变化事件""" value = self.repair_radius.get() self.repair_radius_value.config(text=str(value)) def on_repair_algorithm_change(self, event): """修复算法选择变化事件""" pass def on_detection_method_change(self): """检测方法选择变化事件""" pass def on_mousewheel(self, event): """鼠标滚轮事件处理""" if event.num == 4 or event.delta > 0: # 向上滚动 self.canvas.yview_scroll(-1, "units") elif event.num == 5 or event.delta < 0: # 向下滚动 self.canvas.yview_scroll(1, "units") def on_canvas_configure(self, event): """画布大小变化事件处理""" self.display_image() def run_gui(): """运行GUI应用""" # 尝试导入Analyzer类 try: from analyzer import WatermarkAnalyzer analyzer = WatermarkAnalyzer() except ImportError: # 如果无法导入,创建一个模拟类用于演示 class WatermarkAnalyzer: def __init__(self): self.watermark_regions = [] self.selected_regions = [] self.inpaint_radius = 5 self.inpaint_algorithm = cv2.INPAINT_TELEA self.text_threshold = 180 self.stamp_threshold = 180 self.saliency_threshold = 0.5 self.edge_threshold = 100 self.use_color_filter = True self.use_mser = True self.use_texture_analysis = True def analyze(self, image): # 模拟水印分析 height, width = image.shape[:2] self.watermark_regions = [ { 'type': 'text', 'position': (width // 2 - 50, 10, 100, 30), 'confidence': 0.8, 'contour': None }, { 'type': 'stamp', 'position': (width - 100, height - 100, 80, 80), 'confidence': 0.9, 'contour': None } ] self.selected_regions = self.watermark_regions.copy() def remove_watermarks(self, image): # 模拟水印去除 result = image.copy() for region in self.selected_regions: x, y, w, h = region['position'] # 创建掩码 mask = np.zeros((image.shape[0], image.shape[1]), dtype=np.uint8) mask[y:y + h, x:x + w] = 255 # 使用修复算法去除水印 result = cv2.inpaint(result, mask, self.inpaint_radius, self.inpaint_algorithm) return result analyzer = WatermarkAnalyzer() # 创建主窗口 root = tk.Tk() root.title("图片水印分析与去除工具 V9") root.geometry("1000x800") # 设置样式 style = ttk.Style() style.configure('Accent.TButton', font=(None, 10, 'bold')) # 创建GUI app = WatermarkGUI(root, analyzer) # 启动主循环 root.mainloop() if __name__ == "__main__": run_gui()