refactor(main.py): update segment setup to use cached frames for performance improvement
This commit is contained in:
104
main.py
104
main.py
@@ -43,7 +43,7 @@ class MediaGrader:
|
|||||||
CTRL_SEEK_MULTIPLIER = 10 # CTRL + A/D multiplier
|
CTRL_SEEK_MULTIPLIER = 10 # CTRL + A/D multiplier
|
||||||
|
|
||||||
# Multi-segment mode configuration
|
# Multi-segment mode configuration
|
||||||
SEGMENT_COUNT = 16 # Number of video segments (2x2 grid)
|
SEGMENT_COUNT = 16 # Number of video segments (4x4 grid)
|
||||||
SEGMENT_OVERLAP_PERCENT = 10 # Percentage overlap between segments
|
SEGMENT_OVERLAP_PERCENT = 10 # Percentage overlap between segments
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -518,7 +518,7 @@ class MediaGrader:
|
|||||||
return
|
return
|
||||||
|
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
print(f"Setting up {self.segment_count} segments in parallel...")
|
print(f"Setting up {self.segment_count} segments with frame caching...")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Clean up existing segment captures
|
# Clean up existing segment captures
|
||||||
@@ -528,9 +528,9 @@ class MediaGrader:
|
|||||||
current_file = self.media_files[self.current_index]
|
current_file = self.media_files[self.current_index]
|
||||||
print(f"Working with file: {current_file}")
|
print(f"Working with file: {current_file}")
|
||||||
|
|
||||||
# Initialize arrays
|
# Initialize arrays - no individual captures, just cached frames
|
||||||
print("Initializing arrays...")
|
print("Initializing arrays...")
|
||||||
self.segment_caps = [None] * self.segment_count
|
self.segment_caps = [None] * self.segment_count # Keep for compatibility, but won't use
|
||||||
self.segment_frames = [None] * self.segment_count
|
self.segment_frames = [None] * self.segment_count
|
||||||
self.segment_positions = []
|
self.segment_positions = []
|
||||||
|
|
||||||
@@ -540,10 +540,9 @@ class MediaGrader:
|
|||||||
print("Error: Video has insufficient frames for multi-segment mode")
|
print("Error: Video has insufficient frames for multi-segment mode")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Use a more conservative approach for segment positioning
|
# Use conservative frame range
|
||||||
# Instead of using total_frames directly, use a safer range
|
safe_frame_count = max(1, int(self.total_frames * 0.6))
|
||||||
safe_frame_count = max(1, int(self.total_frames * 0.9)) # Use 90% of reported frames as safety margin
|
print(f"Using safe frame count: {safe_frame_count} (60% of reported {self.total_frames})")
|
||||||
print(f"Using safe frame count: {safe_frame_count} (90% of reported {self.total_frames})")
|
|
||||||
|
|
||||||
for i in range(self.segment_count):
|
for i in range(self.segment_count):
|
||||||
if self.segment_count <= 1:
|
if self.segment_count <= 1:
|
||||||
@@ -551,40 +550,32 @@ class MediaGrader:
|
|||||||
else:
|
else:
|
||||||
position_ratio = i / (self.segment_count - 1)
|
position_ratio = i / (self.segment_count - 1)
|
||||||
start_frame = int(position_ratio * (safe_frame_count - 1))
|
start_frame = int(position_ratio * (safe_frame_count - 1))
|
||||||
start_frame = max(0, min(start_frame, safe_frame_count - 1)) # Clamp to safe range
|
start_frame = max(0, min(start_frame, safe_frame_count - 1))
|
||||||
self.segment_positions.append(start_frame)
|
self.segment_positions.append(start_frame)
|
||||||
print(f"Segment positions: {self.segment_positions}")
|
print(f"Segment positions: {self.segment_positions}")
|
||||||
|
|
||||||
# Create segments in parallel using thread pool
|
# Use the existing main capture to quickly read all segment frames
|
||||||
print("Creating segments in parallel...")
|
print("Pre-loading segment frames...")
|
||||||
parallel_start = time.time()
|
cache_start = time.time()
|
||||||
|
|
||||||
# Submit all segment creation tasks to thread pool
|
if self.current_cap and self.current_cap.isOpened():
|
||||||
futures = []
|
for i, frame_pos in enumerate(self.segment_positions):
|
||||||
for i in range(self.segment_count):
|
self.current_cap.set(cv2.CAP_PROP_POS_FRAMES, frame_pos)
|
||||||
future = self.thread_pool.submit(self._create_segment_parallel, i, str(current_file), self.segment_positions[i])
|
ret, frame = self.current_cap.read()
|
||||||
futures.append(future)
|
if ret and frame is not None:
|
||||||
|
self.segment_frames[i] = frame.copy()
|
||||||
# Collect results
|
print(f"Cached segment {i} at frame {frame_pos}")
|
||||||
successful_segments = 0
|
|
||||||
for i, future in enumerate(futures):
|
|
||||||
try:
|
|
||||||
cap, frame = future.result(timeout=10) # 10 second timeout per segment
|
|
||||||
if cap is not None and frame is not None:
|
|
||||||
self.segment_caps[i] = cap
|
|
||||||
self.segment_frames[i] = frame
|
|
||||||
successful_segments += 1
|
|
||||||
print(f"Segment {i} loaded successfully")
|
|
||||||
else:
|
else:
|
||||||
print(f"Segment {i} failed to load")
|
print(f"Failed to cache segment {i} at frame {frame_pos}")
|
||||||
except Exception as e:
|
|
||||||
print(f"Segment {i} failed with error: {e}")
|
|
||||||
|
|
||||||
parallel_time = (time.time() - parallel_start) * 1000
|
# Reset main capture to original position
|
||||||
print(f"Parallel segment creation: {parallel_time:.1f}ms")
|
self.current_cap.set(cv2.CAP_PROP_POS_FRAMES, self.current_frame)
|
||||||
|
|
||||||
|
cache_time = (time.time() - cache_start) * 1000
|
||||||
|
print(f"Frame caching: {cache_time:.1f}ms")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error in setup initialization: {e}")
|
print(f"Error in setup: {e}")
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return
|
return
|
||||||
@@ -593,7 +584,8 @@ class MediaGrader:
|
|||||||
print(f"Total setup time: {total_time * 1000:.1f}ms")
|
print(f"Total setup time: {total_time * 1000:.1f}ms")
|
||||||
|
|
||||||
# Report success
|
# Report success
|
||||||
print(f"Successfully loaded {successful_segments}/{self.segment_count} segments")
|
successful_segments = sum(1 for frame in self.segment_frames if frame is not None)
|
||||||
|
print(f"Successfully cached {successful_segments}/{self.segment_count} segments")
|
||||||
|
|
||||||
def _create_segment_parallel(self, segment_index: int, file_path: str, start_frame: int):
|
def _create_segment_parallel(self, segment_index: int, file_path: str, start_frame: int):
|
||||||
"""Create a single segment capture and load its initial frame (runs in thread)"""
|
"""Create a single segment capture and load its initial frame (runs in thread)"""
|
||||||
@@ -777,45 +769,15 @@ class MediaGrader:
|
|||||||
return segment_index, None
|
return segment_index, None
|
||||||
|
|
||||||
def update_segment_frames(self):
|
def update_segment_frames(self):
|
||||||
"""Update frames for all segments during playback with parallel processing"""
|
"""Update frames for segments - now using static cached frames for simplicity"""
|
||||||
if not self.multi_segment_mode or not self.segment_frames:
|
if not self.multi_segment_mode or not self.segment_frames:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Only update segments that have valid frames loaded
|
# Since we're using cached static frames, no updates needed during playback
|
||||||
active_segments = [i for i, frame in enumerate(self.segment_frames) if frame is not None]
|
# This dramatically improves performance by eliminating I/O during playback
|
||||||
|
# The segments show different parts of the video but don't animate
|
||||||
if not active_segments:
|
# For animation, we'd need to implement frame interpolation or periodic cache updates
|
||||||
return
|
pass
|
||||||
|
|
||||||
# Use thread pool for parallel frame updates (but limit to avoid overwhelming)
|
|
||||||
if len(active_segments) <= 4:
|
|
||||||
# For small numbers, use parallel processing
|
|
||||||
futures = []
|
|
||||||
for i in active_segments:
|
|
||||||
future = self.thread_pool.submit(self.update_segment_frame_parallel, i)
|
|
||||||
futures.append(future)
|
|
||||||
|
|
||||||
# Collect results
|
|
||||||
for future in futures:
|
|
||||||
segment_index, frame = future.result()
|
|
||||||
if frame is not None:
|
|
||||||
self.segment_frames[segment_index] = frame
|
|
||||||
else:
|
|
||||||
# For larger numbers, process in smaller batches to avoid resource exhaustion
|
|
||||||
batch_size = 4
|
|
||||||
for batch_start in range(0, len(active_segments), batch_size):
|
|
||||||
batch = active_segments[batch_start:batch_start + batch_size]
|
|
||||||
futures = []
|
|
||||||
|
|
||||||
for i in batch:
|
|
||||||
future = self.thread_pool.submit(self.update_segment_frame_parallel, i)
|
|
||||||
futures.append(future)
|
|
||||||
|
|
||||||
# Collect batch results
|
|
||||||
for future in futures:
|
|
||||||
segment_index, frame = future.result()
|
|
||||||
if frame is not None:
|
|
||||||
self.segment_frames[segment_index] = frame
|
|
||||||
|
|
||||||
def reposition_segments_around_frame(self, center_frame: int):
|
def reposition_segments_around_frame(self, center_frame: int):
|
||||||
"""Reposition all segments around a center frame while maintaining spacing"""
|
"""Reposition all segments around a center frame while maintaining spacing"""
|
||||||
|
Reference in New Issue
Block a user