Compare commits
6 Commits
e6616ed1b1
...
10284dad81
Author | SHA1 | Date | |
---|---|---|---|
10284dad81 | |||
a2dc4a2186 | |||
5d76681ded | |||
f8acef2da4 | |||
65b80034cb | |||
5400592afd |
183
croppa/main.py
183
croppa/main.py
@@ -1637,13 +1637,17 @@ class VideoEditor:
|
||||
|
||||
def _get_interpolated_tracking_position(self, frame_number):
|
||||
"""Linear interpolation in ROTATED frame coords. Returns (rx, ry) or None."""
|
||||
# First try template matching if enabled (much better than optical flow)
|
||||
# Get base position from manual tracking points
|
||||
base_pos = self._get_manual_tracking_position(frame_number)
|
||||
|
||||
# Calculate offset from template matching if enabled
|
||||
template_offset = None
|
||||
if self.template_matching_enabled and self.tracking_template is not None:
|
||||
if self.current_display_frame is not None:
|
||||
# Use only the cropped region for much faster template matching
|
||||
if self.crop_rect:
|
||||
crop_x, crop_y, crop_w, crop_h = self.crop_rect
|
||||
# Extract only the cropped region
|
||||
# Extract only the cropped region from raw frame
|
||||
cropped_frame = self.current_display_frame[crop_y:crop_y+crop_h, crop_x:crop_x+crop_w]
|
||||
if cropped_frame is not None and cropped_frame.size > 0:
|
||||
# Track template in cropped frame (much faster!)
|
||||
@@ -1652,12 +1656,12 @@ class VideoEditor:
|
||||
center_x, center_y, confidence = result
|
||||
print(f"DEBUG: Template match found at ({center_x}, {center_y}) with confidence {confidence:.2f}")
|
||||
|
||||
# Map from cropped frame coordinates to rotated frame coordinates
|
||||
# Map from cropped frame coordinates to raw frame coordinates
|
||||
# Add crop offset back
|
||||
rot_x = center_x + crop_x
|
||||
rot_y = center_y + crop_y
|
||||
raw_x = center_x + crop_x
|
||||
raw_y = center_y + crop_y
|
||||
|
||||
return (rot_x, rot_y)
|
||||
template_offset = (raw_x, raw_y)
|
||||
else:
|
||||
# No crop - use full frame
|
||||
raw_frame = self.current_display_frame.copy()
|
||||
@@ -1667,22 +1671,11 @@ class VideoEditor:
|
||||
center_x, center_y, confidence = result
|
||||
print(f"DEBUG: Template match found at ({center_x}, {center_y}) with confidence {confidence:.2f}")
|
||||
|
||||
# Map from raw frame coordinates to rotated frame coordinates
|
||||
if self.rotation_angle == 90:
|
||||
rot_x = self.frame_height - center_y
|
||||
rot_y = center_x
|
||||
elif self.rotation_angle == 180:
|
||||
rot_x = self.frame_width - center_x
|
||||
rot_y = self.frame_height - center_y
|
||||
elif self.rotation_angle == 270:
|
||||
rot_x = center_y
|
||||
rot_y = self.frame_width - center_x
|
||||
else:
|
||||
rot_x, rot_y = center_x, center_y
|
||||
# Template matching returns coordinates in raw frame space
|
||||
template_offset = (center_x, center_y)
|
||||
|
||||
return (rot_x, rot_y)
|
||||
|
||||
# Fall back to feature tracking if enabled - but use smooth interpolation instead of averaging
|
||||
# Calculate offset from feature tracking if enabled
|
||||
feature_offset = None
|
||||
if self.feature_tracker.tracking_enabled:
|
||||
# Get the nearest frames with features for smooth interpolation
|
||||
feature_frames = sorted(self.feature_tracker.features.keys())
|
||||
@@ -1690,19 +1683,54 @@ class VideoEditor:
|
||||
# Find the two nearest frames for interpolation
|
||||
if frame_number <= feature_frames[0]:
|
||||
# Before first feature frame - use first frame
|
||||
return self._get_feature_center(feature_frames[0])
|
||||
feature_offset = self._get_feature_center(feature_frames[0])
|
||||
elif frame_number >= feature_frames[-1]:
|
||||
# After last feature frame - use last frame
|
||||
return self._get_feature_center(feature_frames[-1])
|
||||
feature_offset = self._get_feature_center(feature_frames[-1])
|
||||
else:
|
||||
# Between two feature frames - interpolate smoothly
|
||||
for i in range(len(feature_frames) - 1):
|
||||
if feature_frames[i] <= frame_number <= feature_frames[i + 1]:
|
||||
return self._interpolate_feature_positions(
|
||||
feature_offset = self._interpolate_feature_positions(
|
||||
feature_frames[i], feature_frames[i + 1], frame_number
|
||||
)
|
||||
break
|
||||
|
||||
# Fall back to manual tracking points
|
||||
# Combine tracking methods: average of all available positions
|
||||
positions = []
|
||||
|
||||
# Add manual tracking position
|
||||
if base_pos:
|
||||
positions.append(base_pos)
|
||||
print(f"DEBUG: Manual tracking: ({base_pos[0]:.1f}, {base_pos[1]:.1f})")
|
||||
|
||||
# Add template matching position
|
||||
if template_offset:
|
||||
positions.append(template_offset)
|
||||
print(f"DEBUG: Template matching: ({template_offset[0]:.1f}, {template_offset[1]:.1f})")
|
||||
|
||||
# Add feature tracking position
|
||||
if feature_offset:
|
||||
positions.append(feature_offset)
|
||||
print(f"DEBUG: Feature tracking: ({feature_offset[0]:.1f}, {feature_offset[1]:.1f})")
|
||||
|
||||
# Calculate average of all available positions
|
||||
if positions:
|
||||
avg_x = sum(pos[0] for pos in positions) / len(positions)
|
||||
avg_y = sum(pos[1] for pos in positions) / len(positions)
|
||||
print(f"DEBUG: Average of {len(positions)} positions: ({avg_x:.1f}, {avg_y:.1f})")
|
||||
return (avg_x, avg_y)
|
||||
|
||||
# Fall back to individual tracking methods if no base position
|
||||
if template_offset:
|
||||
return template_offset
|
||||
elif feature_offset:
|
||||
return feature_offset
|
||||
else:
|
||||
return None
|
||||
|
||||
def _get_manual_tracking_position(self, frame_number):
|
||||
"""Get manual tracking position for a frame"""
|
||||
if not self.tracking_points:
|
||||
return None
|
||||
frames = sorted(self.tracking_points.keys())
|
||||
@@ -1732,6 +1760,38 @@ class VideoEditor:
|
||||
return (x1 + t * (x2 - x1), y1 + t * (y2 - y1))
|
||||
return None
|
||||
|
||||
def _get_template_matching_position(self, frame_number):
|
||||
"""Get template matching position and confidence for a frame"""
|
||||
if not self.template_matching_enabled or self.tracking_template is None:
|
||||
return None
|
||||
|
||||
if self.current_display_frame is not None:
|
||||
# Use only the cropped region for much faster template matching
|
||||
if self.crop_rect:
|
||||
crop_x, crop_y, crop_w, crop_h = self.crop_rect
|
||||
# Extract only the cropped region from raw frame
|
||||
cropped_frame = self.current_display_frame[crop_y:crop_y+crop_h, crop_x:crop_x+crop_w]
|
||||
if cropped_frame is not None and cropped_frame.size > 0:
|
||||
# Track template in cropped frame (much faster!)
|
||||
result = self.track_template(cropped_frame)
|
||||
if result:
|
||||
center_x, center_y, confidence = result
|
||||
# Map from cropped frame coordinates to raw frame coordinates
|
||||
# Add crop offset back
|
||||
raw_x = center_x + crop_x
|
||||
raw_y = center_y + crop_y
|
||||
return (raw_x, raw_y, confidence)
|
||||
else:
|
||||
# No crop - use full frame
|
||||
raw_frame = self.current_display_frame.copy()
|
||||
if raw_frame is not None:
|
||||
result = self.track_template(raw_frame)
|
||||
if result:
|
||||
center_x, center_y, confidence = result
|
||||
return (center_x, center_y, confidence)
|
||||
|
||||
return None
|
||||
|
||||
def _get_display_params(self):
|
||||
"""Unified display transform parameters for current frame in rotated space."""
|
||||
eff_x, eff_y, eff_w, eff_h = self._get_effective_crop_rect_for_frame(getattr(self, 'current_frame', 0))
|
||||
@@ -2231,37 +2291,35 @@ class VideoEditor:
|
||||
print(f"DEBUG: Setting template from region ({x}, {y}, {w}, {h})")
|
||||
|
||||
if self.current_display_frame is not None:
|
||||
# Apply transformations to get the display frame
|
||||
display_frame = self.apply_crop_zoom_and_rotation(self.current_display_frame)
|
||||
if display_frame is not None:
|
||||
# Map screen coordinates to display frame coordinates
|
||||
frame_height, frame_width = display_frame.shape[:2]
|
||||
available_height = self.window_height - (0 if self.is_image_mode else self.TIMELINE_HEIGHT)
|
||||
start_y = (available_height - frame_height) // 2
|
||||
start_x = (self.window_width - frame_width) // 2
|
||||
# Map screen coordinates to rotated frame coordinates (raw frame)
|
||||
# This is what we need for template matching during rendering
|
||||
rot_x, rot_y = self._map_screen_to_rotated(x, y)
|
||||
rot_x2, rot_y2 = self._map_screen_to_rotated(x + w, y + h)
|
||||
|
||||
# Convert screen coordinates to display frame coordinates
|
||||
display_x = x - start_x
|
||||
display_y = y - start_y
|
||||
display_w = w
|
||||
display_h = h
|
||||
# Calculate region in rotated frame coordinates
|
||||
raw_x = min(rot_x, rot_x2)
|
||||
raw_y = min(rot_y, rot_y2)
|
||||
raw_w = abs(rot_x2 - rot_x)
|
||||
raw_h = abs(rot_y2 - rot_y)
|
||||
|
||||
# Ensure region is within frame bounds
|
||||
if (display_x >= 0 and display_y >= 0 and
|
||||
display_x + display_w <= frame_width and
|
||||
display_y + display_h <= frame_height):
|
||||
print(f"DEBUG: Mapped to raw frame coordinates ({raw_x}, {raw_y}, {raw_w}, {raw_h})")
|
||||
|
||||
# Extract template from display frame
|
||||
template = display_frame[display_y:display_y+display_h, display_x:display_x+display_w]
|
||||
if template.size > 0:
|
||||
self.tracking_template = template.copy()
|
||||
self.template_region = (display_x, display_y, display_w, display_h)
|
||||
self.show_feedback_message(f"Template set from region ({display_w}x{display_h})")
|
||||
print(f"DEBUG: Template set with size {template.shape}")
|
||||
else:
|
||||
self.show_feedback_message("Template region too small")
|
||||
# Ensure region is within raw frame bounds
|
||||
if (raw_x >= 0 and raw_y >= 0 and
|
||||
raw_x + raw_w <= self.frame_width and
|
||||
raw_y + raw_h <= self.frame_height):
|
||||
|
||||
# Extract template from raw frame
|
||||
template = self.current_display_frame[raw_y:raw_y+raw_h, raw_x:raw_x+raw_w]
|
||||
if template.size > 0:
|
||||
self.tracking_template = template.copy()
|
||||
self.template_region = (raw_x, raw_y, raw_w, raw_h)
|
||||
self.show_feedback_message(f"Template set from region ({raw_w}x{raw_h})")
|
||||
print(f"DEBUG: Template set with size {template.shape}")
|
||||
else:
|
||||
self.show_feedback_message("Template region outside frame bounds")
|
||||
self.show_feedback_message("Template region too small")
|
||||
else:
|
||||
self.show_feedback_message("Template region outside frame bounds")
|
||||
|
||||
|
||||
def apply_rotation(self, frame):
|
||||
@@ -2931,6 +2989,23 @@ class VideoEditor:
|
||||
cv2.circle(canvas, (sx, sy), 4, (0, 255, 0), -1) # Green circles for features
|
||||
cv2.circle(canvas, (sx, sy), 4, (255, 255, 255), 1)
|
||||
|
||||
# Draw template matching point (blue circle with confidence)
|
||||
if (not self.is_image_mode and
|
||||
self.template_matching_enabled and
|
||||
self.tracking_template is not None):
|
||||
# Get template matching position for current frame
|
||||
template_pos = self._get_template_matching_position(self.current_frame)
|
||||
if template_pos:
|
||||
tx, ty, confidence = template_pos
|
||||
# Map to screen coordinates
|
||||
sx, sy = self._map_rotated_to_screen(tx, ty)
|
||||
# Draw blue circle for template matching
|
||||
cv2.circle(canvas, (sx, sy), 8, (255, 0, 255), -1) # Magenta circle for template
|
||||
cv2.circle(canvas, (sx, sy), 8, (255, 255, 255), 2)
|
||||
# Draw confidence text
|
||||
conf_text = f"{confidence:.2f}"
|
||||
cv2.putText(canvas, conf_text, (sx + 10, sy - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
|
||||
|
||||
# Draw selection rectangles for feature extraction/deletion
|
||||
if self.selective_feature_extraction_rect:
|
||||
x, y, w, h = self.selective_feature_extraction_rect
|
||||
@@ -3850,6 +3925,10 @@ class VideoEditor:
|
||||
if not ret:
|
||||
break
|
||||
|
||||
# Set current display frame for motion tracking during rendering
|
||||
self.current_display_frame = frame.copy()
|
||||
self.current_frame = start_frame + i
|
||||
|
||||
processed_frame = self._process_frame_for_render(frame, output_width, output_height, start_frame + i)
|
||||
if processed_frame is not None:
|
||||
if i == 0:
|
||||
|
Reference in New Issue
Block a user