diff --git a/croppa/main.py b/croppa/main.py index 130e84e..5512a99 100644 --- a/croppa/main.py +++ b/croppa/main.py @@ -1102,6 +1102,40 @@ class VideoEditor: print(f"DEBUG: Jump next tracking wrap from {current} -> {tracking_frames[0]}; tracking_frames={tracking_frames}") self.seek_to_frame(tracking_frames[0]) + def _get_previous_tracking_point(self): + """Get the tracking point from the previous frame that has tracking points.""" + if self.is_image_mode or not self.tracking_points: + return None + + tracking_frames = sorted(k for k, v in self.tracking_points.items() if v and 0 <= k < self.total_frames) + if not tracking_frames: + return None + + # Find the last frame with tracking points that's before current frame + prev_frames = [f for f in tracking_frames if f < self.current_frame] + if not prev_frames: + return None + + prev_frame = max(prev_frames) + return prev_frame, self.tracking_points[prev_frame] + + def _get_next_tracking_point(self): + """Get the tracking point from the next frame that has tracking points.""" + if self.is_image_mode or not self.tracking_points: + return None + + tracking_frames = sorted(k for k, v in self.tracking_points.items() if v and 0 <= k < self.total_frames) + if not tracking_frames: + return None + + # Find the first frame with tracking points that's after current frame + next_frames = [f for f in tracking_frames if f > self.current_frame] + if not next_frames: + return None + + next_frame = min(next_frames) + return next_frame, self.tracking_points[next_frame] + def advance_frame(self) -> bool: """Advance to next frame - handles playback speed and marker looping""" if not self.is_playing: @@ -2056,12 +2090,12 @@ class VideoEditor: cv2.circle(canvas, (sx, sy), 6, (255, 0, 0), -1) cv2.circle(canvas, (sx, sy), 6, (255, 255, 255), 1) - # Draw previous and next frame tracking points with 50% alpha + # Draw previous and next tracking points with 50% alpha if not self.is_image_mode and self.tracking_points: - # Previous frame tracking points (red) - prev_frame = self.current_frame - 1 - if prev_frame in self.tracking_points: - prev_pts = self.tracking_points[prev_frame] + # Previous tracking point (red) - from the most recent frame with tracking points before current + prev_result = self._get_previous_tracking_point() + if prev_result: + prev_frame, prev_pts = prev_result for (rx, ry) in prev_pts: sx, sy = self._map_rotated_to_screen(rx, ry) # Create overlay for alpha blending @@ -2069,10 +2103,10 @@ class VideoEditor: cv2.circle(overlay, (sx, sy), 4, (0, 0, 255), -1) # Red circle cv2.addWeighted(overlay, 0.5, canvas, 0.5, 0, canvas) - # Next frame tracking points (green) - next_frame = self.current_frame + 1 - if next_frame in self.tracking_points: - next_pts = self.tracking_points[next_frame] + # Next tracking point (green) - from the next frame with tracking points after current + next_result = self._get_next_tracking_point() + if next_result: + next_frame, next_pts = next_result for (rx, ry) in next_pts: sx, sy = self._map_rotated_to_screen(rx, ry) # Create overlay for alpha blending diff --git a/croppa/spec.md b/croppa/spec.md index a8f18bd..dc4cd0e 100644 --- a/croppa/spec.md +++ b/croppa/spec.md @@ -63,7 +63,7 @@ Be careful to save and load settings when navigating this way - **Blue cross**: Shows computed tracking position - **Automatic interpolation**: Tracks between keyframes - **Crop follows**: Crop area centers on tracked object -- **Display** Points are rendered as blue dots per frame, in addition dots are rendered on each frame for each dot on the previous (in red) and next (in green) frame +- **Display** Points are rendered as blue dots per frame, in addition the previous tracking point (red) and next tracking point (green) are shown regardless of which frame they're on #### Motion Tracking Navigation - **,**: Jump to previous tracking marker (previous frame that has one or more tracking points). Wrap-around supported.