diff --git a/backend/interface/ch.ini b/backend/interface/ch.ini index d2c3e2c..0b90e6c 100644 --- a/backend/interface/ch.ini +++ b/backend/interface/ch.ini @@ -59,7 +59,7 @@ OpenVideoFirst = 请先打开视频 VideoPreview = 视频预览 InterfaceLanguage = 界面语言 InpaintMode = 处理模型 -SelectSubtitleArea = 请在视频预览中框选处理区域 +SelectSubtitleArea = 请在视频预览中框选处理区域: {} InpaintModeDesc = STTN智能擦除, 对于真人视频效果较好,速度快, 智能擦除(最低4GB显存) STTN字幕检测 带字幕检测版, 无智能擦除(最低4GB显存) LAMA: 对于动画类视频效果好,速度一般(显存要求较低) diff --git a/backend/interface/chinese_cht.ini b/backend/interface/chinese_cht.ini index 51663d7..45aa8a6 100644 --- a/backend/interface/chinese_cht.ini +++ b/backend/interface/chinese_cht.ini @@ -59,7 +59,7 @@ OpenVideoFirst = 請先開啟影片 VideoPreview = 影片預覽 InterfaceLanguage = 介面語言 InpaintMode = 處理模型 -SelectSubtitleArea = 請在影片預覽中框選處理區域 +SelectSubtitleArea = 請在影片預覽中框選處理區域: {} InpaintModeDesc = STTN智能擦除,對於真人視頻效果較好,速度快,智能擦除(最低4GB顯存) STTN字幕檢測 帶字幕檢測版,無智能擦除(最低4GB顯存) LAMA:對於動畫類視頻效果好,速度一般(顯存要求較低) diff --git a/backend/interface/en.ini b/backend/interface/en.ini index 8620126..80c8542 100644 --- a/backend/interface/en.ini +++ b/backend/interface/en.ini @@ -59,7 +59,7 @@ OpenVideoFirst = Please open a video first VideoPreview = Video Preview InterfaceLanguage = Interface Language InpaintMode = Processing Model -SelectSubtitleArea = Select processing area in video preview +SelectSubtitleArea = Select processing area in video preview: {} InpaintModeDesc = STTN Smart Inpainting: Best for real-person videos, fast speed, smart inpainting (minimum 4GB VRAM) STTN Subtitle Detection: With subtitle detection, no smart inpainting (minimum 4GB VRAM) LAMA: Good for animation videos, moderate speed (low VRAM requirement) diff --git a/backend/interface/es.ini b/backend/interface/es.ini index 6953b00..50428ee 100644 --- a/backend/interface/es.ini +++ b/backend/interface/es.ini @@ -59,7 +59,7 @@ OpenVideoFirst = Abre un video primero VideoPreview = Vista previa InterfaceLanguage = Idioma de interfaz InpaintMode = Modelo de procesamiento -SelectSubtitleArea = Selecciona área en vista previa +SelectSubtitleArea = Selecciona área en vista previa: {} InpaintModeDesc = STTN Borrado inteligente: Mejor para videos de personas reales, velocidad rápida, borrado inteligente (mínimo 4GB de VRAM) STTN Detección de subtítulos: Con detección de subtítulos, sin borrado inteligente (mínimo 4GB de VRAM) LAMA: Bueno para videos animados, velocidad media (bajo requerimiento de VRAM) diff --git a/backend/interface/japan.ini b/backend/interface/japan.ini index ef39399..9ee98d9 100644 --- a/backend/interface/japan.ini +++ b/backend/interface/japan.ini @@ -59,7 +59,7 @@ OpenVideoFirst = 動画を先に開いてください VideoPreview = 動画プレビュー InterfaceLanguage = インターフェース言語 InpaintMode = 処理モデル -SelectSubtitleArea = プレビューで処理領域を選択 +SelectSubtitleArea = プレビューで処理領域を選択: {} InpaintModeDesc = STTNスマート消去:実写動画に最適、高速、スマート消去(最低4GB VRAM) STTN字幕検出:字幕検出付き、スマート消去なし(最低4GB VRAM) LAMA:アニメ動画に最適、速度は普通(VRAM要件低め) diff --git a/backend/interface/ko.ini b/backend/interface/ko.ini index eb1b8c2..da6a674 100644 --- a/backend/interface/ko.ini +++ b/backend/interface/ko.ini @@ -59,7 +59,7 @@ OpenVideoFirst = 동영상을 먼저 열어주세요 VideoPreview = 동영상 미리보기 InterfaceLanguage = 인터페이스 언어 InpaintMode = 처리 모델 -SelectSubtitleArea = 미리보기에서 처리 영역 선택 +SelectSubtitleArea = 미리보기에서 처리 영역 선택: {} InpaintModeDesc = STTN 스마트 지우기: 실제 인물 영상에 적합, 빠른 속도, 스마트 지우기(최소 4GB VRAM) STTN 자막 감지: 자막 감지 버전, 스마트 지우기 없음(최소 4GB VRAM) LAMA: 애니메이션 영상에 적합, 보통 속도(VRAM 요구량 낮음) diff --git a/backend/interface/vi.ini b/backend/interface/vi.ini index 5caef78..b8d56aa 100644 --- a/backend/interface/vi.ini +++ b/backend/interface/vi.ini @@ -59,7 +59,7 @@ OpenVideoFirst = Vui lòng mở video trước VideoPreview = Xem trước video InterfaceLanguage = Ngôn ngữ giao diện InpaintMode = Chế độ xử lý -SelectSubtitleArea = Chọn vùng xử lý trong preview +SelectSubtitleArea = Chọn vùng xử lý trong preview: {} InpaintModeDesc = STTN Xóa thông minh: Phù hợp cho video người thật, tốc độ nhanh, xóa thông minh (tối thiểu 4GB VRAM) STTN Phát hiện phụ đề: Có phát hiện phụ đề, không xóa thông minh (tối thiểu 4GB VRAM) LAMA: Phù hợp cho video hoạt hình, tốc độ trung bình (yêu cầu VRAM thấp) diff --git a/backend/main.py b/backend/main.py index c81b098..e4c0cf8 100644 --- a/backend/main.py +++ b/backend/main.py @@ -24,20 +24,18 @@ from backend.tools.inpaint_tools import create_mask, batch_generator, expand_fra from backend.tools.model_config import ModelConfig from backend.tools.ffmpeg_cli import FFmpegCLI from backend.tools.subtitle_detect import SubtitleDetect -import platform import tempfile import multiprocessing -from shapely.geometry import Polygon import time from tqdm import tqdm import numpy as np class SubtitleRemover: - def __init__(self, vd_path, sub_areas=[], gui_mode=False): + def __init__(self, vd_path, gui_mode=False): # 线程锁 self.lock = threading.RLock() # 用户指定的字幕区域位置 - self.sub_areas = sub_areas + self.sub_areas = [] # 是否为gui运行,gui运行需要显示预览 self.gui_mode = gui_mode self.hardware_accelerator = HardwareAccelerator.instance() @@ -67,9 +65,6 @@ class SubtitleRemover: self.video_out_path = os.path.abspath(os.path.join(os.path.dirname(self.video_path), f'{self.vd_name}_no_sub.mp4')) self.propainter_inpaint = None self.ext = os.path.splitext(vd_path)[-1] - if len(self.sub_areas) == 0: - self.append_output(tr['Main']['FullScreenProcessingNote']) - self.sub_areas.append((0, self.frame_height, 0, self.frame_width)) if self.is_picture: pic_dir = os.path.join(os.path.dirname(self.video_path), 'no_sub') if not os.path.exists(pic_dir): @@ -331,6 +326,10 @@ class SubtitleRemover: def run(self): # 记录开始时间 start_time = time.time() + if len(self.sub_areas) == 0: + self.append_output(tr['Main']['FullScreenProcessingNote']) + self.sub_areas.append((0, self.frame_height, 0, self.frame_width)) + self.append_output(tr['Main']['SubtitleArea'].format(self.sub_areas)) self.append_output(tr['Main']['ABSection'].format(str(self.ab_sections).replace("range", "") if self.ab_sections is not None and len(self.ab_sections) > 0 else tr['Main']['ABSectionAll'])) # 如果使用GPU加速,则打印GPU加速提示 if self.hardware_accelerator.has_accelerator(): @@ -466,11 +465,11 @@ if __name__ == '__main__': config.set(config.interface, 'en') TRANSLATION_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'interface', f"{config.interface.value}.ini") tr.read(TRANSLATION_FILE, encoding='utf-8') - print('Subtitle Area:', 'fullscreen' if len(args.subtitle_area_coords) <= 0 else args.subtitle_area_coords) - sr = SubtitleRemover(args.input, sub_areas=args.subtitle_area_coords) + sr = SubtitleRemover(args.input) if not is_video_or_image(args.input): sr.append_output(f'Error: {video_path} is not supported not corrupted.') exit(-1) + sr.sub_areas = args.subtitle_area_coords sr.video_out_path = args.output config.inpaintMode.value = args.inpaint_mode sr.run() diff --git a/ui/component/task_list_component.py b/ui/component/task_list_component.py index e092aa2..b544854 100644 --- a/ui/component/task_list_component.py +++ b/ui/component/task_list_component.py @@ -20,6 +20,7 @@ class TaskStatus(Enum): @unique class TaskOptions(Enum): AB_SECTIONS = "ab_sections" + SUB_AREAS = "sub_areas" @dataclass class Task: diff --git a/ui/component/video_display_component.py b/ui/component/video_display_component.py index 5cd876c..1656c70 100644 --- a/ui/component/video_display_component.py +++ b/ui/component/video_display_component.py @@ -11,7 +11,7 @@ class VideoDisplayComponent(QWidget): """视频显示组件,包含视频预览和选择框功能""" # 定义信号 - selection_changed = Signal(list) # 选择框变化信号 + selections_changed = Signal(list) # 选择框变化信号 ab_sections_changed = Signal(list) # AB分区变化信号 def __init__(self, parent=None): @@ -22,7 +22,6 @@ class VideoDisplayComponent(QWidget): self.is_drawing = False self.selection_rect = QRect() # 当前正在绘制或调整的选区 self.selection_rects = [] # 存储多个选区 - self.selection_ratios = [] # 存储多个选区的比例 self.active_selection_index = -1 # 当前活动选区的索引 self.drag_start_pos = None self.resize_edge = None @@ -254,21 +253,6 @@ class VideoDisplayComponent(QWidget): self.current_pixmap = rounded_pix.copy() self.video_display.setPixmap(rounded_pix) - - # 如果有保存的选择框比例,根据新视频尺寸重新计算选择框 - if draw_selection and self.selection_ratios and self.scaled_width and self.scaled_height: - self.selection_rects = [] - for ratio in self.selection_ratios: - x_ratio, y_ratio, w_ratio, h_ratio = ratio - - # 计算新的选择框坐标和大小 - x = int(x_ratio * self.scaled_width) + self.border_left - y = int(y_ratio * self.scaled_height) + self.border_top - w = int(w_ratio * self.scaled_width) - h = int(h_ratio * self.scaled_height) - - # 创建新的选择框 - self.selection_rects.append(QRect(x, y, w, h)) # 更新视频显示 self.update_preview_with_rect(draw_selection=draw_selection) @@ -376,10 +360,9 @@ class VideoDisplayComponent(QWidget): # 检测双击,重置所有选区 if event.type() == QtCore.QEvent.MouseButtonDblClick: self.selection_rects = [] - self.selection_ratios = [] self.active_selection_index = -1 self.update_preview_with_rect() - self.selection_changed.emit(self.selection_rects) + self.selections_changed.emit(self.selection_rects) return # 如果按下Ctrl键,开始绘制新选区 @@ -572,23 +555,17 @@ class VideoDisplayComponent(QWidget): self.selection_rects.append(self.selection_rect) self.active_selection_index = len(self.selection_rects) - 1 - # 保存选择框的相对位置和大小 - self.save_selections_to_configs() - # 发送选择框变化信号 - self.selection_changed.emit(self.selection_rects) + self.selections_changed.emit(self.selection_rects) self.is_drawing = False self.selection_rect = QRect() elif self.resize_edge and self.active_selection_index >= 0: # 标准化选择框 self.selection_rects[self.active_selection_index] = self.selection_rects[self.active_selection_index].normalized() - - # 保存选择框的相对位置和大小 - self.save_selections_to_configs() - + # 发送选择框变化信号 - self.selection_changed.emit(self.selection_rects) + self.selections_changed.emit(self.selection_rects) self.resize_edge = None @@ -678,14 +655,16 @@ class VideoDisplayComponent(QWidget): """获取选择框坐标""" return self.selection_rect - def set_selection_rect(self, rect): + def set_selection_rects(self, rects): """设置选择框""" - self.selection_rect = rect - self.save_selections_to_config() + self.selection_rects = rects + self.selection_rect = rects[-1] if rects else QRect() + self.active_selection_index = len(rects) - 1 self.update_preview_with_rect() def load_selections_from_config(self): """从配置中加载选择框的相对位置和大小""" + print("Loading selections from config...") # 检查是否有有效的视频尺寸 if not self.scaled_width or not self.scaled_height: return False @@ -699,7 +678,6 @@ class VideoDisplayComponent(QWidget): # 清空现有选区 self.selection_rects = [] - self.selection_ratios = [] # 解析所有选区 for area_str in areas_str.split(';'): @@ -717,9 +695,6 @@ class VideoDisplayComponent(QWidget): if w_ratio <= 0.01 or h_ratio <= 0.005: continue - # 保存选择框比例 - self.selection_ratios.append((x_ratio, y_ratio, w_ratio, h_ratio)) - # 计算实际像素坐标 x = int(x_ratio * self.scaled_width) + self.border_left y = int(y_ratio * self.scaled_height) + self.border_top @@ -736,41 +711,17 @@ class VideoDisplayComponent(QWidget): self.active_selection_index = len(self.selection_rects) - 1 else: self.active_selection_index = -1 - + self.selections_changed.emit(self.selection_rects) + # 更新预览 self.update_preview_with_rect() return len(self.selection_rects) > 0 - - def save_selections_to_config(self): - """保存所有选择框的相对位置和大小""" - if not self.scaled_width or not self.scaled_height: - return - - self.selection_ratios = [] - areas_str_parts = [] - - for rect in self.selection_rects: - # 计算相对于实际视频的位置和大小比例 - x_ratio = (rect.x() - self.border_left) / self.scaled_width if self.scaled_width > 0 else 0 - y_ratio = (rect.y() - self.border_top) / self.scaled_height if self.scaled_height > 0 else 0 - w_ratio = rect.width() / self.scaled_width if self.scaled_width > 0 else 0 - h_ratio = rect.height() / self.scaled_height if self.scaled_height > 0 else 0 - - # 添加到比例列表 - self.selection_ratios.append((x_ratio, y_ratio, w_ratio, h_ratio)) - - # 添加到字符串部分 - areas_str_parts.append(f"{x_ratio},{y_ratio},{w_ratio},{h_ratio}") - - # 更新配置 - config.subtitleSelectionAreas.value = ";".join(areas_str_parts) - qconfig.save() - def get_original_coordinates(self): + def preview_coordinates_to_video_coordinates(self, preview_selection_rects): """获取选择框在原始视频中的坐标""" selection_rects = [] - for rect in self.selection_rects: + for rect in preview_selection_rects: if not rect.isValid() or not self.scaled_width or not self.scaled_height: continue @@ -806,12 +757,11 @@ class VideoDisplayComponent(QWidget): self.video_display.setMouseTracking(enabled) self.video_display.setCursor(Qt.ArrowCursor) - def save_selections_to_configs(self): + def save_selections_to_config(self): """保存所有选择框的相对位置和大小""" if not self.scaled_width or not self.scaled_height: return - self.selection_ratios = [] areas_str_parts = [] for rect in self.selection_rects: @@ -821,9 +771,6 @@ class VideoDisplayComponent(QWidget): w_ratio = round(rect.width() / self.scaled_width if self.scaled_width > 0 else 0, 4) h_ratio = round(rect.height() / self.scaled_height if self.scaled_height > 0 else 0, 4) - # 添加到比例列表 - self.selection_ratios.append((x_ratio, y_ratio, w_ratio, h_ratio)) - # 添加到字符串部分 areas_str_parts.append(f"{x_ratio},{y_ratio},{w_ratio},{h_ratio}") @@ -840,32 +787,27 @@ class VideoDisplayComponent(QWidget): def clear_selections(self): """清除所有选区""" self.selection_rects = [] - self.selection_ratios = [] self.active_selection_index = -1 self.update_preview_with_rect() - self.selection_changed.emit(self.selection_rects) + self.selections_changed.emit(self.selection_rects) def __handle_delete_selection(self): """处理删除当前选区的逻辑""" if self.active_selection_index >= 0 and self.selection_rects: # 删除当前活跃选区 self.selection_rects.pop(self.active_selection_index) - if self.selection_ratios: - self.selection_ratios.pop(self.active_selection_index) # 如果还有其他选区,将最后一个选区设为活跃选区 if self.selection_rects: self.active_selection_index = len(self.selection_rects) - 1 else: self.active_selection_index = -1 - - self.save_selections_to_configs() # 更新显示 self.update_preview_with_rect() # 发送选区变化信号 - self.selection_changed.emit(self.selection_rects) + self.selections_changed.emit(self.selection_rects) return True def __handle_mark_for_ab_start(self): diff --git a/ui/home_interface.py b/ui/home_interface.py index 4837b38..cb28dd8 100644 --- a/ui/home_interface.py +++ b/ui/home_interface.py @@ -1,22 +1,19 @@ import os import cv2 import threading -import atexit import multiprocessing import time import traceback from pathlib import Path -from multiprocessing import managers -from PySide6.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout, QLabel, QPushButton, QFileDialog -from PySide6.QtCore import Qt, Slot, QTimer, QRect, QRectF, Signal -from PySide6 import QtCore, QtWidgets, QtGui -from qfluentwidgets import (qconfig, PushButton, CardWidget, SubtitleLabel, PlainTextEdit, - FluentIcon, HollowHandleStyle) +from PySide6.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout +from PySide6.QtCore import Slot, QRect, Signal +from PySide6 import QtWidgets +from qfluentwidgets import (PushButton, CardWidget, PlainTextEdit, FluentIcon) from ui.setting_interface import SettingInterface from ui.component.video_display_component import VideoDisplayComponent from ui.component.task_list_component import TaskListComponent, TaskStatus, TaskOptions from ui.icon.my_fluent_icon import MyFluentIcon -from backend.config import config, tr +from backend.config import tr from backend.tools.subtitle_remover_remote_call import SubtitleRemoverRemoteCall from backend.tools.process_manager import ProcessManager from backend.tools.common_tools import get_readable_path, is_image_file, read_image @@ -71,6 +68,7 @@ class HomeInterface(QWidget): # 创建视频显示组件 self.video_display_component = VideoDisplayComponent(self) self.video_display_component.ab_sections_changed.connect(self.ab_sections_changed) + self.video_display_component.selections_changed.connect(self.selections_changed) left_layout.addWidget(self.video_display_component) # 获取视频显示和滑块的引用 @@ -170,6 +168,12 @@ class HomeInterface(QWidget): return self.task_list_component.update_task_option(get_current_task_index, TaskOptions.AB_SECTIONS, ab_sections) + def selections_changed(self, selections): + get_current_task_index = self.task_list_component.get_current_task_index() + if get_current_task_index == -1: + return + self.task_list_component.update_task_option(get_current_task_index, TaskOptions.SUB_AREAS, selections) + def on_task_selected(self, index, file_path): """处理任务被选中事件 @@ -181,6 +185,11 @@ class HomeInterface(QWidget): self.load_video(file_path) ab_sections = self.task_list_component.get_task_option(index, TaskOptions.AB_SECTIONS, []) self.video_display_component.set_ab_sections(ab_sections) + selections = self.task_list_component.get_task_option(index, TaskOptions.SUB_AREAS, []) + if len(selections) <= 0: + self.video_display_component.load_selections_from_config() + else: + self.video_display_component.set_selection_rects(selections) def on_task_deleted(self, index): """处理任务被删除事件 @@ -309,6 +318,15 @@ class HomeInterface(QWidget): self.task_list_component.update_task_status(self.current_processing_task_index, TaskStatus.FAILED) continue + # 获取字幕区域坐标 + subtitle_areas = self.task_list_component.get_task_option(self.current_processing_task_index, TaskOptions.SUB_AREAS, []) + if not subtitle_areas or len(subtitle_areas) <= 0: + self.append_output(tr['SubtitleExtractorGUI']['SelectSubtitleArea'].format(task.path)) + self.task_list_component.update_task_status(self.current_processing_task_index, TaskStatus.FAILED) + continue + + self.video_display_component.save_selections_to_config() + # 更新任务状态为运行中 self.task_list_component.update_task_progress(self.current_processing_task_index, 1) @@ -319,15 +337,14 @@ class HomeInterface(QWidget): self.video_cap.release() self.video_cap = None - # 获取字幕区域坐标(直接从视频显示组件获取) - subtitle_areas = self.video_display_component.get_original_coordinates() - if not subtitle_areas: - self.append_output(tr['SubtitleExtractorGUI']['SelectSubtitleArea']) - return - self.append_output(f"{tr['SubtitleExtractorGUI']['SubtitleArea']}: {subtitle_areas}") - self.task_list_component.update_task_status(self.current_processing_task_index, TaskStatus.PROCESSING) - process = self.run_subtitle_remover_process(task.path, task.output_path, subtitle_areas, task.options) + options = {} + for key in task.options: + value = task.options[key] + if key == TaskOptions.SUB_AREAS.value: + value = self.video_display_component.preview_coordinates_to_video_coordinates(value) + options[key] = value + process = self.run_subtitle_remover_process(task.path, task.output_path, options) # 更新任务状态为已完成 task = self.task_list_component.get_task(self.current_processing_task_index) @@ -363,20 +380,19 @@ class HomeInterface(QWidget): self.stop_button.setVisible(False) @staticmethod - def remover_process(queue, video_path, output_path, subtitle_area, options): + def remover_process(queue, video_path, output_path, options): """ 在子进程中执行字幕提取的函数 Args: video_path: 视频文件路径 output_path: 输出文件路径 - subtitle_area: 字幕区域坐标 (ymin, ymax, xmin, xmax) options: 选项 """ sr = None try: from backend.main import SubtitleRemover - sr = SubtitleRemover(video_path, subtitle_area, True) + sr = SubtitleRemover(video_path, True) sr.video_out_path = output_path for key in options: setattr(sr, key, options[key]) @@ -396,14 +412,13 @@ class HomeInterface(QWidget): # 修改run_subtitle_remover_process方法 - def run_subtitle_remover_process(self, video_path, output_path, subtitle_areas, options): + def run_subtitle_remover_process(self, video_path, output_path, options): """ 使用多进程执行字幕提取,并等待进程完成 Args: video_path: 视频文件路径 output_path: 输出文件路径 - subtitle_areas: 字幕区域坐标 [(ymin, ymax, xmin, xmax)] options: 任务选项 """ subtitle_remover_remote_caller = SubtitleRemoverRemoteCall() @@ -413,7 +428,7 @@ class HomeInterface(QWidget): subtitle_remover_remote_caller.register_error_callback(self.task_error_signal.emit) process = multiprocessing.Process( target=HomeInterface.remover_process, - args=(subtitle_remover_remote_caller.queue, video_path, output_path, subtitle_areas, options) + args=(subtitle_remover_remote_caller.queue, video_path, output_path, options) ) try: if not self.running_task: @@ -487,14 +502,15 @@ class HomeInterface(QWidget): def update_preview_with_comp(self, args): """更新执行时预览""" frame_ori, frame_comp = args - - subtitle_areas = self.video_display_component.get_original_coordinates() - if subtitle_areas: - if frame_ori is frame_comp: - frame_ori = frame_ori.copy() - for rect in subtitle_areas: - ymin, ymax, xmin, xmax = rect - cv2.rectangle(frame_ori, (xmin, ymin), (xmax, ymax), (0, 255, 0), 2) + if self.current_processing_task_index >= 0: + subtitle_areas = self.task_list_component.get_task_option(self.current_processing_task_index, TaskOptions.SUB_AREAS, []) + if len(subtitle_areas) > 0: + subtitle_areas = self.video_display_component.preview_coordinates_to_video_coordinates(subtitle_areas) + if frame_ori is frame_comp: + frame_ori = frame_ori.copy() + for rect in subtitle_areas: + ymin, ymax, xmin, xmax = rect + cv2.rectangle(frame_ori, (xmin, ymin), (xmax, ymax), (0, 255, 0), 2) preview_frame = cv2.hconcat([frame_ori, frame_comp]) # 先缩放图像 resized_frame = self._img_resize(preview_frame) @@ -525,7 +541,6 @@ class HomeInterface(QWidget): self.fps = self.video_cap.get(cv2.CAP_PROP_FPS) self.update_preview(frame) - self.video_display_component.load_selections_from_config() self.video_slider.setMaximum(self.frame_count) self.video_slider.setValue(1) self.video_display_component.set_dragger_enabled(True) @@ -544,7 +559,6 @@ class HomeInterface(QWidget): self.frame_width = frame.shape[1] self.fps = 1 self.update_preview(frame) - self.video_display_component.load_selections_from_config() self.video_slider.setMaximum(self.frame_count) self.video_slider.setValue(1) self.video_display_component.set_dragger_enabled(True) @@ -588,6 +602,7 @@ class HomeInterface(QWidget): self.task_error_signal.disconnect(self.on_task_error) self.video_display_component.video_slider.valueChanged.disconnect(self.slider_changed) self.video_display_component.ab_sections_changed.disconnect(self.ab_sections_changed) + self.video_display_component.selections_changed.disconnect(self.selections_changed) # 释放视频资源 if self.video_cap: self.video_cap.release()