diff --git a/croppa/main.py b/croppa/main.py index c7f1c2e..bff5059 100644 --- a/croppa/main.py +++ b/croppa/main.py @@ -202,7 +202,7 @@ class MotionTracker: return (0.0, 0.0) # Calculate offset to center the crop on the tracked point - # The offset should move the crop so the tracked point stays centered + # The offset should move the display so the tracked point stays centered offset_x = current_pos[0] - self.base_zoom_center[0] offset_y = current_pos[1] - self.base_zoom_center[1] @@ -1241,17 +1241,10 @@ class VideoEditor: # Apply brightness/contrast first (to original frame for best quality) processed_frame = self.apply_brightness_contrast(processed_frame) - # Apply crop with motion tracking offset + # Apply crop if self.crop_rect: x, y, w, h = self.crop_rect x, y, w, h = int(x), int(y), int(w), int(h) - - # Apply motion tracking offset to center crop on tracked point - if self.motion_tracker.tracking_enabled: - tracking_offset_x, tracking_offset_y = self.motion_tracker.get_tracking_offset(self.current_frame) - x += int(tracking_offset_x) - y += int(tracking_offset_y) - # Ensure crop is within frame bounds x = max(0, min(x, processed_frame.shape[1] - 1)) y = max(0, min(y, processed_frame.shape[0] - 1)) @@ -1273,11 +1266,14 @@ class VideoEditor: processed_frame, (new_width, new_height), interpolation=cv2.INTER_LINEAR ) - # Handle zoom center and display offset + # Handle zoom center and display offset with motion tracking if new_width > self.window_width or new_height > self.window_height: + # Apply motion tracking offset to display offset + tracking_offset_x, tracking_offset_y = self.motion_tracker.get_tracking_offset(self.current_frame) + # Calculate crop from zoomed image to fit window - start_x = max(0, self.display_offset[0]) - start_y = max(0, self.display_offset[1]) + start_x = max(0, self.display_offset[0] + int(tracking_offset_x)) + start_y = max(0, self.display_offset[1] + int(tracking_offset_y)) end_x = min(new_width, start_x + self.window_width) end_y = min(new_height, start_y + self.window_height) processed_frame = processed_frame[start_y:end_y, start_x:end_x] @@ -1993,30 +1989,13 @@ class VideoEditor: point_removed = False for i, (px, py) in enumerate(current_points): - # Convert point to screen coordinates to check distance - # We need to calculate the same parameters as in display_current_frame - if self.current_display_frame is not None: - # Apply transformations to get display frame - display_frame = self.apply_crop_zoom_and_rotation(self.current_display_frame) - if display_frame is not None: - height, width = display_frame.shape[:2] - available_height = self.window_height - (0 if self.is_image_mode else self.TIMELINE_HEIGHT) - scale = min(self.window_width / width, available_height / height) - - # Calculate display position - frame_height, frame_width = display_frame.shape[:2] - start_y = (available_height - frame_height) // 2 - start_x = (self.window_width - frame_width) // 2 - - screen_px, screen_py = self.video_to_screen_coords(px, py, start_x, start_y, scale) - if screen_px is not None and screen_py is not None: - # Calculate distance in screen coordinates - distance = ((x - screen_px) ** 2 + (y - screen_py) ** 2) ** 0.5 - if distance < 20: # Within 20 pixels - self.motion_tracker.remove_tracking_point(self.current_frame, i) - self.set_feedback_message(f"Tracking point removed at frame {self.current_frame}") - point_removed = True - break + # Calculate distance in video coordinates (simpler and more reliable) + distance = ((video_x - px) ** 2 + (video_y - py) ** 2) ** 0.5 + if distance < 50: # Within 50 pixels in video coordinates + self.motion_tracker.remove_tracking_point(self.current_frame, i) + self.set_feedback_message(f"Tracking point removed at frame {self.current_frame}") + point_removed = True + break if not point_removed: # No nearby point found, add a new one @@ -2388,42 +2367,44 @@ class VideoEditor: # Normalize rotation to 0-270 degrees rotation = self.rotation_angle % 360 - if hjkl_key == 'h': # Left + # The mapping should be: when video is rotated, the visual directions change + # but HJKL should still correspond to the same visual directions + if hjkl_key == 'h': # Visual Left if rotation == 0: return 'left' elif rotation == 90: - return 'down' + return 'up' # Visual left becomes up in rotated video elif rotation == 180: return 'right' elif rotation == 270: - return 'up' - elif hjkl_key == 'j': # Down + return 'down' # Visual left becomes down in rotated video + elif hjkl_key == 'j': # Visual Down if rotation == 0: return 'down' elif rotation == 90: - return 'left' + return 'left' # Visual down becomes left in rotated video elif rotation == 180: return 'up' elif rotation == 270: - return 'right' - elif hjkl_key == 'k': # Up + return 'right' # Visual down becomes right in rotated video + elif hjkl_key == 'k': # Visual Up if rotation == 0: return 'up' elif rotation == 90: - return 'right' + return 'right' # Visual up becomes right in rotated video elif rotation == 180: return 'down' elif rotation == 270: - return 'left' - elif hjkl_key == 'l': # Right + return 'left' # Visual up becomes left in rotated video + elif hjkl_key == 'l': # Visual Right if rotation == 0: return 'right' elif rotation == 90: - return 'up' + return 'down' # Visual right becomes down in rotated video elif rotation == 180: return 'left' elif rotation == 270: - return 'down' + return 'up' # Visual right becomes up in rotated video return hjkl_key # Fallback to original if not recognized