# Tải dữ liệu video

> [!WARNING]
> Hỗ trợ video đang ở giai đoạn thử nghiệm và có thể thay đổi.

Bộ dữ liệu video có các cột kiểu [Video](/docs/datasets/v5.2.0/vi/package_reference/main_classes#datasets.Video), chứa các đối tượng `torchcodec`.

> [!TIP]
> Để làm việc với các bộ dữ liệu video, bạn cần cài đặt các gói `torchcodec` và `ffmpeg`. Xem hướng dẫn [cài đặt](https://github.com/meta-pytorch/torchcodec#installing-torchcodec) để biết cách cài đặt chúng.

Khi bạn tải một tập dữ liệu video và gọi cột video, các video sẽ được giải mã thành các Video `torchcodec`:

```py
>>> 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>
```

> [!WARNING]
> Truy cập vào bộ dữ liệu video bằng cách sử dụng chỉ số hàng trước, sau đó là cột `video` - `dataset[0]["video"]` - để tránh tạo ra tất cả các đối tượng video trong bộ dữ liệu. Nếu không, đây có thể là một quy trình chậm và tốn thời gian nếu bạn có một bộ dữ liệu lớn.

Để tìm hiểu cách tải bất kỳ loại tập dữ liệu nào, hãy xem hướng dẫn tải chung.

## Đọc các khung hình

Truy cập trực tiếp các khung hình từ một video bằng `VideoReader` sử dụng `next()`:

```python
>>> 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
```

Để lấy nhiều khung hình cùng lúc, bạn có thể gọi `.get_frames_in_range(start: int, stop: int, step: int)`. Điều này sẽ trả về một lô khung hình. Đây là cách hiệu quả để lấy một danh sách dài các khung hình, hãy tham khảo [tài liệu torchcodec](https://meta-pytorch.org/torchcodec/stable/generated/torchcodec.decoders.VideoDecoder.html) để xem thêm các hàm khác để truy cập dữ liệu một cách hiệu quả:

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

Ngoài ra còn có `.get_frames_played_in_range(start_seconds: float, stop_seconds: float)` để truy cập tất cả các khung hình đã được phát trong một khoảng thời gian nhất định.

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

## Tệp cục bộ

Bạn có thể tải một tập dữ liệu từ đường dẫn video. Sử dụng hàm [cast_column()](/docs/datasets/v5.2.0/vi/package_reference/main_classes#datasets.Dataset.cast_column) để nhận một cột chứa các đường dẫn tệp video, và giải mã nó thành video `torchcodec` với tính năng [Video](/docs/datasets/v5.2.0/vi/package_reference/main_classes#datasets.Video):

```py
>>> 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>
```

Nếu bạn chỉ muốn tải đường dẫn gốc của tập dữ liệu video mà không giải mã đối tượng video, hãy đặt `decode=False` trong tính năng [Video](/docs/datasets/v5.2.0/vi/package_reference/main_classes#datasets.Video):

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

## VideoFolder

Bạn cũng có thể tải một tập dữ liệu bằng trình xây dựng tập dữ liệu `VideoFolder`, không yêu cầu viết một dataloader tùy chỉnh. Điều này khiến `VideoFolder` trở nên lý tưởng để nhanh chóng tạo và tải các tập dữ liệu video với hàng nghìn video cho các tác vụ thị giác khác nhau. Cấu trúc tập dữ liệu video của bạn nên trông như sau:

```
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
```

Nếu bộ dữ liệu tuân theo cấu trúc `VideoFolder`, bạn có thể tải nó trực tiếp bằng [load_dataset()](/docs/datasets/v5.2.0/vi/package_reference/loading_methods#datasets.load_dataset):

```py
>>> from datasets import load_dataset

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

Đối với các bộ dữ liệu cục bộ, điều này tương đương với việc truyền `videofolder` thủ công trong [load_dataset()](/docs/datasets/v5.2.0/vi/package_reference/loading_methods#datasets.load_dataset) và thư mục trong `data_dir`:

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

Sau đó, bạn có thể truy cập các video dưới dạng các đối tượng `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}
```

Để bỏ qua thông tin trong tệp metadata, đặt `drop_metadata=True` trong [load_dataset()](/docs/datasets/v5.2.0/vi/package_reference/loading_methods#datasets.load_dataset):

```py
>>> from datasets import load_dataset

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

Nếu bạn không có tệp metadata, `VideoFolder` sẽ tự động suy ra tên nhãn từ tên thư mục. Nếu bạn muốn loại bỏ các nhãn được tạo tự động, hãy đặt `drop_labels=True`. Trong trường hợp này, tập dữ liệu của bạn sẽ chỉ chứa một cột video:

```py
>>> from datasets import load_dataset

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

Cuối cùng, đối số `filters` cho phép bạn chỉ tải một tập con của bộ dữ liệu, dựa trên điều kiện về nhãn hoặc metadata. Điều này đặc biệt hữu ích nếu metadata ở định dạng Parquet, vì định dạng này cho phép lọc nhanh. Cũng nên sử dụng đối số này cùng với `streaming=True`, vì mặc định bộ dữ liệu sẽ được tải xuống hoàn toàn trước khi lọc.

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

> [!TIP]
> Để biết thêm thông tin về việc tạo bộ dữ liệu `VideoFolder` của riêng bạn, hãy xem hướng dẫn [Tạo bộ dữ liệu video](./video_dataset).

## WebDataset

Định dạng [WebDataset](https://github.com/webdataset/webdataset) dựa trên một thư mục chứa các tệp lưu trữ TAR và phù hợp cho các bộ dữ liệu video lớn. Do kích thước của chúng, WebDataset thường được nạp ở chế độ streaming (sử dụng `streaming=True`).

Bạn có thể nạp một WebDataset như sau:

```python
>>> from datasets import load_dataset

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

## Lance

[Lance](https://lance.org) là định dạng bảng lakehouse đa phương thức mã nguồn mở. Các bảng Lance có thể lưu trữ bản địa không chỉ văn bản và giá trị vô hướng mà còn cả các đối tượng nhị phân lớn (blobs) như hình ảnh, âm thanh và video cùng với dữ liệu bảng của bạn. Bên trong một bảng Lance, các blob lớn như video được lưu trữ dưới dạng byte kèm theo các offset (xem [hướng dẫn về blob](https://lance.org/guide/blob/) để biết thêm chi tiết), điều này giúp việc quét và lọc metadata trở nên dễ dàng mà không cần tải các blob video nặng hơn, và sau đó chỉ lấy các blob video cụ thể bạn cần theo yêu cầu.

Ngoài ra, vì Lance là một định dạng cột, bạn có thể chỉ chiếu và lọc các cột metadata mà bạn quan tâm (mà không cần tải các tệp video lớn), sau đó truy xuất một tập con nhỏ các dòng (bao gồm cả video) khi bạn sẵn sàng. Điều này giúp bạn giữ metadata và video ở một nơi, mà không cần một kho lưu trữ tệp riêng biệt hay một chỉ mục bên ngoài.

```python
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())
```

Trong ví dụ này, video được lưu trữ dạng gốc (dưới dạng byte đã mã hóa) trong bảng Lance, vì vậy bạn có thể ghi trực tiếp nó vào một tệp `mp4` trên hệ thống tệp cục bộ mà không cần bước chuyển đổi bổ sung nào.

Để biết thêm chi tiết về việc làm việc với bộ dữ liệu Lance, hãy xem [tài liệu Lance](https://lance.org).

## Giải mã video

Mặc định, video được giải mã tuần tự dưới dạng `VideoDecoders` của torchcodec khi bạn lặp qua một tập dữ liệu. Nó giải mã tuần tự metadata của các video và không đọc khung hình video cho đến khi bạn truy cập chúng.

Tuy nhiên, có thể tăng tốc bộ dữ liệu đáng kể bằng cách giải mã đa luồng:

```python
>>> 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 !
...     ...
```

Bạn có thể bật đa luồng bằng cách sử dụng `num_threads`. Điều này đặc biệt hữu ích để tăng tốc độ truyền dữ liệu từ xa. Tuy nhiên, nó có thể chậm hơn so với `num_threads=0` đối với dữ liệu cục bộ trên các ổ đĩa nhanh.

Nếu bạn không quan tâm đến các video được giải mã dưới dạng `VideoDecoders` của torchcodec và muốn truy cập đường dẫn/bytes thay vào đó, bạn có thể tắt giải mã:

```python
>>> dataset = dataset.decode(False)
```

Lưu ý: [IterableDataset.decode()](/docs/datasets/v5.2.0/vi/package_reference/main_classes#datasets.IterableDataset.decode) hiện chỉ khả dụng cho các bộ dữ liệu dạng streaming.

