Enhance VideoEditor with improved point transformation, bounds checking, and debugging features

This commit refines the point transformation methods in the VideoEditor class, ensuring coordinates are validated against frame dimensions and crop areas. It adds detailed logging for point transformations and mouse interactions, improving visibility into the state of tracking points. Additionally, it introduces debug features for visualizing crop rectangles and point indices, enhancing the debugging experience during video editing.
This commit is contained in:
2025-09-16 20:32:33 +02:00
parent 70364d0458
commit cb097c55f1
2 changed files with 81 additions and 19 deletions

View File

@@ -1200,11 +1200,22 @@ class VideoEditor:
if point is None:
return None
x, y = float(point[0]), float(point[1])
orig_x, orig_y = float(point[0]), float(point[1])
print(f"transform_point: original point ({orig_x}, {orig_y})")
print(f"transform_point: original point ({x}, {y})")
# Get frame dimensions
if self.current_display_frame is None:
return None
frame_height, frame_width = self.current_display_frame.shape[:2]
# Step 1: Check if point is within frame bounds
if not (0 <= orig_x < frame_width and 0 <= orig_y < frame_height):
print(f"transform_point: point is outside frame bounds ({frame_width}x{frame_height})")
# We'll still transform it, but it might not be visible
# Step 1: Apply crop (adjust point relative to crop origin)
x, y = orig_x, orig_y
if self.crop_rect:
crop_x, crop_y, crop_w, crop_h = self.crop_rect
print(f"transform_point: crop_rect = {self.crop_rect}")
@@ -1224,12 +1235,8 @@ class VideoEditor:
if self.crop_rect:
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
crop_h, crop_w = float(frame_height), float(frame_width)
print(f"transform_point: rotation_angle = {self.rotation_angle}, dimensions = ({crop_w}, {crop_h})")
# Apply rotation to coordinates
@@ -1270,10 +1277,14 @@ class VideoEditor:
if point is None or self.current_display_frame is None:
return None
x, y = float(point[0]), float(point[1])
print(f"untransform_point: original display point ({x}, {y})")
display_x, display_y = float(point[0]), float(point[1])
print(f"untransform_point: original display point ({display_x}, {display_y})")
# Get frame dimensions
frame_height, frame_width = self.current_display_frame.shape[:2]
# Step 1: Reverse zoom
x, y = display_x, display_y
if self.zoom_factor != 1.0:
x /= self.zoom_factor
y /= self.zoom_factor
@@ -1285,8 +1296,7 @@ class VideoEditor:
if self.crop_rect:
crop_w, crop_h = float(self.crop_rect[2]), float(self.crop_rect[3])
else:
crop_h, crop_w = self.current_display_frame.shape[:2]
crop_h, crop_w = float(crop_h), float(crop_w)
crop_h, crop_w = float(frame_height), float(frame_width)
print(f"untransform_point: rotation_angle = {self.rotation_angle}, dimensions = ({crop_w}, {crop_h})")
@@ -1314,9 +1324,11 @@ class VideoEditor:
x += crop_x
y += crop_y
print(f"untransform_point: after reverse crop ({x}, {y}), crop_rect = {self.crop_rect}")
# Don't clamp coordinates - allow points outside frame bounds
# This is important for tracking points that may be outside the current crop
# Check if the resulting point is within frame bounds
if not (0 <= x < frame_width and 0 <= y < frame_height):
print(f"untransform_point: result is outside frame bounds ({frame_width}x{frame_height})")
# We'll still return it, but it might not be visible
print(f"untransform_point: final result = ({x}, {y})")
return (x, y)
@@ -1494,18 +1506,46 @@ class VideoEditor:
print(f"draw_tracking_points: offset=({offset_x},{offset_y}), scale={scale}")
# Draw tracking points for the current frame (green circles with white border)
# Draw tracking points for the current frame
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]
# Draw coordinate axes for debugging (if in debug mode)
debug_mode = True
if debug_mode and self.crop_rect:
# Draw crop rectangle outline on the canvas
crop_x, crop_y, crop_w, crop_h = self.crop_rect
# Transform the crop corners to display coordinates
top_left = self.transform_point((crop_x, crop_y))
top_right = self.transform_point((crop_x + crop_w, crop_y))
bottom_left = self.transform_point((crop_x, crop_y + crop_h))
bottom_right = self.transform_point((crop_x + crop_w, crop_y + crop_h))
# Draw crop outline if all corners are visible
if all([top_left, top_right, bottom_left, bottom_right]):
# Convert to canvas coordinates
tl_x, tl_y = int(offset_x + top_left[0] * scale), int(offset_y + top_left[1] * scale)
tr_x, tr_y = int(offset_x + top_right[0] * scale), int(offset_y + top_right[1] * scale)
bl_x, bl_y = int(offset_x + bottom_left[0] * scale), int(offset_y + bottom_left[1] * scale)
br_x, br_y = int(offset_x + bottom_right[0] * scale), int(offset_y + bottom_right[1] * scale)
# Draw crop outline
cv2.line(canvas, (tl_x, tl_y), (tr_x, tr_y), (255, 0, 255), 1)
cv2.line(canvas, (tr_x, tr_y), (br_x, br_y), (255, 0, 255), 1)
cv2.line(canvas, (br_x, br_y), (bl_x, bl_y), (255, 0, 255), 1)
cv2.line(canvas, (bl_x, bl_y), (tl_x, tl_y), (255, 0, 255), 1)
# Process each tracking point
for i, point in enumerate(tracking_points):
print(f"draw_tracking_points: processing point {i}: {point}")
# Check if the point is within the frame bounds
is_in_frame = (0 <= point[0] < frame_width and 0 <= point[1] < frame_height)
print(f"draw_tracking_points: point {i} is {'inside' if is_in_frame else 'outside'} frame bounds")
# Check if the point is within the crop area (if cropping is active)
is_in_crop = True
@@ -1513,6 +1553,7 @@ class VideoEditor:
crop_x, crop_y, crop_w, crop_h = self.crop_rect
is_in_crop = (crop_x <= point[0] < crop_x + crop_w and
crop_y <= point[1] < crop_y + crop_h)
print(f"draw_tracking_points: point {i} is {'inside' if is_in_crop else 'outside'} crop area")
# Transform point from original frame coordinates to display coordinates
display_point = self.transform_point(point)
@@ -1537,12 +1578,16 @@ class VideoEditor:
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)
# Draw point index for identification
cv2.putText(canvas, str(i), (x + 15, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 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)
# Draw point index for identification
cv2.putText(canvas, str(i), (x + 15, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
else:
print(f"draw_tracking_points: point {i} is outside canvas bounds")
else:
@@ -2127,7 +2172,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}")
print(f"mouse_callback: right-click at ({x}, {y}), canvas dimensions: {self.window_width}x{self.window_height}")
print(f"mouse_callback: display frame at ({start_x}, {start_y}), size: {final_display_width}x{final_display_height}, scale={scale}")
# Check if click is within the frame area
if (start_x <= x < start_x + final_display_width and
@@ -2145,6 +2191,17 @@ class VideoEditor:
print(f"mouse_callback: untransformed to original coords {original_point}")
if original_point:
# Get original frame dimensions for validation
frame_height, frame_width = self.current_display_frame.shape[:2]
# Ensure point is within the original frame bounds
x_clamped = max(0, min(frame_width - 1, original_point[0]))
y_clamped = max(0, min(frame_height - 1, original_point[1]))
if x_clamped != original_point[0] or y_clamped != original_point[1]:
print(f"mouse_callback: clamped point from {original_point} to ({x_clamped}, {y_clamped})")
original_point = (x_clamped, y_clamped)
# Check if clicking on an existing tracking point to remove it
removed = self.motion_tracker.remove_tracking_point(
self.current_frame,
@@ -2164,6 +2221,13 @@ class VideoEditor:
)
print(f"mouse_callback: added tracking point at {original_point}")
# Draw a debug marker at the exact point for visualization
if display_frame is not None:
# Transform the point back to display coordinates for verification
verification_point = self.transform_point(original_point)
if verification_point:
print(f"mouse_callback: verification - point transforms back to {verification_point}")
# Save state when tracking points change
self.save_state()
self.display_needs_update = True

View File

@@ -108,8 +108,6 @@ class MotionTracker:
def get_tracking_offset(self, frame_number: int) -> Tuple[float, float]:
"""Get the offset to center the crop on the tracked point"""
import logging
logger = logging.getLogger('croppa')
if not self.tracking_enabled:
print(f"get_tracking_offset: tracking not enabled, returning (0,0)")