diff --git a/croppa/main.py b/croppa/main.py index ebab0dd..44b2664 100644 --- a/croppa/main.py +++ b/croppa/main.py @@ -1620,13 +1620,25 @@ class VideoEditor: def _get_interpolated_tracking_position(self, frame_number): """Linear interpolation in ROTATED frame coords. Returns (rx, ry) or None.""" - # First try feature tracking if enabled + # First try feature tracking if enabled - but use smooth interpolation instead of averaging if self.feature_tracker.tracking_enabled: - feature_pos = self.feature_tracker.get_tracking_position(frame_number) - if feature_pos: - # Features are stored in rotated frame coordinates (like existing motion tracking) - # We can use them directly for the tracking system - return (feature_pos[0], feature_pos[1]) + # Get the nearest frames with features for smooth interpolation + feature_frames = sorted(self.feature_tracker.features.keys()) + if feature_frames: + # Find the two nearest frames for interpolation + if frame_number <= feature_frames[0]: + # Before first feature frame - use first frame + return self._get_feature_center(feature_frames[0]) + elif frame_number >= feature_frames[-1]: + # After last feature frame - use last frame + return self._get_feature_center(feature_frames[-1]) + else: + # Between two feature frames - interpolate smoothly + for i in range(len(feature_frames) - 1): + if feature_frames[i] <= frame_number <= feature_frames[i + 1]: + return self._interpolate_feature_positions( + feature_frames[i], feature_frames[i + 1], frame_number + ) # Fall back to manual tracking points if not self.tracking_points: @@ -2036,6 +2048,37 @@ class VideoEditor: except Exception as e: print(f"Error filling all gaps: {e}") + + def _get_feature_center(self, frame_number): + """Get the center of features for a frame (smooth, not jarring)""" + if frame_number not in self.feature_tracker.features: + return None + + positions = self.feature_tracker.features[frame_number]['positions'] + if not positions: + return None + + # Calculate center of mass (smoother than average) + center_x = sum(pos[0] for pos in positions) / len(positions) + center_y = sum(pos[1] for pos in positions) / len(positions) + + return (center_x, center_y) + + def _interpolate_feature_positions(self, start_frame, end_frame, target_frame): + """Smoothly interpolate between feature centers of two frames""" + start_center = self._get_feature_center(start_frame) + end_center = self._get_feature_center(end_frame) + + if not start_center or not end_center: + return None + + # Linear interpolation between centers + alpha = (target_frame - start_frame) / (end_frame - start_frame) + + interp_x = start_center[0] + alpha * (end_center[0] - start_center[0]) + interp_y = start_center[1] + alpha * (end_center[1] - start_center[1]) + + return (interp_x, interp_y) def apply_rotation(self, frame):