From e1d94f2b2470ec876102b3f4d776a028e9395384 Mon Sep 17 00:00:00 2001 From: PhatPhuckDave Date: Fri, 26 Sep 2025 18:42:35 +0200 Subject: [PATCH] Refactor template management in VideoEditor to streamline template handling This commit removes unused attributes and logic related to template matching, enhancing the clarity and efficiency of the template management system. The changes include the removal of the template matching enabled flag and associated state management, focusing on the current template ID and its associated data. Debug messages have been updated to reflect these changes, ensuring better tracking and feedback during video editing sessions. --- croppa/main.py | 81 ++++++++++++++++++++++---------------------------- 1 file changed, 35 insertions(+), 46 deletions(-) diff --git a/croppa/main.py b/croppa/main.py index d85ada6..6f5a426 100644 --- a/croppa/main.py +++ b/croppa/main.py @@ -860,9 +860,6 @@ class VideoEditor: self.previous_frame_for_flow = None # Template matching tracking - self.template_matching_enabled = False - self.tracking_template = None - self.template_region = None self.template_match_history = [] # Store recent match confidences for adaptive thresholding # (x, y, w, h) in rotated frame coordinates self.template_selection_start = None @@ -922,8 +919,6 @@ class VideoEditor: '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(), - 'template_matching_enabled': self.template_matching_enabled, - 'template_region': self.template_region, 'template_matching_full_frame': self.template_matching_full_frame, 'templates': {str(k): { 'template': None, # Don't save template images (too large) @@ -1021,12 +1016,6 @@ class VideoEditor: 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 'template_region' in state and state['template_region'] is not None: - self.template_region = state['template_region'] - # Recreate template from region when needed - self.tracking_template = None if 'template_matching_full_frame' in state: self.template_matching_full_frame = state['template_matching_full_frame'] @@ -1666,7 +1655,7 @@ class VideoEditor: # Calculate offset from template matching if enabled template_offset = None - if self.template_matching_enabled and self.tracking_template is not None: + if self.templates and self.current_template_id is not None: if self.current_display_frame is not None: if self.template_matching_full_frame: # Full frame mode - use the entire original frame @@ -1836,7 +1825,7 @@ class VideoEditor: def _get_template_matching_position(self, frame_number): """Get template matching position and confidence for a frame""" - if not self.template_matching_enabled or self.tracking_template is None: + if not self.templates or self.current_template_id is None: return None if self.current_display_frame is not None: @@ -1948,8 +1937,6 @@ class VideoEditor: def _map_screen_to_rotated(self, sx, sy): """Map a point on canvas screen coords back to ROTATED frame coords (pre-crop).""" - frame_number = getattr(self, 'current_frame', 0) - angle = self.rotation_angle # Use unified display params params = self._get_display_params() # Back to processed (zoomed+cropped) space @@ -2293,12 +2280,18 @@ class VideoEditor: def track_template(self, frame): """Track the template in the current frame""" - if self.tracking_template is None: + if self.current_template_id is None or self.current_template_id not in self.templates: + return None + + template_data = self.templates[self.current_template_id] + tracking_template = template_data['template'] + + if tracking_template is None: return None try: # Apply image preprocessing for better template matching - gray_frame, gray_template = self._improve_template_matching(frame, self.tracking_template) + gray_frame, gray_template = self._improve_template_matching(frame, tracking_template) # Single-scale template matching (faster) result = cv2.matchTemplate(gray_frame, gray_template, cv2.TM_CCOEFF_NORMED) @@ -2391,7 +2384,7 @@ class VideoEditor: # Extract template from raw frame template = self.current_display_frame[raw_y:raw_y+raw_h, raw_x:raw_x+raw_w] if template.size > 0: - # Add template to collection instead of setting single template + # Add template to collection template_id = self.add_template(template, (raw_x, raw_y, raw_w, raw_h)) self.show_feedback_message(f"Template {template_id} set from region ({raw_w}x{raw_h})") print(f"DEBUG: Template {template_id} set with size {template.shape}") @@ -2410,16 +2403,19 @@ class VideoEditor: if end_frame is None: end_frame = self.total_frames - 1 - # End any templates that start after the current frame + # Only end the current template if it exists and starts at or before the current frame + if self.current_template_id is not None and self.current_template_id in self.templates: + current_template = self.templates[self.current_template_id] + if current_template['start_frame'] <= self.current_frame: + # End the current template at the previous frame + self.templates[self.current_template_id]['end_frame'] = self.current_frame - 1 + print(f"DEBUG: Ended current template {self.current_template_id} at frame {self.current_frame - 1}") + + # Remove any templates that start after the current frame (they shouldn't exist yet) for template_id, template_data in list(self.templates.items()): if template_data['start_frame'] > self.current_frame: - # This template starts after the current frame, so remove it del self.templates[template_id] print(f"DEBUG: Removed future template {template_id} that started at frame {template_data['start_frame']}") - elif template_data['start_frame'] == self.current_frame: - # This template starts at the same frame, end it at the previous frame - self.templates[template_id]['end_frame'] = self.current_frame - 1 - print(f"DEBUG: Ended template {template_id} at frame {self.current_frame - 1}") self.templates[template_id] = { 'template': template.copy(), @@ -2428,11 +2424,8 @@ class VideoEditor: 'end_frame': end_frame } - # Set as current template and enable template matching + # Set as current template self.current_template_id = template_id - self.tracking_template = template.copy() - self.template_region = region - self.template_matching_enabled = True # Enable template matching when template is created self.show_feedback_message(f"Template {template_id} added (frames {start_frame}-{end_frame})") return template_id @@ -2446,11 +2439,8 @@ class VideoEditor: if self.current_template_id == template_id: self._select_best_template_for_frame(self.current_frame) - # If no templates left, disable template matching + # If no templates left, clear current template if not self.templates: - self.template_matching_enabled = False - self.tracking_template = None - self.template_region = None self.current_template_id = None self.show_feedback_message(f"Template {template_id} removed") @@ -2491,13 +2481,10 @@ class VideoEditor: else: return False - self.tracking_template = template_data['template'].copy() - self.template_region = template_data['region'] + # Template is already stored in the templates dict return True else: self.current_template_id = None - self.tracking_template = None - self.template_region = None return False def _recreate_template_from_template_data(self, template_id, frame): @@ -3168,7 +3155,7 @@ class VideoEditor: if self.optical_flow_enabled: feature_text += " (OPTICAL FLOW)" template_text = "" - if self.template_matching_enabled: + if self.templates: mode = "Full Frame" if self.template_matching_full_frame else "Cropped" template_text = f" | Template: {mode}" autorepeat_text = ( @@ -3284,8 +3271,8 @@ class VideoEditor: # Draw template matching point (blue circle with confidence) if (not self.is_image_mode and - self.template_matching_enabled and - self.tracking_template is not None): + self.templates and + self.current_template_id is not None): # Get template matching position for current frame template_pos = self._get_template_matching_position(self.current_frame) if template_pos: @@ -3911,13 +3898,10 @@ class VideoEditor: def _render_video_worker(self, output_path: str): """Worker method that runs in the render thread""" - render_cap = None try: if not output_path.endswith(".mp4"): output_path += ".mp4" - start_time = time.time() - # Send progress update to main thread self.render_progress_queue.put(("init", "Initializing render...", 0.0, 0.0)) @@ -4728,10 +4712,15 @@ class VideoEditor: self.show_feedback_message(f"Optical flow {'ON' if self.optical_flow_enabled else 'OFF'}") self.save_state() elif key == ord("m"): - # Toggle template matching tracking - self.template_matching_enabled = not self.template_matching_enabled - print(f"DEBUG: Template matching toggled to {self.template_matching_enabled}") - self.show_feedback_message(f"Template matching {'ON' if self.template_matching_enabled else 'OFF'}") + # Clear all templates + if self.templates: + self.templates.clear() + self.current_template_id = None + print("DEBUG: All templates cleared") + self.show_feedback_message("All templates cleared") + else: + print("DEBUG: No templates to clear") + self.show_feedback_message("No templates to clear") self.save_state() elif key == ord("M"): # Shift+M - Toggle multi-scale template matching self.template_matching_full_frame = not self.template_matching_full_frame