Datasets documentation

加载视频数据

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v5.2.0).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

加载视频数据

视频支持目前处于实验阶段,可能会发生变化。

视频数据集包含 Video 类型的列,其中包含 torchcodec 对象。

要处理视频数据集,您需要安装 torchcodec 和 ffmpeg 包。请参阅 安装 指南了解如何安装它们。

加载视频数据集并调用视频列时,视频会解码为 torchcodec 视频:

>>> from datasets import load_dataset, Video

>>> dataset = load_dataset("path/to/video/folder", split="train")
>>> dataset[0]["video"]
<torchcodec.decoders._video_decoder.VideoDecoder object at 0x14a61d5a0>

首先使用行索引,然后使用 video 列来索引视频数据集 - dataset[0]["video"] - 以避免创建数据集中的所有视频对象。否则,如果数据集很大,这可能是一个缓慢且耗时的过程。

有关如何加载任意类型数据集的指南,请参阅通用加载指南。

读取帧

使用 VideoReader 并通过 next() 直接从视频中访问帧:

>>> video = dataset[0]["video"]
>>> first_frame = video.get_frame_at(0)
>>> first_frame.data.shape
(3, 240, 320)
>>> first_frame.pts_seconds  # timestamp
0.0

要一次性获取多帧,可以调用 .get_frames_in_range(start: int, stop: int, step: int)。这将返回一个帧批次。 这是获取长帧列表的高效方式,请参阅 torchcodec 文档 以了解更多高效访问数据的函数:

>>> import torch
>>> frames = video.get_frames_in_range(0, 6, 1)
>>> frames.data.shape
torch.Size([5, 3, 240, 320])

此外,还可以使用 .get_frames_played_in_range(start_seconds: float, stop_seconds: float) 来访问在特定时间范围内播放的所有帧。

>>> frames = video.get_frames_played_in_range(.5, 1.2)
>>> frames.data.shape
torch.Size([42, 3, 240, 320])

本地文件

您可以从视频路径加载数据集。使用 cast_column() 函数接受一个包含视频文件路径的列,并使用 Video 特征将其解码为 torchcodec 视频:

>>> from datasets import Dataset, Video

>>> dataset = Dataset.from_dict({"video": ["path/to/video_1", "path/to/video_2", ..., "path/to/video_n"]}).cast_column("video", Video())
>>> dataset[0]["video"]
<torchcodec.decoders._video_decoder.VideoDecoder object at 0x14a61e080>

如果只想加载视频数据集的底层路径而不解码视频对象,请在 Video 特征中设置 decode=False:

>>> dataset = dataset.cast_column("video", Video(decode=False))
>>> dataset[0]["video"]
{'bytes': None,
 'path': 'path/to/video/folder/video0.mp4'}

VideoFolder

您还可以使用 VideoFolder 数据集构建器来加载数据集,这无需编写自定义的数据加载器。这使得 VideoFolder 非常适合快速创建和加载包含数千个视频的数据集,以用于不同的视觉任务。您的视频数据集结构应如下所示:

folder/train/dog/golden_retriever.mp4
folder/train/dog/german_shepherd.mp4
folder/train/dog/chihuahua.mp4

folder/train/cat/maine_coon.mp4
folder/train/cat/bengal.mp4
folder/train/cat/birman.mp4

如果数据集遵循 VideoFolder 结构,则可以直接使用 load_dataset() 加载:

>>> from datasets import load_dataset

>>> dataset = load_dataset("username/dataset_name")
>>> # OR locally:
>>> dataset = load_dataset("/path/to/folder")

对于本地数据集,这相当于在 load_dataset() 中手动传入 videofolder 以及 data_dir 中的目录:

>>> dataset = load_dataset("videofolder", data_dir="/path/to/folder")

然后,您可以将视频作为 torchcodec.decoders._video_decoder.VideoDecoder 对象进行访问:

>>> dataset["train"][0]
{"video": <torchcodec.decoders._video_decoder.VideoDecoder object at 0x14a61e080>, "label": 0}

