Download GeometryForcing/datasets/video/utils/io.py from BonanDing/worldmem-baseline-evals: direct link, hf CLI and curl.
- Browser
- Download file 5.88 kB
-
https://huggingface.co/BonanDing/worldmem-baseline-evals/resolve/main/GeometryForcing/datasets/video/utils/io.py
- Command line
-
hf download hf://BonanDing/worldmem-baseline-evals/GeometryForcing/datasets/video/utils/io.py
-
curl -L -o io.py https://huggingface.co/BonanDing/worldmem-baseline-evals/resolve/main/GeometryForcing/datasets/video/utils/io.py
5.88 kB
| from typing import Optional, Union | |
| import warnings | |
| import numpy as np | |
| import torch | |
| from fractions import Fraction | |
| import os | |
| try: | |
| import av | |
| av.logging.set_level(av.logging.ERROR) | |
| if not hasattr(av.video.frame.VideoFrame, "pict_type"): | |
| av = ImportError( | |
| """\ | |
| Your version of PyAV is too old for the necessary video operations in torchvision. | |
| If you are on Python 3.5, you will have to build from source (the conda-forge | |
| packages are not up-to-date). See | |
| https://github.com/mikeboers/PyAV#installation for instructions on how to | |
| install PyAV on your system. | |
| """ | |
| ) | |
| except ImportError: | |
| av = ImportError( | |
| """\ | |
| PyAV is not installed, and is necessary for the video operations in torchvision. | |
| See https://github.com/mikeboers/PyAV#installation for instructions on how to | |
| install PyAV on your system. | |
| """ | |
| ) | |
| from torchvision.io.video import ( | |
| _check_av_available, | |
| _read_from_stream, | |
| ) | |
| def read_video( | |
| filename: str, | |
| start_pts: Union[float, Fraction] = 0, | |
| end_pts: Optional[Union[float, Fraction]] = None, | |
| pts_unit: str = "pts", | |
| output_format: str = "THWC", | |
| ) -> torch.Tensor: | |
| """ | |
| Adapted from torchvision.io.video.read_video | |
| Simplified to only read video frames (not audio and additional info) | |
| Args: | |
| filename (str): path to the video file | |
| start_pts (int if pts_unit = 'pts', float / Fraction if pts_unit = 'sec', optional): | |
| The start presentation time of the video | |
| end_pts (int if pts_unit = 'pts', float / Fraction if pts_unit = 'sec', optional): | |
| The end presentation time | |
| pts_unit (str, optional): unit in which start_pts and end_pts values will be interpreted, | |
| either 'pts' or 'sec'. Defaults to 'pts'. | |
| output_format (str, optional): The format of the output video tensors. Can be either "THWC" (default) or "TCHW". | |
| Returns: | |
| vframes (Tensor[T, H, W, C] or Tensor[T, C, H, W]): the `T` video frames | |
| """ | |
| output_format = output_format.upper() | |
| if output_format not in ("THWC", "TCHW"): | |
| raise ValueError( | |
| f"output_format should be either 'THWC' or 'TCHW', got {output_format}." | |
| ) | |
| if not os.path.exists(filename): | |
| raise RuntimeError(f"File not found: {filename}") | |
| _check_av_available() | |
| if end_pts is None: | |
| end_pts = float("inf") | |
| if end_pts < start_pts: | |
| raise ValueError( | |
| f"end_pts should be larger than start_pts, got start_pts={start_pts} and end_pts={end_pts}" | |
| ) | |
| video_frames = [] | |
| try: | |
| with av.open(filename, metadata_errors="ignore") as container: | |
| if container.streams.video: | |
| with warnings.catch_warnings(): | |
| warnings.simplefilter("ignore") | |
| video_frames = _read_from_stream( | |
| container, | |
| start_pts, | |
| end_pts, | |
| pts_unit, | |
| container.streams.video[0], | |
| {"video": 0}, | |
| ) | |
| except av.AVError as e : | |
| # TODO raise a warning? | |
| print(f"[Warning][dataset][video][utils]io.py read video av error : {filename} Erorr={e}") | |
| pass | |
| vframes_list = [frame.to_rgb().to_ndarray() for frame in video_frames] | |
| if vframes_list: | |
| vframes = torch.as_tensor(np.stack(vframes_list)) | |
| else: | |
| vframes = torch.empty((0, 1, 1, 3), dtype=torch.uint8) | |
| if output_format == "TCHW": | |
| # [T,H,W,C] --> [T,C,H,W] | |
| vframes = vframes.permute(0, 3, 1, 2) | |
| return vframes | |
| import av | |
| def is_video_openable(filepath: str) -> bool: | |
| """ | |
| 尝试打开视频文件并读取第一帧以确认视频可用。 | |
| 不真正读取全部帧。 | |
| Args: | |
| filepath (str): 视频文件路径 | |
| Returns: | |
| bool: 如果能成功打开并读取至少一帧,返回 True,否则 False | |
| """ | |
| try: | |
| with av.open(filepath) as container: | |
| # 找到第一个视频流 | |
| video_stream = next((s for s in container.streams if s.type == 'video'), None) | |
| if video_stream is None: | |
| return False | |
| # 尝试解码第一帧 | |
| for packet in container.demux(video_stream): | |
| for frame in packet.decode(): | |
| # 只要能解码出一帧,就返回 True | |
| return True | |
| # 如果没解码出任何帧,返回 False | |
| return False | |
| except Exception: | |
| return False | |
| if __name__ == "__main__": | |
| import os | |
| from concurrent.futures import ThreadPoolExecutor, as_completed | |
| from tqdm import tqdm | |
| def is_video_openable(filepath: str) -> bool: | |
| import av | |
| try: | |
| with av.open(filepath) as container: | |
| video_stream = next((s for s in container.streams if s.type == 'video'), None) | |
| if video_stream is None: | |
| return False | |
| for packet in container.demux(video_stream): | |
| for frame in packet.decode(): | |
| return True | |
| return False | |
| except Exception: | |
| return False | |
| video_folder = "data/real-estate-10k/training_256/" | |
| files = [f for f in os.listdir(video_folder) if f.endswith(".mp4")] | |
| def check_file(filename): | |
| filepath = os.path.join(video_folder, filename) | |
| return filename, is_video_openable(filepath) | |
| # 用线程池并行检查 | |
| with ThreadPoolExecutor(max_workers=8) as executor: | |
| futures = {executor.submit(check_file, f): f for f in files} | |
| for future in tqdm(as_completed(futures), total=len(futures)): | |
| filename, can_open = future.result() | |
| if not can_open: | |
| print(f"{filename} is NOT openable or decodable.") | |