Update .gitignore and enhance VideoEditor with improved crop handling and logging

This commit adds a new entry to the .gitignore file to exclude log files. In the VideoEditor class, it refines the crop position adjustment logic to calculate the center of the crop rectangle before applying offsets, ensuring more accurate positioning. Additionally, it enhances logging throughout the point transformation and tracking processes, providing better insights into the state of tracking points and their visibility relative to the crop area.
This commit is contained in:
2025-09-16 20:24:20 +02:00
parent c88c2cc354
commit 70364d0458
3 changed files with 80 additions and 57 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
__pycache__
croppa/build/lib
croppa/croppa.egg-info
*.log

View File

@@ -1120,8 +1120,18 @@ class VideoEditor:
# Only apply offset if it's not zero
if tracking_offset[0] != 0 or tracking_offset[1] != 0:
x += int(tracking_offset[0])
y += int(tracking_offset[1])
# Calculate the center of the crop rect
center_x = x + w // 2
center_y = y + h // 2
# Apply offset to center
new_center_x = center_x + int(tracking_offset[0])
new_center_y = center_y + int(tracking_offset[1])
# Calculate new top-left corner
x = new_center_x - w // 2
y = new_center_y - h // 2
print(f"apply_crop_zoom_and_rotation: adjusted crop position to ({x}, {y})")
x, y, w, h = int(x), int(y), int(w), int(h)
@@ -1192,7 +1202,6 @@ class VideoEditor:
x, y = float(point[0]), float(point[1])
# Log original point
print(f"transform_point: original point ({x}, {y})")
# Step 1: Apply crop (adjust point relative to crop origin)
@@ -1200,8 +1209,7 @@ class VideoEditor:
crop_x, crop_y, crop_w, crop_h = self.crop_rect
print(f"transform_point: crop_rect = {self.crop_rect}")
# Check if point is inside the crop area - but don't filter out points
# We'll still transform them and let the drawing code decide visibility
# Check if point is inside the crop area
is_inside = (crop_x <= x < crop_x + crop_w and crop_y <= y < crop_y + crop_h)
print(f"transform_point: point ({x}, {y}) is {'inside' if is_inside else 'outside'} crop area")
@@ -1214,10 +1222,11 @@ class VideoEditor:
if self.rotation_angle != 0:
# Get dimensions after crop
if self.crop_rect:
crop_w, crop_h = self.crop_rect[2], self.crop_rect[3]
crop_w, crop_h = float(self.crop_rect[2]), float(self.crop_rect[3])
else:
if self.current_display_frame is not None:
crop_h, crop_w = self.current_display_frame.shape[:2]
crop_h, crop_w = float(crop_h), float(crop_w)
else:
return None
@@ -1306,14 +1315,8 @@ class VideoEditor:
y += crop_y
print(f"untransform_point: after reverse crop ({x}, {y}), crop_rect = {self.crop_rect}")
# Ensure coordinates are within the frame bounds
if self.current_display_frame is not None:
height, width = self.current_display_frame.shape[:2]
orig_x, orig_y = x, y
x = max(0, min(width - 1, x))
y = max(0, min(height - 1, y))
if orig_x != x or orig_y != y:
print(f"untransform_point: clamped from ({orig_x}, {orig_y}) to ({x}, {y})")
# Don't clamp coordinates - allow points outside frame bounds
# This is important for tracking points that may be outside the current crop
print(f"untransform_point: final result = ({x}, {y})")
return (x, y)
@@ -1495,11 +1498,14 @@ class VideoEditor:
tracking_points = self.motion_tracker.get_tracking_points_for_frame(self.current_frame)
print(f"draw_tracking_points: found {len(tracking_points)} tracking points for frame {self.current_frame}")
# Get current frame dimensions for bounds checking
frame_height, frame_width = self.current_display_frame.shape[:2]
for i, point in enumerate(tracking_points):
print(f"draw_tracking_points: processing point {i}: {point}")
# Transform point from original frame coordinates to display coordinates
display_point = self.transform_point(point)
# Check if the point is within the frame bounds
is_in_frame = (0 <= point[0] < frame_width and 0 <= point[1] < frame_height)
# Check if the point is within the crop area (if cropping is active)
is_in_crop = True
@@ -1508,7 +1514,10 @@ class VideoEditor:
is_in_crop = (crop_x <= point[0] < crop_x + crop_w and
crop_y <= point[1] < crop_y + crop_h)
if display_point:
# Transform point from original frame coordinates to display coordinates
display_point = self.transform_point(point)
if display_point is not None:
print(f"draw_tracking_points: point {i} transformed to {display_point}")
# Scale and offset the point to match the canvas
@@ -1517,19 +1526,25 @@ class VideoEditor:
print(f"draw_tracking_points: point {i} canvas position: ({x},{y})")
# Draw the point - use different colors based on whether it's in the crop area
if is_in_crop:
# Point is in crop area - draw normally
# Draw white border
cv2.circle(canvas, (x, y), self.tracking_point_radius + 2, (255, 255, 255), 2)
# Draw green circle
cv2.circle(canvas, (x, y), self.tracking_point_radius, (0, 255, 0), -1)
# Check if the point is within the canvas bounds
is_on_canvas = (0 <= x < self.window_width and 0 <= y < self.window_height)
if is_on_canvas:
# Draw the point - use different colors based on whether it's in the crop area
if is_in_crop:
# Point is in crop area - draw normally
# Draw white border
cv2.circle(canvas, (x, y), self.tracking_point_radius + 2, (255, 255, 255), 2)
# Draw green circle
cv2.circle(canvas, (x, y), self.tracking_point_radius, (0, 255, 0), -1)
else:
# Point is outside crop area - draw with different color
# Draw gray border
cv2.circle(canvas, (x, y), self.tracking_point_radius + 2, (128, 128, 128), 2)
# Draw yellow circle
cv2.circle(canvas, (x, y), self.tracking_point_radius, (0, 255, 255), -1)
else:
# Point is outside crop area - draw with different color (semi-transparent)
# Draw gray border
cv2.circle(canvas, (x, y), self.tracking_point_radius + 2, (128, 128, 128), 2)
# Draw yellow circle
cv2.circle(canvas, (x, y), self.tracking_point_radius, (0, 255, 255), -1)
print(f"draw_tracking_points: point {i} is outside canvas bounds")
else:
print(f"draw_tracking_points: point {i} not visible in current view")
@@ -2112,6 +2127,8 @@ class VideoEditor:
start_x = (self.window_width - final_display_width) // 2
start_y = (available_height - final_display_height) // 2
print(f"mouse_callback: right-click at ({x}, {y}), display frame at ({start_x}, {start_y}), scale={scale}")
# Check if click is within the frame area
if (start_x <= x < start_x + final_display_width and
start_y <= y < start_y + final_display_height):
@@ -2120,9 +2137,13 @@ class VideoEditor:
display_x = (x - start_x) / scale
display_y = (y - start_y) / scale
print(f"mouse_callback: converted to display coords ({display_x}, {display_y})")
# Now convert display coordinates to original frame coordinates
original_point = self.untransform_point((display_x, display_y))
print(f"mouse_callback: untransformed to original coords {original_point}")
if original_point:
# Check if clicking on an existing tracking point to remove it
removed = self.motion_tracker.remove_tracking_point(
@@ -2132,13 +2153,16 @@ class VideoEditor:
self.tracking_point_distance
)
if not removed:
if removed:
print(f"mouse_callback: removed tracking point at {original_point}")
else:
# If no point was removed, add a new tracking point
self.motion_tracker.add_tracking_point(
self.current_frame,
original_point[0],
original_point[1]
)
print(f"mouse_callback: added tracking point at {original_point}")
# Save state when tracking points change
self.save_state()
@@ -3115,7 +3139,6 @@ class VideoEditor:
if self.motion_tracker.tracking_enabled:
self.motion_tracker.stop_tracking()
print("Motion tracking disabled")
print("Motion tracking disabled")
else:
# If we have tracking points, start tracking
if self.motion_tracker.has_tracking_points():
@@ -3123,33 +3146,28 @@ class VideoEditor:
current_pos = self.motion_tracker.get_interpolated_position(self.current_frame)
print(f"Toggle tracking: interpolated position = {current_pos}")
# Always use the current position as the base zoom center if available
if current_pos:
base_zoom_center = current_pos
print(f"Toggle tracking: using interpolated position as base: {base_zoom_center}")
# Use crop center if we have a crop rect
if self.crop_rect:
elif self.crop_rect:
x, y, w, h = self.crop_rect
crop_center = (x + w//2, y + h//2)
print(f"Toggle tracking: crop_rect = {self.crop_rect}, crop_center = {crop_center}")
# If we have a current position from tracking points, use that as base
if current_pos:
# The base zoom center is the current position
base_zoom_center = current_pos
print(f"Toggle tracking: using interpolated position as base: {base_zoom_center}")
else:
# Use crop center as fallback
base_zoom_center = crop_center
print(f"Toggle tracking: using crop center as base: {base_zoom_center}")
base_zoom_center = (x + w//2, y + h//2)
print(f"Toggle tracking: using crop center as base: {base_zoom_center}")
# No crop rect, use frame center
elif self.current_display_frame is not None:
h, w = self.current_display_frame.shape[:2]
base_zoom_center = (w // 2, h // 2)
print(f"Toggle tracking: using frame center as base: {base_zoom_center}")
else:
# No crop rect, use frame center
if self.current_display_frame is not None:
h, w = self.current_display_frame.shape[:2]
base_zoom_center = (w // 2, h // 2)
print(f"Toggle tracking: using frame center as base: {base_zoom_center}")
else:
base_zoom_center = None
print("Toggle tracking: no base center available")
base_zoom_center = None
print("Toggle tracking: no base center available")
# Use current crop rect as base
base_crop_rect = self.crop_rect
# Create a crop rect if one doesn't exist
base_crop_rect = self.crop_rect
if not base_crop_rect and current_pos and self.current_display_frame is not None:
# Create a default crop rect centered on the current position
h, w = self.current_display_frame.shape[:2]
@@ -3157,6 +3175,8 @@ class VideoEditor:
x = max(0, int(current_pos[0] - crop_size // 2))
y = max(0, int(current_pos[1] - crop_size // 2))
base_crop_rect = (x, y, crop_size, crop_size)
# Update the actual crop rect
self.crop_rect = base_crop_rect
print(f"Toggle tracking: created default crop rect: {base_crop_rect}")
self.motion_tracker.start_tracking(
@@ -3164,10 +3184,8 @@ class VideoEditor:
base_zoom_center
)
print("Motion tracking enabled")
print(f"Motion tracking enabled with base_crop_rect={base_crop_rect}, base_zoom_center={base_zoom_center}")
else:
print("No tracking points available. Add tracking points with right-click first.")
print("Motion tracking not enabled - no tracking points available")
self.save_state()
else: # V - Clear all tracking points
self.motion_tracker.clear_tracking_points()

View File

@@ -137,12 +137,16 @@ class MotionTracker:
self.tracking_enabled = True
self.base_crop_rect = base_crop_rect
print(f"start_tracking: base_crop_rect={base_crop_rect}, base_zoom_center={base_zoom_center}")
# If no base_zoom_center is provided, use the center of the crop rect
if base_zoom_center is None and base_crop_rect is not None:
x, y, w, h = base_crop_rect
self.base_zoom_center = (x + w//2, y + h//2)
print(f"start_tracking: using crop center as base_zoom_center: {self.base_zoom_center}")
else:
self.base_zoom_center = base_zoom_center
print(f"start_tracking: using provided base_zoom_center: {self.base_zoom_center}")
def stop_tracking(self):
"""Stop motion tracking"""