>>> dataset["train"][-1]
{"video": <torchcodec.decoders._video_decoder.VideoDecoder object at 0x14a61e090>, "label": 1}

若要忽略元数据文件中的信息,请在 load_dataset() 中设置 drop_metadata=True:

>>> from datasets import load_dataset

>>> dataset = load_dataset("username/dataset_with_metadata", drop_metadata=True)

如果没有元数据文件,VideoFolder 会自动从目录名称推断标签名称。 如果要丢弃自动创建的标签,请设置 drop_labels=True。 在这种情况下,您的数据集将只包含一个视频列:

>>> from datasets import load_dataset

>>> dataset = load_dataset("username/dataset_without_metadata", drop_labels=True)

最后,filters 参数允许你基于标签或元数据中的条件,仅加载数据集的一个子集。如果元数据采用 Parquet 格式,这一点尤其有用,因为该格式支持快速过滤。同时,建议将此参数与 streaming=True 一起使用,因为默认情况下,数据集会在过滤之前被完整下载。

>>> filters = [("label", "=", 0)]
>>> dataset = load_dataset("username/dataset_name", streaming=True, filters=filters)

有关创建自己的 VideoFolder 数据集的更多信息,请参阅 创建视频数据集 指南。

WebDataset

WebDataset 格式基于 TAR 归档文件夹,适用于大型视频数据集。 由于其体积庞大,WebDataset 通常以流式模式加载(使用 streaming=True)。

您可以按以下方式加载 WebDataset:

>>> from datasets import load_dataset

>>> dataset = load_dataset("webdataset", data_dir="/path/to/folder", streaming=True)

Lance

Lance 是一种开放的多模态数据湖仓表格式。Lance 表不仅可以原生存储文本和标量值,还可以与您的表格数据一起存储大型二进制对象(blobs),例如图像、音频和视频。在 Lance 表中,像视频这样的大型 blob 以带有偏移量的字节形式存储(有关更多详细信息,请参阅 blob 指南),这使得您可以轻松扫描和过滤元数据,而无需加载较重的视频 blob,然后仅按需获取您需要的特定视频 blob。

此外,由于 Lance 是一种列式格式,您可以仅投影和筛选所需的元数据列(而无需获取大型视频文件),然后在准备好时再检索包含视频的一小部分行。这样可以将元数据和视频集中存储在一处,无需单独的文件存储或外部索引。

import lance

ds = lance.dataset("hf://datasets/lance-format/openvid-lance/data/train.lance")

# 1. Browse metadata without loading video blobs.
metadata = ds.scanner(
    columns=["caption", "aesthetic_score"],
    filter="aesthetic_score >= 4.5",
    limit=2,
).to_table().to_pylist()

# 2. Fetch a single video blob by row index.
selected_index = 0
blob_file = ds.take_blobs("video_blob", ids=[selected_index])[0]
with open("video_0.mp4", "wb") as f:
    f.write(blob_file.read())

在此示例中,视频以原生形式(即其编码后的字节)存储在 Lance 表中,因此您可以直接将其写入本地文件系统中的 mp4 文件,无需任何额外的转换步骤。

有关使用 Lance 数据集的更多详细信息,请参阅 Lance 文档。

视频解码

默认情况下,当您在数据集上迭代时,视频会作为 VideoDecoders 按顺序解码。 它会按顺序解码视频的元数据,直到您访问视频帧之前,不会读取视频帧。

不过,可以使用多线程解码来显著加快数据集的处理速度:

>>> import os
>>> num_threads = num_threads = min(32, (os.cpu_count() or 1) + 4)
>>> dataset = dataset.decode(num_threads=num_threads)
>>> for example in dataset:  # up to 20 times faster !
...     ...

您可以使用 num_threads 来启用多线程。这对于加速远程数据流式传输特别有用。 但对于位于高速磁盘上的本地数据,它可能比 num_threads=0 更慢。

如果您对以 torchcodec VideoDecoders 解码的视频不感兴趣,而是希望访问路径/字节,则可以禁用解码:

>>> dataset = dataset.decode(False)

注意:IterableDataset.decode() 目前仅适用于流式数据集。

Update on GitHub