@dataclassclassVideoInfo:""" A class to store video information, including width, height, fps and total number of frames. Attributes: width (int): width of the video in pixels height (int): height of the video in pixels fps (int): frames per second of the video total_frames (Optional[int]): total number of frames in the video, default is None Examples: ```python import supervision as sv video_info = sv.VideoInfo.from_video_path(video_path=<SOURCE_VIDEO_FILE>) video_info # VideoInfo(width=3840, height=2160, fps=25, total_frames=538) video_info.resolution_wh # (3840, 2160) ``` """width:intheight:intfps:inttotal_frames:Optional[int]=None@classmethoddeffrom_video_path(cls,video_path:str)->VideoInfo:video=cv2.VideoCapture(video_path)ifnotvideo.isOpened():raiseException(f"Could not open video at {video_path}")width=int(video.get(cv2.CAP_PROP_FRAME_WIDTH))height=int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))fps=int(video.get(cv2.CAP_PROP_FPS))total_frames=int(video.get(cv2.CAP_PROP_FRAME_COUNT))video.release()returnVideoInfo(width,height,fps,total_frames)@propertydefresolution_wh(self)->Tuple[int,int]:returnself.width,self.height
classVideoSink:""" Context manager that saves video frames to a file using OpenCV. Attributes: target_path (str): The path to the output file where the video will be saved. video_info (VideoInfo): Information about the video resolution, fps, and total frame count. codec (str): FOURCC code for video format Example: ```python import supervision as sv video_info = sv.VideoInfo.from_video_path(<SOURCE_VIDEO_PATH>) frames_generator = sv.get_video_frames_generator(<SOURCE_VIDEO_PATH>) with sv.VideoSink(target_path=<TARGET_VIDEO_PATH>, video_info=video_info) as sink: for frame in frames_generator: sink.write_frame(frame=frame) ``` """# noqa: E501 // docsdef__init__(self,target_path:str,video_info:VideoInfo,codec:str="mp4v"):self.target_path=target_pathself.video_info=video_infoself.__codec=codecself.__writer=Nonedef__enter__(self):try:self.__fourcc=cv2.VideoWriter_fourcc(*self.__codec)exceptTypeErrorase:print(str(e)+". Defaulting to mp4v...")self.__fourcc=cv2.VideoWriter_fourcc(*"mp4v")self.__writer=cv2.VideoWriter(self.target_path,self.__fourcc,self.video_info.fps,self.video_info.resolution_wh,)returnselfdefwrite_frame(self,frame:np.ndarray):""" Writes a single video frame to the target video file. Args: frame (np.ndarray): The video frame to be written to the file. The frame must be in BGR color format. """self.__writer.write(frame)def__exit__(self,exc_type,exc_value,exc_traceback):self.__writer.release()
defwrite_frame(self,frame:np.ndarray):""" Writes a single video frame to the target video file. Args: frame (np.ndarray): The video frame to be written to the file. The frame must be in BGR color format. """self.__writer.write(frame)
classFPSMonitor:""" A class for monitoring frames per second (FPS) to benchmark latency. """def__init__(self,sample_size:int=30):""" Args: sample_size (int): The maximum number of observations for latency benchmarking. Examples: ```python import supervision as sv frames_generator = sv.get_video_frames_generator(source_path=<SOURCE_FILE_PATH>) fps_monitor = sv.FPSMonitor() for frame in frames_generator: # your processing code here fps_monitor.tick() fps = fps_monitor.fps ``` """# noqa: E501 // docsself.all_timestamps=deque(maxlen=sample_size)@propertydeffps(self)->float:""" Computes and returns the average FPS based on the stored time stamps. Returns: float: The average FPS. Returns 0.0 if no time stamps are stored. """ifnotself.all_timestamps:return0.0taken_time=self.all_timestamps[-1]-self.all_timestamps[0]return(len(self.all_timestamps))/taken_timeiftaken_time!=0else0.0deftick(self)->None:""" Adds a new time stamp to the deque for FPS calculation. """self.all_timestamps.append(time.monotonic())defreset(self)->None:""" Clears all the time stamps from the deque. """self.all_timestamps.clear()
The maximum number of observations for latency
benchmarking.
30
Examples:
importsupervisionassvframes_generator=sv.get_video_frames_generator(source_path=<SOURCE_FILE_PATH>)fps_monitor=sv.FPSMonitor()forframeinframes_generator:# your processing code herefps_monitor.tick()fps=fps_monitor.fps
def__init__(self,sample_size:int=30):""" Args: sample_size (int): The maximum number of observations for latency benchmarking. Examples: ```python import supervision as sv frames_generator = sv.get_video_frames_generator(source_path=<SOURCE_FILE_PATH>) fps_monitor = sv.FPSMonitor() for frame in frames_generator: # your processing code here fps_monitor.tick() fps = fps_monitor.fps ``` """# noqa: E501 // docsself.all_timestamps=deque(maxlen=sample_size)
If True, the generator will seek to the
start frame by grabbing each frame, which is much slower. This is a
workaround for videos that don't open at all when you set the start value.
defget_video_frames_generator(source_path:str,stride:int=1,start:int=0,end:Optional[int]=None,iterative_seek:bool=False,)->Generator[np.ndarray,None,None]:""" Get a generator that yields the frames of the video. Args: source_path (str): The path of the video file. stride (int): Indicates the interval at which frames are returned, skipping stride - 1 frames between each. start (int): Indicates the starting position from which video should generate frames end (Optional[int]): Indicates the ending position at which video should stop generating frames. If None, video will be read to the end. iterative_seek (bool): If True, the generator will seek to the `start` frame by grabbing each frame, which is much slower. This is a workaround for videos that don't open at all when you set the `start` value. Returns: (Generator[np.ndarray, None, None]): A generator that yields the frames of the video. Examples: ```python import supervision as sv for frame in sv.get_video_frames_generator(source_path=<SOURCE_VIDEO_PATH>): ... ``` """video,start,end=_validate_and_setup_video(source_path,start,end,iterative_seek)frame_position=startwhileTrue:success,frame=video.read()ifnotsuccessorframe_position>=end:breakyieldframefor_inrange(stride-1):success=video.grab()ifnotsuccess:breakframe_position+=stridevideo.release()
A function that takes in
a numpy ndarray representation of a video frame and an
int index of the frame and returns a processed numpy ndarray
representation of the frame.
defprocess_video(source_path:str,target_path:str,callback:Callable[[np.ndarray,int],np.ndarray],)->None:""" Process a video file by applying a callback function on each frame and saving the result to a target video file. Args: source_path (str): The path to the source video file. target_path (str): The path to the target video file. callback (Callable[[np.ndarray, int], np.ndarray]): A function that takes in a numpy ndarray representation of a video frame and an int index of the frame and returns a processed numpy ndarray representation of the frame. Examples: ```python import supervision as sv def callback(scene: np.ndarray, index: int) -> np.ndarray: ... process_video( source_path=<SOURCE_VIDEO_PATH>, target_path=<TARGET_VIDEO_PATH>, callback=callback ) ``` """source_video_info=VideoInfo.from_video_path(video_path=source_path)withVideoSink(target_path=target_path,video_info=source_video_info)assink:forindex,frameinenumerate(get_video_frames_generator(source_path=source_path)):result_frame=callback(frame,index)sink.write_frame(frame=result_frame)