Refine VideoEditor point transformation methods with enhanced consistency and logging

This commit improves the point transformation and untransformation methods in the VideoEditor class by ensuring they match the applied transformations precisely. It adds checks for null points and current display frames, enhancing robustness. Additionally, detailed logging has been introduced to track coordinate adjustments during cropping and transformations, aiding in debugging and ensuring consistent behavior across the coordinate mapping process.
This commit is contained in:
2025-09-16 20:58:54 +02:00
parent 2979dca40a
commit 33a553c092

View File

@@ -1206,30 +1206,25 @@ class VideoEditor:
2. Rotation
3. Zoom
The key insight is that we need to handle coordinates in the same way
the frame is processed in apply_crop_zoom_and_rotation.
IMPORTANT: This function must exactly match the transformations applied in
apply_crop_zoom_and_rotation to ensure consistent coordinate mapping.
"""
if point is None:
if point is None or self.current_display_frame is None:
return None
# Get original coordinates and convert to float for precise calculations
orig_x, orig_y = float(point[0]), float(point[1])
print(f"transform_point: original point ({orig_x}, {orig_y})")
# Get frame dimensions
if self.current_display_frame is None:
return None
# Get original frame dimensions
frame_height, frame_width = self.current_display_frame.shape[:2]
# STEP 1: Apply crop
# If we're cropped, check if the point is within the crop area
# STEP 1: Apply crop - adjust coordinates 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}")
# Check if point is inside the crop area
# Check if point is inside the crop area (for debugging only)
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")
@@ -1237,13 +1232,11 @@ class VideoEditor:
x -= crop_x
y -= crop_y
# If point is outside crop area, it will have negative coordinates or coordinates > crop dimensions
# We'll still transform it for consistent behavior
print(f"transform_point: after crop adjustment ({x}, {y})")
# Update dimensions for rotation calculations
frame_width, frame_height = crop_w, crop_h
print(f"transform_point: after crop adjustment ({x}, {y})")
# STEP 2: Apply rotation
if self.rotation_angle != 0:
print(f"transform_point: rotation_angle = {self.rotation_angle}, dimensions = ({frame_width}, {frame_height})")
@@ -1283,8 +1276,8 @@ class VideoEditor:
2. Reverse rotation
3. Reverse crop
The key insight is that we need to handle coordinates in the exact reverse
order as they are processed in apply_crop_zoom_and_rotation.
IMPORTANT: This function must exactly reverse the transformations applied in
transform_point to ensure consistent coordinate mapping.
"""
if point is None or self.current_display_frame is None:
return None
@@ -1293,10 +1286,10 @@ class VideoEditor:
display_x, display_y = float(point[0]), float(point[1])
print(f"untransform_point: original display point ({display_x}, {display_y})")
# Get frame dimensions
# Get original frame dimensions
orig_frame_height, orig_frame_width = self.current_display_frame.shape[:2]
# Get dimensions of the frame after crop (if any)
# Get dimensions for rotation calculations
if self.crop_rect:
frame_width, frame_height = float(self.crop_rect[2]), float(self.crop_rect[3])
else:
@@ -1338,7 +1331,7 @@ class VideoEditor:
y += crop_y
print(f"untransform_point: after reverse crop ({x}, {y}), crop_rect = {self.crop_rect}")
# Ensure the point is within the original frame bounds
# Clamp coordinates to frame bounds to ensure they're valid
x = max(0, min(x, orig_frame_width - 1))
y = max(0, min(y, orig_frame_height - 1))
@@ -2192,17 +2185,23 @@ class VideoEditor:
start_y <= y < start_y + final_display_height):
# Convert screen coordinates to display frame coordinates
# This is critical - we need to account for the canvas offset and scale
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
# This is where the magic happens - we need to reverse all transformations
original_point = self.untransform_point((display_x, display_y))
print(f"mouse_callback: untransformed to original coords {original_point}")
if original_point:
# Store the original frame dimensions for reference
frame_height, frame_width = self.current_display_frame.shape[:2]
print(f"mouse_callback: frame dimensions: {frame_width}x{frame_height}")
# Check if clicking on an existing tracking point to remove it
removed = self.motion_tracker.remove_tracking_point(
self.current_frame,
@@ -2223,20 +2222,24 @@ class VideoEditor:
print(f"mouse_callback: added tracking point at {original_point}")
# Perform a round-trip verification to ensure our coordinate system is consistent
# This will help debug any issues with the transformation
verification_point = self.transform_point(original_point)
if verification_point:
print(f"mouse_callback: verification - point transforms back to {verification_point}")
# Calculate expected canvas position for verification
expected_x = int(offset_x + verification_point[0] * scale)
expected_y = int(offset_y + verification_point[1] * scale)
expected_x = int(start_x + verification_point[0] * scale)
expected_y = int(start_y + verification_point[1] * scale)
print(f"mouse_callback: verification - expected canvas position: ({expected_x}, {expected_y}), actual: ({x}, {y})")
# Calculate the error between click and expected position
error_x = abs(expected_x - x)
error_y = abs(expected_y - y)
print(f"mouse_callback: verification - position error: ({error_x}, {error_y}) pixels")
# If error is significant, print a warning
if error_x > 2 or error_y > 2:
print(f"WARNING: Significant coordinate transformation error detected!")
print(f"This may indicate a problem with the transform/untransform functions.")
# Save state when tracking points change
self.save_state()