From 44ed4220b94b367d8f91b74bacd3f13e5e1d4739 Mon Sep 17 00:00:00 2001 From: PhatPhuckDave Date: Fri, 26 Sep 2025 18:20:12 +0200 Subject: [PATCH] Refactor template matching logic in VideoEditor to support full frame and cropped modes This commit introduces a toggle for template matching modes in the VideoEditor, allowing users to choose between full frame and cropped region matching. The logic has been updated to handle both modes effectively, improving flexibility and performance during template tracking. Additionally, the multi-scale template matching feature has been removed, streamlining the template matching process. Debug messages and feedback have been enhanced to reflect the current mode, ensuring better user experience during video editing sessions. --- croppa/main.py | 135 ++++++++++++++++++++++++++++--------------------- 1 file changed, 77 insertions(+), 58 deletions(-) diff --git a/croppa/main.py b/croppa/main.py index 3497523..2d7acf7 100644 --- a/croppa/main.py +++ b/croppa/main.py @@ -882,7 +882,7 @@ class VideoEditor: self.tracking_template = None self.template_region = None self.template_match_history = [] # Store recent match confidences for adaptive thresholding - self.multi_scale_template_matching = False # Disable multi-scale by default # (x, y, w, h) in rotated frame coordinates + # (x, y, w, h) in rotated frame coordinates self.template_selection_start = None self.template_selection_rect = None @@ -890,6 +890,9 @@ class VideoEditor: self.templates = {} # {template_id: {'template': image, 'region': (x,y,w,h), 'start_frame': int, 'end_frame': int}} self.current_template_id = None self.template_id_counter = 0 + + # Template matching modes + self.template_matching_full_frame = False # Toggle for full frame vs cropped template matching # Project view mode self.project_view_mode = False @@ -939,7 +942,7 @@ class VideoEditor: 'feature_tracker': self.feature_tracker.get_state_dict(), 'template_matching_enabled': self.template_matching_enabled, 'template_region': self.template_region, - 'multi_scale_template_matching': self.multi_scale_template_matching, + 'template_matching_full_frame': self.template_matching_full_frame, 'templates': {str(k): { 'template': None, # Don't save template images (too large) 'region': v['region'], @@ -1042,8 +1045,8 @@ class VideoEditor: self.template_region = state['template_region'] # Recreate template from region when needed self.tracking_template = None - if 'multi_scale_template_matching' in state: - self.multi_scale_template_matching = state['multi_scale_template_matching'] # Will be recreated on first use + if 'template_matching_full_frame' in state: + self.template_matching_full_frame = state['template_matching_full_frame'] # Load multiple templates state if 'templates' in state: @@ -1683,34 +1686,42 @@ class VideoEditor: template_offset = None if self.template_matching_enabled and self.tracking_template is not None: if self.current_display_frame is not None: - # Use only the cropped region for much faster template matching - if self.crop_rect: - crop_x, crop_y, crop_w, crop_h = self.crop_rect - # Extract only the cropped region from raw frame - cropped_frame = self.current_display_frame[crop_y:crop_y+crop_h, crop_x:crop_x+crop_w] - if cropped_frame is not None and cropped_frame.size > 0: - # Apply motion tracking offset to the cropped frame - offset_frame = self._apply_motion_tracking_offset(cropped_frame, base_pos) + if self.template_matching_full_frame: + # Full frame mode - use the entire original frame + result = self.track_template(self.current_display_frame) + if result: + center_x, center_y, confidence = result + print(f"DEBUG: Template match found at ({center_x}, {center_y}) with confidence {confidence:.2f}") + template_offset = (center_x, center_y) + else: + # Cropped mode - use only the cropped region for faster template matching + if self.crop_rect: + crop_x, crop_y, crop_w, crop_h = self.crop_rect + # Extract only the cropped region from raw frame + cropped_frame = self.current_display_frame[crop_y:crop_y+crop_h, crop_x:crop_x+crop_w] + if cropped_frame is not None and cropped_frame.size > 0: + # Apply motion tracking offset to the cropped frame + offset_frame = self._apply_motion_tracking_offset(cropped_frame, base_pos) + if offset_frame is not None: + # Track template in cropped and offset frame (much faster!) + result = self.track_template(offset_frame) + if result: + center_x, center_y, confidence = result + print(f"DEBUG: Template match found at ({center_x}, {center_y}) with confidence {confidence:.2f}") + + # Map from cropped frame coordinates to raw frame coordinates + # Add crop offset back + raw_x = center_x + crop_x + raw_y = center_y + crop_y + template_offset = (raw_x, raw_y) + else: + # No crop - use full frame with offset + offset_frame = self._apply_motion_tracking_offset(self.current_display_frame, base_pos) if offset_frame is not None: - # Track template in cropped and offset frame (much faster!) result = self.track_template(offset_frame) if result: center_x, center_y, confidence = result - print(f"DEBUG: Template match found at ({center_x}, {center_y}) with confidence {confidence:.2f}") - - # Map from cropped frame coordinates to raw frame coordinates - # Add crop offset back - raw_x = center_x + crop_x - raw_y = center_y + crop_y - template_offset = (raw_x, raw_y) - else: - # No crop - use full frame with offset - offset_frame = self._apply_motion_tracking_offset(self.current_display_frame, base_pos) - if offset_frame is not None: - result = self.track_template(offset_frame) - if result: - center_x, center_y, confidence = result - template_offset = (center_x, center_y) + template_offset = (center_x, center_y) # Calculate offset from feature tracking if enabled feature_offset = None @@ -1850,32 +1861,39 @@ class VideoEditor: # Get base position for motion tracking offset base_pos = self._get_manual_tracking_position(frame_number) - # Use only the cropped region for much faster template matching - if self.crop_rect: - crop_x, crop_y, crop_w, crop_h = self.crop_rect - # Extract only the cropped region from raw frame - cropped_frame = self.current_display_frame[crop_y:crop_y+crop_h, crop_x:crop_x+crop_w] - if cropped_frame is not None and cropped_frame.size > 0: - # Apply motion tracking offset to the cropped frame - offset_frame = self._apply_motion_tracking_offset(cropped_frame, base_pos) + if self.template_matching_full_frame: + # Full frame mode - use the entire original frame + result = self.track_template(self.current_display_frame) + if result: + center_x, center_y, confidence = result + return (center_x, center_y, confidence) + else: + # Cropped mode - use only the cropped region for faster template matching + if self.crop_rect: + crop_x, crop_y, crop_w, crop_h = self.crop_rect + # Extract only the cropped region from raw frame + cropped_frame = self.current_display_frame[crop_y:crop_y+crop_h, crop_x:crop_x+crop_w] + if cropped_frame is not None and cropped_frame.size > 0: + # Apply motion tracking offset to the cropped frame + offset_frame = self._apply_motion_tracking_offset(cropped_frame, base_pos) + if offset_frame is not None: + # Track template in cropped and offset frame (much faster!) + result = self.track_template(offset_frame) + if result: + center_x, center_y, confidence = result + # Map from cropped frame coordinates to raw frame coordinates + # Add crop offset back + raw_x = center_x + crop_x + raw_y = center_y + crop_y + return (raw_x, raw_y, confidence) + else: + # No crop - use full frame with offset + offset_frame = self._apply_motion_tracking_offset(self.current_display_frame, base_pos) if offset_frame is not None: - # Track template in cropped and offset frame (much faster!) result = self.track_template(offset_frame) if result: center_x, center_y, confidence = result - # Map from cropped frame coordinates to raw frame coordinates - # Add crop offset back - raw_x = center_x + crop_x - raw_y = center_y + crop_y - return (raw_x, raw_y, confidence) - else: - # No crop - use full frame with offset - offset_frame = self._apply_motion_tracking_offset(self.current_display_frame, base_pos) - if offset_frame is not None: - result = self.track_template(offset_frame) - if result: - center_x, center_y, confidence = result - return (center_x, center_y, confidence) + return (center_x, center_y, confidence) return None @@ -2312,7 +2330,7 @@ class VideoEditor: gray_frame, gray_template = self._improve_template_matching(frame, self.tracking_template) # Multi-scale template matching for better tracking (if enabled) - if self.multi_scale_template_matching: + if False: # Multi-scale template matching removed scales = [0.8, 0.9, 1.0, 1.1, 1.2] # Different scales to try best_match = None best_confidence = 0.0 @@ -3231,10 +3249,11 @@ class VideoEditor: feature_text = f" | Features: {feature_count} pts" if self.optical_flow_enabled: feature_text += " (OPTICAL FLOW)" - template_text = ( - f" | Template: {self.template_matching_enabled}" if self.template_matching_enabled else "" - ) - if self.template_matching_enabled and self.multi_scale_template_matching: + template_text = "" + if self.template_matching_enabled: + mode = "Full Frame" if self.template_matching_full_frame else "Cropped" + template_text = f" | Template: {mode}" + if False: # Multi-scale template matching removed template_text += " (MULTI-SCALE)" autorepeat_text = ( f" | Loop: ON" if self.looping_between_markers else "" @@ -4799,9 +4818,9 @@ class VideoEditor: self.show_feedback_message(f"Template matching {'ON' if self.template_matching_enabled else 'OFF'}") self.save_state() elif key == ord("M"): # Shift+M - Toggle multi-scale template matching - self.multi_scale_template_matching = not self.multi_scale_template_matching - print(f"DEBUG: Multi-scale template matching toggled to {self.multi_scale_template_matching}") - self.show_feedback_message(f"Multi-scale template matching {'ON' if self.multi_scale_template_matching else 'OFF'}") + self.template_matching_full_frame = not self.template_matching_full_frame + print(f"DEBUG: Template matching full frame toggled to {self.template_matching_full_frame}") + self.show_feedback_message(f"Template matching: {'Full Frame' if self.template_matching_full_frame else 'Cropped'}") self.save_state() elif key == ord(";"): # Semicolon - Jump to previous template marker self.jump_to_previous_template()