feat(main.py): implement bisection navigation for videos
This commit is contained in:
101
main.py
101
main.py
@@ -84,6 +84,9 @@ class MediaGrader:
|
||||
self.current_watch_start = None # Frame where current viewing session started
|
||||
self.last_frame_position = 0 # Track last known frame position
|
||||
|
||||
# Bisection navigation tracking
|
||||
self.last_jump_position = {} # Dict[file_path: last_frame] for bisection reference
|
||||
|
||||
def find_media_files(self) -> List[Path]:
|
||||
"""Find all media files recursively in the directory"""
|
||||
media_files = []
|
||||
@@ -307,6 +310,9 @@ class MediaGrader:
|
||||
target_frame = sample_points[current_jump]
|
||||
target_frame = min(target_frame, self.total_frames - 1)
|
||||
|
||||
# Track last position for bisection
|
||||
self.last_jump_position[current_file] = self.current_frame
|
||||
|
||||
# Jump to the target frame
|
||||
self.current_cap.set(cv2.CAP_PROP_POS_FRAMES, target_frame)
|
||||
self.load_current_frame()
|
||||
@@ -320,6 +326,95 @@ class MediaGrader:
|
||||
print(f"Sample {current_jump + 1}/{len(sample_points)}: jumped to frame {target_frame} ({percentage:.1f}% through video)")
|
||||
return True
|
||||
|
||||
def bisect_backwards(self):
|
||||
"""Bisect backwards between last position and current position"""
|
||||
if not self.is_video(self.media_files[self.current_index]):
|
||||
return False
|
||||
|
||||
current_file = str(self.media_files[self.current_index])
|
||||
|
||||
if current_file not in self.last_jump_position:
|
||||
print("No previous position to bisect from. Use L first to establish a reference point.")
|
||||
return False
|
||||
|
||||
last_pos = self.last_jump_position[current_file]
|
||||
current_pos = self.current_frame
|
||||
|
||||
if last_pos == current_pos:
|
||||
print("Already at the same position as last jump.")
|
||||
return False
|
||||
|
||||
# Calculate midpoint
|
||||
if last_pos < current_pos:
|
||||
midpoint = (last_pos + current_pos) // 2
|
||||
else:
|
||||
midpoint = (current_pos + last_pos) // 2
|
||||
|
||||
# Update last position for further bisection
|
||||
self.last_jump_position[current_file] = current_pos
|
||||
|
||||
# Jump to midpoint
|
||||
self.current_cap.set(cv2.CAP_PROP_POS_FRAMES, midpoint)
|
||||
self.load_current_frame()
|
||||
|
||||
percentage = (midpoint / self.total_frames) * 100
|
||||
print(f"Bisected backwards to frame {midpoint} ({percentage:.1f}% through video)")
|
||||
return True
|
||||
|
||||
def bisect_forwards(self):
|
||||
"""Bisect forwards between current position and next sample point"""
|
||||
if not self.is_video(self.media_files[self.current_index]):
|
||||
return False
|
||||
|
||||
current_file = str(self.media_files[self.current_index])
|
||||
|
||||
# Get next sample point
|
||||
if not hasattr(self, 'jump_counters') or current_file not in self.jump_counters:
|
||||
print("No sampling started yet. Use L first to establish sample points.")
|
||||
return False
|
||||
|
||||
# Define same sampling strategy as L key
|
||||
segments = 8
|
||||
segment_size = self.total_frames // segments
|
||||
sample_points = [
|
||||
segment_size * 2, # 1/4 through
|
||||
segment_size * 4, # 1/2 through
|
||||
segment_size * 6, # 3/4 through
|
||||
segment_size * 1, # 1/8 through
|
||||
segment_size * 3, # 3/8 through
|
||||
segment_size * 5, # 5/8 through
|
||||
segment_size * 7, # 7/8 through
|
||||
0 # Beginning
|
||||
]
|
||||
|
||||
current_jump = self.jump_counters[current_file]
|
||||
|
||||
if current_jump >= len(sample_points):
|
||||
print("All sample points visited. No forward reference point.")
|
||||
return False
|
||||
|
||||
next_sample = sample_points[current_jump]
|
||||
next_sample = min(next_sample, self.total_frames - 1)
|
||||
current_pos = self.current_frame
|
||||
|
||||
# Calculate midpoint between current and next sample
|
||||
midpoint = (current_pos + next_sample) // 2
|
||||
|
||||
if midpoint == current_pos:
|
||||
print("Already at or very close to next sample point.")
|
||||
return False
|
||||
|
||||
# Update last position for further bisection
|
||||
self.last_jump_position[current_file] = current_pos
|
||||
|
||||
# Jump to midpoint
|
||||
self.current_cap.set(cv2.CAP_PROP_POS_FRAMES, midpoint)
|
||||
self.load_current_frame()
|
||||
|
||||
percentage = (midpoint / self.total_frames) * 100
|
||||
print(f"Bisected forwards to frame {midpoint} ({percentage:.1f}% through video)")
|
||||
return True
|
||||
|
||||
def display_current_frame(self):
|
||||
"""Display the current cached frame with overlays"""
|
||||
if self.current_display_frame is None:
|
||||
@@ -624,6 +719,8 @@ class MediaGrader:
|
||||
print(" P: Previous file")
|
||||
print(" U: Undo last grading action")
|
||||
print(" L: Sample video at key points (videos only)")
|
||||
print(" K: Bisect backwards from current position (videos only)")
|
||||
print(" J: Bisect forwards toward next sample (videos only)")
|
||||
print(" Q/ESC: Quit")
|
||||
|
||||
cv2.namedWindow("Media Grader", cv2.WINDOW_NORMAL)
|
||||
@@ -681,6 +778,10 @@ class MediaGrader:
|
||||
elif key == ord("l"):
|
||||
# Jump to largest unwatched region
|
||||
self.jump_to_unwatched_region()
|
||||
elif key == ord("j"):
|
||||
self.bisect_backwards()
|
||||
elif key == ord("k"):
|
||||
self.bisect_forwards()
|
||||
elif key in [ord("1"), ord("2"), ord("3"), ord("4"), ord("5")]:
|
||||
grade = int(chr(key))
|
||||
if not self.grade_media(grade):
|
||||
|
Reference in New Issue
Block a user