From f942392fb30835e0c6963afc9fbd526cdcbe5d83 Mon Sep 17 00:00:00 2001 From: PhatPhuckDave Date: Fri, 26 Sep 2025 14:26:42 +0200 Subject: [PATCH] Enhance VideoEditor with template matching state management and user interaction updates This commit adds functionality to manage the state of template matching in the VideoEditor, including loading and saving template matching settings. It also updates user interaction for selecting template regions, changing the control scheme from Alt+Right-click to Ctrl+Left-click for better usability. Additionally, it improves the handling of the current display frame during feature extraction, ensuring robustness in the tracking process. --- croppa/main.py | 60 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/croppa/main.py b/croppa/main.py index 163a384..885be61 100644 --- a/croppa/main.py +++ b/croppa/main.py @@ -929,7 +929,10 @@ class VideoEditor: 'is_playing': getattr(self, 'is_playing', False), 'tracking_enabled': self.tracking_enabled, 'tracking_points': {str(k): v for k, v in self.tracking_points.items()}, - 'feature_tracker': self.feature_tracker.get_state_dict() + 'feature_tracker': self.feature_tracker.get_state_dict(), + 'template_matching_enabled': self.template_matching_enabled, + 'tracking_template': self.tracking_template, + 'template_region': self.template_region } with open(state_file, 'w') as f: @@ -1016,6 +1019,14 @@ class VideoEditor: if 'feature_tracker' in state: self.feature_tracker.load_state_dict(state['feature_tracker']) print(f"Loaded feature tracker state") + + # Load template matching state + if 'template_matching_enabled' in state: + self.template_matching_enabled = state['template_matching_enabled'] + if 'tracking_template' in state and state['tracking_template'] is not None: + self.tracking_template = state['tracking_template'] + if 'template_region' in state: + self.template_region = state['template_region'] # Validate cut markers against current video length if self.cut_start_frame is not None and self.cut_start_frame >= self.total_frames: @@ -1831,21 +1842,22 @@ class VideoEditor: orig_x + orig_w <= self.frame_width and orig_y + orig_h <= self.frame_height): - region_frame = self.current_display_frame[orig_y:orig_y+orig_h, orig_x:orig_x+orig_w] - if region_frame.size > 0: - # Map coordinates from region to rotated frame coordinates - def coord_mapper(px, py): - # Map from region coordinates to rotated frame coordinates - if self.rotation_angle == 90: - rot_x = orig_x + py - rot_y = self.frame_height - (orig_y + px) - elif self.rotation_angle == 270: - rot_x = self.frame_width - (orig_y + py) - rot_y = orig_x + px - else: - rot_x = orig_x + px - rot_y = orig_y + py - return (int(rot_x), int(rot_y)) + if self.current_display_frame is not None: + region_frame = self.current_display_frame[orig_y:orig_y+orig_h, orig_x:orig_x+orig_w] + if region_frame is not None and region_frame.size > 0: + # Map coordinates from region to rotated frame coordinates + def coord_mapper(px, py): + # Map from region coordinates to rotated frame coordinates + if self.rotation_angle == 90: + rot_x = orig_x + py + rot_y = self.frame_height - (orig_y + px) + elif self.rotation_angle == 270: + rot_x = self.frame_width - (orig_y + py) + rot_y = orig_x + px + else: + rot_x = orig_x + px + rot_y = orig_y + py + return (int(rot_x), int(rot_y)) # Extract features and add them to existing features success = self.feature_tracker.extract_features_from_region(region_frame, self.current_frame, coord_mapper) @@ -2143,7 +2155,7 @@ class VideoEditor: # Template matching result = cv2.matchTemplate(gray_frame, gray_template, cv2.TM_CCOEFF_NORMED) - min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) + _, max_val, _, max_loc = cv2.minMaxLoc(result) # Only accept matches above threshold if max_val > 0.6: # Adjust threshold as needed @@ -3055,21 +3067,21 @@ class VideoEditor: self.selective_feature_deletion_start = None self.selective_feature_deletion_rect = None - # Handle Alt+Right-click+drag for template region selection - if event == cv2.EVENT_RBUTTONDOWN and (flags & cv2.EVENT_FLAG_ALTKEY): + # Handle Ctrl+Left-click+drag for template region selection + if event == cv2.EVENT_LBUTTONDOWN and (flags & cv2.EVENT_FLAG_CTRLKEY): if not self.is_image_mode: self.template_selection_start = (x, y) self.template_selection_rect = None print(f"DEBUG: Started template selection at ({x}, {y})") - # Handle Alt+Right-click+drag for template region selection - if event == cv2.EVENT_MOUSEMOVE and (flags & cv2.EVENT_FLAG_ALTKEY) and self.template_selection_start: + # Handle Ctrl+Left-click+drag for template region selection + if event == cv2.EVENT_MOUSEMOVE and (flags & cv2.EVENT_FLAG_CTRLKEY) and self.template_selection_start: if not self.is_image_mode: start_x, start_y = self.template_selection_start self.template_selection_rect = (min(start_x, x), min(start_y, y), abs(x - start_x), abs(y - start_y)) - # Handle Alt+Right-click release for template region selection - if event == cv2.EVENT_RBUTTONUP and (flags & cv2.EVENT_FLAG_ALTKEY) and self.template_selection_start: + # Handle Ctrl+Left-click release for template region selection + if event == cv2.EVENT_LBUTTONUP and (flags & cv2.EVENT_FLAG_CTRLKEY) and self.template_selection_start: if not self.is_image_mode and self.template_selection_rect: self._set_template_from_region(self.template_selection_rect) self.template_selection_start = None @@ -3927,7 +3939,7 @@ class VideoEditor: print(" m: Toggle template matching tracking") print(" Shift+Right-click+drag: Extract features from selected region") print(" Ctrl+Right-click+drag: Delete features from selected region") - print(" Alt+Right-click+drag: Set template region for tracking") + print(" Ctrl+Left-click+drag: Set template region for tracking") if len(self.video_files) > 1: print(" N: Next video") print(" n: Previous video")