diff --git a/croppa/main.py b/croppa/main.py index 5512a99..1c3d162 100644 --- a/croppa/main.py +++ b/croppa/main.py @@ -2090,29 +2090,63 @@ 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 tracking points with 50% alpha + # Draw previous and next tracking points with motion path visualization if not self.is_image_mode and self.tracking_points: - # Previous tracking point (red) - from the most recent frame with tracking points before current prev_result = self._get_previous_tracking_point() + next_result = self._get_next_tracking_point() + + # Draw motion path if we have both previous and next points + if prev_result and next_result: + prev_frame, prev_pts = prev_result + next_frame, next_pts = next_result + + # Draw lines between corresponding tracking points + for i, (prev_rx, prev_ry) in enumerate(prev_pts): + if i < len(next_pts): + next_rx, next_ry = next_pts[i] + prev_sx, prev_sy = self._map_rotated_to_screen(prev_rx, prev_ry) + next_sx, next_sy = self._map_rotated_to_screen(next_rx, next_ry) + + # Draw motion path line with arrow (thin and transparent) + overlay = canvas.copy() + cv2.line(overlay, (prev_sx, prev_sy), (next_sx, next_sy), (255, 255, 0), 1) # Thin yellow line + + # Draw arrow head pointing from previous to next + angle = np.arctan2(next_sy - prev_sy, next_sx - prev_sx) + arrow_length = 12 + arrow_angle = np.pi / 6 # 30 degrees + + # Calculate arrow head points + arrow_x1 = int(next_sx - arrow_length * np.cos(angle - arrow_angle)) + arrow_y1 = int(next_sy - arrow_length * np.sin(angle - arrow_angle)) + arrow_x2 = int(next_sx - arrow_length * np.cos(angle + arrow_angle)) + arrow_y2 = int(next_sy - arrow_length * np.sin(angle + arrow_angle)) + + cv2.line(overlay, (next_sx, next_sy), (arrow_x1, arrow_y1), (255, 255, 0), 1) + cv2.line(overlay, (next_sx, next_sy), (arrow_x2, arrow_y2), (255, 255, 0), 1) + cv2.addWeighted(overlay, 0.3, canvas, 0.7, 0, canvas) # Very transparent + + # Previous tracking point (red) - from the most recent frame with tracking points before current 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 + # Create overlay for alpha blending (more transparent) overlay = canvas.copy() - cv2.circle(overlay, (sx, sy), 4, (0, 0, 255), -1) # Red circle - cv2.addWeighted(overlay, 0.5, canvas, 0.5, 0, canvas) + cv2.circle(overlay, (sx, sy), 5, (0, 0, 255), -1) # Red circle + cv2.circle(overlay, (sx, sy), 5, (255, 255, 255), 1) # White border + cv2.addWeighted(overlay, 0.4, canvas, 0.6, 0, canvas) # More transparent - # Next tracking point (green) - from the next frame with tracking points after current - next_result = self._get_next_tracking_point() + # Next tracking point (magenta/purple) - from the next frame with tracking points after current 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 + # Create overlay for alpha blending (more transparent) overlay = canvas.copy() - cv2.circle(overlay, (sx, sy), 4, (0, 255, 0), -1) # Green circle - cv2.addWeighted(overlay, 0.5, canvas, 0.5, 0, canvas) + cv2.circle(overlay, (sx, sy), 5, (255, 0, 255), -1) # Magenta circle + cv2.circle(overlay, (sx, sy), 5, (255, 255, 255), 1) # White border + cv2.addWeighted(overlay, 0.4, canvas, 0.6, 0, canvas) # More transparent if self.tracking_enabled and not self.is_image_mode: interp = self._get_interpolated_tracking_position(self.current_frame) if interp: diff --git a/croppa/spec.md b/croppa/spec.md index dc4cd0e..dc6f7cb 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 the previous tracking point (red) and next tracking point (green) are shown regardless of which frame they're on +- **Display** Points are rendered as blue dots per frame, in addition the previous tracking point (red) and next tracking point (magenta) are shown with yellow arrows indicating motion direction #### Motion Tracking Navigation - **,**: Jump to previous tracking marker (previous frame that has one or more tracking points). Wrap-around supported.