import cv2 import logging from typing import Optional import numpy as np # Setup logging logging.basicConfig( filename="app.log", level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" ) # Global video capture object cap: Optional[cv2.VideoCapture] = None FRAME_SKIP: int = 5 current_frame_idx: int = 0 def preload_video(video_path: str) -> None: """ Load a video file for processing. Args: video_path: Path to the video file. Raises: RuntimeError: If the video cannot be loaded. """ global cap, current_frame_idx release_video() try: cap = cv2.VideoCapture(video_path) if not cap.isOpened(): raise RuntimeError(f"Failed to open video: {video_path}. Ensure the file exists and is a supported format (.mp4, .avi).") # Validate video properties total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) fps = cap.get(cv2.CAP_PROP_FPS) if total_frames <= 0: raise RuntimeError(f"Invalid video: {video_path}. No frames detected.") if fps <= 0: raise RuntimeError(f"Invalid video: {video_path}. Invalid FPS.") # Check video codec fourcc = int(cap.get(cv2.CAP_PROP_FOURCC)) codec = "".join([chr((fourcc >> 8 * i) & 0xFF) for i in range(4)]) log_message = (f"Loaded video: {video_path}, Total Frames: {total_frames}, " f"FPS: {fps}, Codec: {codec}") logging.info(log_message) current_frame_idx = 0 except Exception as e: logging.error(f"Error loading video {video_path}: {str(e)}") raise RuntimeError(f"Error loading video {video_path}: {str(e)}") def release_video() -> None: """ Release the video capture object. """ global cap if cap is not None: cap.release() cap = None def reset_video_index() -> None: """ Reset the video frame index to the beginning. """ global current_frame_idx if cap is not None and cap.isOpened(): cap.set(cv2.CAP_PROP_POS_FRAMES, 0) current_frame_idx = 0 logging.info("Reset video to start.") else: logging.warning("Cannot reset video: Video capture not initialized.") def get_next_video_frame() -> Optional[np.ndarray]: """ Retrieve the next frame from the video, skipping frames as specified. Returns: The next frame as a numpy array, or None if no frame is available. """ global cap, current_frame_idx if cap is None or not cap.isOpened(): logging.error("Video capture not initialized.") return None try: # Skip frames to reduce processing load previous_idx = current_frame_idx target_frame = current_frame_idx + FRAME_SKIP cap.set(cv2.CAP_PROP_POS_FRAMES, target_frame) success, frame = cap.read() if not success or frame is None: # Video ended, reset to start logging.info("Reached end of video, resetting to start.") reset_video_index() success, frame = cap.read() if not success or frame is None: logging.warning("Failed to retrieve frame after reset.") return None current_frame_idx = int(cap.get(cv2.CAP_PROP_POS_FRAMES)) logging.info(f"Retrieved frame {current_frame_idx}, skipped {current_frame_idx - previous_idx} frames") return frame except Exception as e: logging.error(f"Error retrieving frame: {str(e)}") return None