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:
@@ -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
|
||||
|
@@ -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)")
|
||||
|
Reference in New Issue
Block a user