Enhance VideoEditor and MotionTracker with improved logging and crop handling
This commit adds detailed logging to the VideoEditor and MotionTracker classes, providing insights into the transformations and adjustments made during point processing and cropping. It refines the crop position adjustment logic to ensure offsets are only applied when necessary and enhances the visibility of tracking points based on their position relative to the crop area. Additionally, it improves the handling of motion tracking toggling, ensuring a default crop rect is created when needed, thus enhancing the overall user experience during video editing.
This commit is contained in:
118
croppa/main.py
118
croppa/main.py
@@ -1116,17 +1116,32 @@ class VideoEditor:
|
||||
# Apply tracking offset to crop position if motion tracking is enabled
|
||||
if self.motion_tracker.tracking_enabled:
|
||||
tracking_offset = self.motion_tracker.get_tracking_offset(self.current_frame)
|
||||
x += int(tracking_offset[0])
|
||||
y += int(tracking_offset[1])
|
||||
print(f"apply_crop_zoom_and_rotation: tracking_offset = {tracking_offset}")
|
||||
|
||||
# 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])
|
||||
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)
|
||||
print(f"apply_crop_zoom_and_rotation: final crop = ({x}, {y}, {w}, {h})")
|
||||
|
||||
# Ensure crop is within frame bounds
|
||||
orig_x, orig_y = x, y
|
||||
x = max(0, min(x, processed_frame.shape[1] - 1))
|
||||
y = max(0, min(y, processed_frame.shape[0] - 1))
|
||||
w = min(w, processed_frame.shape[1] - x)
|
||||
h = min(h, processed_frame.shape[0] - y)
|
||||
|
||||
if orig_x != x or orig_y != y:
|
||||
print(f"apply_crop_zoom_and_rotation: crop adjusted from ({orig_x}, {orig_y}) to ({x}, {y}) to stay in bounds")
|
||||
|
||||
if w > 0 and h > 0:
|
||||
processed_frame = processed_frame[y : y + h, x : x + w]
|
||||
print(f"apply_crop_zoom_and_rotation: crop applied, new shape = {processed_frame.shape}")
|
||||
else:
|
||||
print(f"apply_crop_zoom_and_rotation: invalid crop dimensions, skipping crop")
|
||||
|
||||
# Apply rotation
|
||||
if self.rotation_angle != 0:
|
||||
@@ -1177,18 +1192,23 @@ 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)
|
||||
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 - but don't filter out points
|
||||
# We'll still transform them and let the drawing code decide visibility
|
||||
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")
|
||||
|
||||
# Check if point is inside the crop area
|
||||
if not (crop_x <= x < crop_x + crop_w and crop_y <= y < crop_y + crop_h):
|
||||
# Point is outside the crop area
|
||||
return None
|
||||
|
||||
# Adjust coordinates relative to crop origin
|
||||
x -= crop_x
|
||||
y -= crop_y
|
||||
print(f"transform_point: after crop adjustment ({x}, {y})")
|
||||
|
||||
# Step 2: Apply rotation
|
||||
if self.rotation_angle != 0:
|
||||
@@ -1201,6 +1221,8 @@ class VideoEditor:
|
||||
else:
|
||||
return None
|
||||
|
||||
print(f"transform_point: rotation_angle = {self.rotation_angle}, dimensions = ({crop_w}, {crop_h})")
|
||||
|
||||
# Apply rotation to coordinates
|
||||
if self.rotation_angle == 90:
|
||||
# 90° clockwise: (x,y) -> (y, width-x)
|
||||
@@ -1217,11 +1239,15 @@ class VideoEditor:
|
||||
new_y = x
|
||||
x, y = new_x, new_y
|
||||
|
||||
print(f"transform_point: after rotation ({x}, {y})")
|
||||
|
||||
# Step 3: Apply zoom
|
||||
if self.zoom_factor != 1.0:
|
||||
x *= self.zoom_factor
|
||||
y *= self.zoom_factor
|
||||
print(f"transform_point: after zoom ({x}, {y}), zoom_factor = {self.zoom_factor}")
|
||||
|
||||
print(f"transform_point: final result = ({x}, {y})")
|
||||
return (x, y)
|
||||
|
||||
def untransform_point(self, point: Tuple[float, float]) -> Tuple[float, float]:
|
||||
@@ -1236,11 +1262,13 @@ class VideoEditor:
|
||||
return None
|
||||
|
||||
x, y = float(point[0]), float(point[1])
|
||||
print(f"untransform_point: original display point ({x}, {y})")
|
||||
|
||||
# Step 1: Reverse zoom
|
||||
if self.zoom_factor != 1.0:
|
||||
x /= self.zoom_factor
|
||||
y /= self.zoom_factor
|
||||
print(f"untransform_point: after reverse zoom ({x}, {y}), zoom_factor = {self.zoom_factor}")
|
||||
|
||||
# Step 2: Reverse rotation
|
||||
if self.rotation_angle != 0:
|
||||
@@ -1251,6 +1279,8 @@ class VideoEditor:
|
||||
crop_h, crop_w = self.current_display_frame.shape[:2]
|
||||
crop_h, crop_w = float(crop_h), float(crop_w)
|
||||
|
||||
print(f"untransform_point: rotation_angle = {self.rotation_angle}, dimensions = ({crop_w}, {crop_h})")
|
||||
|
||||
# Apply inverse rotation to coordinates
|
||||
if self.rotation_angle == 90:
|
||||
# Reverse 90° clockwise: (x,y) -> (width-y, x)
|
||||
@@ -1267,18 +1297,25 @@ class VideoEditor:
|
||||
new_y = crop_h - x
|
||||
x, y = new_x, new_y
|
||||
|
||||
print(f"untransform_point: after reverse rotation ({x}, {y})")
|
||||
|
||||
# Step 3: Reverse crop (add crop offset)
|
||||
if self.crop_rect:
|
||||
crop_x, crop_y = float(self.crop_rect[0]), float(self.crop_rect[1])
|
||||
x += crop_x
|
||||
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})")
|
||||
|
||||
print(f"untransform_point: final result = ({x}, {y})")
|
||||
return (x, y)
|
||||
|
||||
|
||||
@@ -1452,32 +1489,67 @@ class VideoEditor:
|
||||
if self.current_frame is None:
|
||||
return
|
||||
|
||||
print(f"draw_tracking_points: offset=({offset_x},{offset_y}), scale={scale}")
|
||||
|
||||
# Draw tracking points for the current frame (green circles with white border)
|
||||
tracking_points = self.motion_tracker.get_tracking_points_for_frame(self.current_frame)
|
||||
for point in tracking_points:
|
||||
print(f"draw_tracking_points: found {len(tracking_points)} tracking points for frame {self.current_frame}")
|
||||
|
||||
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 crop area (if cropping is active)
|
||||
is_in_crop = True
|
||||
if self.crop_rect:
|
||||
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)
|
||||
|
||||
if display_point:
|
||||
print(f"draw_tracking_points: point {i} transformed to {display_point}")
|
||||
|
||||
# Scale and offset the point to match the canvas
|
||||
x = int(offset_x + display_point[0] * scale)
|
||||
y = int(offset_y + display_point[1] * scale)
|
||||
|
||||
# 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)
|
||||
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)
|
||||
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)
|
||||
else:
|
||||
print(f"draw_tracking_points: point {i} not visible in current view")
|
||||
|
||||
# Draw computed tracking position (blue cross) if tracking is enabled
|
||||
if self.motion_tracker.tracking_enabled:
|
||||
interpolated_pos = self.motion_tracker.get_interpolated_position(self.current_frame)
|
||||
print(f"draw_tracking_points: interpolated position: {interpolated_pos}")
|
||||
|
||||
if interpolated_pos:
|
||||
# Transform point from original frame coordinates to display coordinates
|
||||
display_point = self.transform_point(interpolated_pos)
|
||||
print(f"draw_tracking_points: interpolated display point: {display_point}")
|
||||
|
||||
if display_point:
|
||||
# Scale and offset the point to match the canvas
|
||||
x = int(offset_x + display_point[0] * scale)
|
||||
y = int(offset_y + display_point[1] * scale)
|
||||
|
||||
print(f"draw_tracking_points: interpolated canvas position: ({x},{y})")
|
||||
|
||||
# Draw blue cross
|
||||
cross_size = 10
|
||||
cv2.line(canvas, (x - cross_size, y), (x + cross_size, y), (255, 0, 0), 2)
|
||||
@@ -3043,39 +3115,59 @@ 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():
|
||||
# Get the current interpolated position to use as base
|
||||
current_pos = self.motion_tracker.get_interpolated_position(self.current_frame)
|
||||
print(f"Toggle tracking: interpolated position = {current_pos}")
|
||||
|
||||
# Use crop center if we have a crop rect
|
||||
if 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}")
|
||||
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")
|
||||
|
||||
# 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]
|
||||
crop_size = min(w, h) // 2 # Use half of the smaller dimension
|
||||
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)
|
||||
print(f"Toggle tracking: created default crop rect: {base_crop_rect}")
|
||||
|
||||
self.motion_tracker.start_tracking(
|
||||
self.crop_rect,
|
||||
base_crop_rect,
|
||||
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()
|
||||
|
Reference in New Issue
Block a user