<!-- # hard line break macro for HTML -->

<a id="importing-datasets"></a>

# Importing data into FiftyOne


<div class="available-in">
    <div class="available-in-row">
        <span class="available-in-label">Available in:</span>
        <span class="available-in-pill available-in-pill--oss">Open Source</span><span class="available-in-pill available-in-pill--enterprise">Enterprise</span>
    </div>
    <div class="available-in-row">
        <span class="available-in-versions">Introduced in <a href="../release-notes.html#fiftyone-0-3-0">FiftyOne 0.3.0</a> &middot; <a href="../release-notes.html#fiftyone-enterprise-1-0">FiftyOne Enterprise 1.0</a></span>
    </div>
    
</div>

The first step to using FiftyOne is to load your data into a
[dataset](using_datasets.md#using-datasets). FiftyOne supports automatic loading of
datasets stored in various [common formats](#supported-import-formats).
If your dataset is stored in a custom format, don’t worry, FiftyOne also
provides support for easily loading datasets in
[custom formats](#loading-custom-datasets).

Check out the sections below to see which import pattern is the best fit for
your data.

#### NOTE
Did you know? You can import media and/or labels from within the FiftyOne
App by installing the
[@voxel51/io](https://github.com/voxel51/fiftyone-plugins/tree/main/plugins/io)
plugin!

#### NOTE
When you create a [`Dataset`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset), its samples and all of their fields (metadata,
labels, custom fields, etc.) are written to FiftyOne’s backing database.

**Important:** Samples only store the `filepath` to the media, not the
raw media itself. FiftyOne does not create duplicate copies of your data!

<a id="loading-custom-datasets"></a>

## Custom formats

The simplest and most flexible approach to loading your data into FiftyOne is
to iterate over your data in a simple Python loop, create a [`Sample`](../api/fiftyone.core.sample.md#fiftyone.core.sample.Sample) for each
data + label(s) pair, and then add those samples to a [`Dataset`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset).

FiftyOne provides [label types](using_datasets.md#using-labels) for common tasks such as
classification, detection, segmentation, and many more. The examples below
give you a sense of the basic workflow for a few tasks.

Image classification

Object detection

Labeled videos

3D scenes

```python
import glob
import fiftyone as fo

images_patt = "/path/to/images/*"

# Ex: your custom label format
annotations = {
    "/path/to/images/000001.jpg": "dog",
    ....,
}

# Create samples for your data
samples = []
for filepath in glob.glob(images_patt):
    sample = fo.Sample(filepath=filepath)

    # Store classification in a field name of your choice
    label = annotations[filepath]
    sample["ground_truth"] = fo.Classification(label=label)

    samples.append(sample)

# Create dataset
dataset = fo.Dataset("my-classification-dataset")
dataset.add_samples(samples)
```

```python
import glob
import fiftyone as fo

images_patt = "/path/to/images/*"

# Ex: your custom label format
annotations = {
    "/path/to/images/000001.jpg": [
        {"bbox": ..., "label": ...},
        ...
    ],
    ...
}

# Create samples for your data
samples = []
for filepath in glob.glob(images_patt):
    sample = fo.Sample(filepath=filepath)

    # Convert detections to FiftyOne format
    detections = []
    for obj in annotations[filepath]:
        label = obj["label"]

        # Bounding box coordinates should be relative values
        # in [0, 1] in the following format:
        # [top-left-x, top-left-y, width, height]
        bounding_box = obj["bbox"]

        detections.append(
            fo.Detection(label=label, bounding_box=bounding_box)
        )

    # Store detections in a field name of your choice
    sample["ground_truth"] = fo.Detections(detections=detections)

    samples.append(sample)

# Create dataset
dataset = fo.Dataset("my-detection-dataset")
dataset.add_samples(samples)
```

```python
import fiftyone as fo

video_path = "/path/to/video.mp4"

# Ex: your custom label format
frame_labels = {
    1: {
        "weather": "sunny",
        "objects": [
            {
                "label": ...
                "bbox": ...
            },
            ...
        ]
    },
    ...
}

# Create video sample with frame labels
sample = fo.Sample(filepath=video_path)
for frame_number, labels in frame_labels.items():
    frame = fo.Frame()

    # Store a frame classification
    weather = labels["weather"]
    frame["weather"] = fo.Classification(label=weather)

    # Convert detections to FiftyOne format
    detections = []
    for obj in labels["objects"]:
        label = obj["label"]

        # Bounding box coordinates should be relative values
        # in [0, 1] in the following format:
        # [top-left-x, top-left-y, width, height]
        bounding_box = obj["bbox"]

        detections.append(
            fo.Detection(label=label, bounding_box=bounding_box)
        )

    # Store object detections
    frame["objects"] = fo.Detections(detections=detections)

    # Add frame to sample
    sample.frames[frame_number] = frame

# Create dataset
dataset = fo.Dataset("my-labeled-video-dataset")
dataset.add_sample(sample)
```

```python
import fiftyone as fo

# Create a 3D scene with a mesh
scene = fo.Scene()
scene.add(fo.GltfMesh("mesh", "mesh.gltf"))
scene.write("/path/to/scene.fo3d")

# Define a 3D cuboid
detection = fo.Detection(
    label="vehicle",
    location=[0.47, 1.49, 69.44],
    dimensions=[2.85, 2.63, 12.34],
    rotation=[0, -1.56, 0],
)

# Construct a sample representing the scene
sample = fo.Sample(
    filepath="/path/to/scene.fo3d",
    ground_truth=fo.Detections(detections=[detection]),
)

# Create dataset
dataset = fo.Dataset("my-3d-dataset")
dataset.add_sample(sample)
```

#### NOTE
Using
[`Dataset.add_samples()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.add_samples)
to add batches of samples to your datasets can be significantly more
efficient than adding samples one-by-one via
[`Dataset.add_sample()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.add_sample).

#### NOTE
If you use the same custom data format frequently in your workflows, then
writing a [custom dataset importer](#custom-dataset-importer) is a
great way to abstract and streamline the loading of your data into
FiftyOne.

<a id="loading-common-datasets"></a>

## Common formats

If your data is stored on disk in one of the
[many common formats](#supported-import-formats) supported natively by
FiftyOne, then you can load your data into a [`Dataset`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset) via Python or the CLI
with the following simple pattern:

Python

CLI

You can import a [`Dataset`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset) from disk via the
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) factory
method.

If your data is stored in the
[canonical format](#supported-import-formats) of the type you’re
importing, then you can load it by providing the `dataset_dir` and
`dataset_type` parameters:

```python
import fiftyone as fo

# The directory containing the dataset to import
dataset_dir = "/path/to/dataset"

# The type of the dataset being imported
dataset_type = fo.types.COCODetectionDataset  # for example

# Import the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=dataset_type,
)
```

Alternatively, when importing labeled datasets in formats such as
[COCO](#cocodetectiondataset-import), you may find it more natural to
provide the `data_path` and `labels_path` parameters to independently
specify the location of the source media on disk and the annotations file
containing the labels to import:

```python
# The directory containing the source images
data_path = "/path/to/images"

# The path to the COCO labels JSON file
labels_path = "/path/to/coco-labels.json"

# Import the dataset
dataset = fo.Dataset.from_dir(
    dataset_type=fo.types.COCODetectionDataset,
    data_path=data_path,
    labels_path=labels_path,
)
```

Many formats like [COCO](#cocodetectiondataset-import) also support
storing absolute filepaths to the source media directly in the labels, in
which case you can provide only the `labels_path` parameter:

```python
# The path to a COCO labels JSON file containing absolute image paths
labels_path = "/path/to/coco-labels.json"

# Import the dataset
dataset = fo.Dataset.from_dir(
    dataset_type=fo.types.COCODetectionDataset,
    labels_path=labels_path,
)
```

In general, you can pass any parameter for the [`DatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.DatasetImporter) of the
format you’re importing to
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir). For
example, most builtin importers support optional `max_samples`, `shuffle`,
and `seed` parameters, which provide support for importing a small subset
of a potentially large dataset:

```python
# Import a random subset of 10 samples from the dataset
dataset = fo.Dataset.from_dir(
    ...,
    max_samples=10,
    shuffle=True,
    seed=51,
)
```

You can import a dataset from disk into FiftyOne
[via the CLI](../cli/index.md#cli-fiftyone-datasets-create).

If your data is stored in the
[canonical format](#supported-import-formats) of the type you’re
importing, then you can load it by providing the `--dataset-dir` and
`--type` options:

```shell
# A name for the dataset
NAME=my-dataset

# The directory containing the dataset to import
DATASET_DIR=/path/to/dataset

# The type of the dataset being imported
# Any subclass of `fiftyone.types.Dataset` is supported
TYPE=fiftyone.types.COCODetectionDataset  # for example

# Import the dataset
fiftyone datasets create --name $NAME --dataset-dir $DATASET_DIR --type $TYPE
```

Alternatively, when importing labeled datasets in formats such as
[COCO](#cocodetectiondataset-import), you may find it more natural to
provide the `data_path` and `labels_path` parameters via the
[kwargs option](../cli/index.md#cli-fiftyone-datasets-create) to independently
specify the location of the source media on disk and the annotations file
containing the labels to import:

```shell
# The directory containing the source images
DATA_PATH=/path/to/images

# The path to the COCO labels JSON file
LABELS_PATH=/path/to/coco-labels.json

# Import the dataset
fiftyone datasets create --name my-dataset \
    --type fiftyone.types.COCODetectionDataset \
    --kwargs \
        data_path=$DATA_PATH \
        labels_path=$LABELS_PATH
```

Many formats like [COCO](#cocodetectiondataset-import) also support
storing absolute filepaths to the source media directly in the labels, in
which case you can provide only the `labels_path` parameter:

```shell
# The path to a COCO labels JSON file containing absolute image paths
LABELS_PATH=/path/to/coco-labels.json

# Import the dataset
fiftyone datasets create --name my-dataset \
    --type fiftyone.types.COCODetectionDataset \
    --kwargs labels_path=$LABELS_PATH
```

In general, you can pass any parameter for the [`DatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.DatasetImporter) of the
format you’re importing via the
[kwargs option](../cli/index.md#cli-fiftyone-datasets-create). For example, most
builtin importers support optional `max_samples`, `shuffle`, and `seed`
parameters, which provide support for importing a small subset of a
potentially large dataset:

```shell
# Import a random subset of 10 samples from the dataset
fiftyone datasets create \
    --name $NAME --dataset-dir $DATASET_DIR --type $TYPE \
    --kwargs \
        max_samples=10 \
        shuffle=True \
        seed=51
```

#### NOTE
Jump to [this section](#supported-import-formats) to see a full list
of supported import formats.

#### NOTE
Did you know? You can write
[custom importers](#custom-dataset-importer) to streamline import of
data in custom formats.

<a id="loading-media"></a>

## Loading media

If you’re just getting started with a project and all you have is a bunch of
media files, you can easily load them into a FiftyOne dataset and start
visualizing them [in the App](app.md#fiftyone-app).

Images

Videos

You can use the
[`Dataset.from_images()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_images),
[`Dataset.from_images_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_images_dir), and
[`Dataset.from_images_patt()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_images_patt)
factory methods to load your images into FiftyOne:

```python
import fiftyone as fo

# Create a dataset from a list of images
dataset = fo.Dataset.from_images(
    ["/path/to/image1.jpg", "/path/to/image2.jpg", ...]
)

# Create a dataset from a directory of images
dataset = fo.Dataset.from_images_dir("/path/to/images")

# Create a dataset from a glob pattern of images
dataset = fo.Dataset.from_images_patt("/path/to/images/*.jpg")

session = fo.launch_app(dataset)
```

You can also use
[`Dataset.add_images()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.add_images),
[`Dataset.add_images_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.add_images_dir), and
[`Dataset.add_images_patt()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.add_images_patt)
to add images to an existing dataset.

You can use the [fiftyone app view](../cli/index.md#cli-fiftyone-app-view) command
from the CLI to quickly browse images in the App without creating a
(persistent) FiftyOne dataset:

```shell
# View a glob pattern of images in the App
fiftyone app view --images-patt '/path/to/images/*.jpg'

# View a directory of images in the App
fiftyone app view --images-dir '/path/to/images'
```

You can use the
[`Dataset.from_videos()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_videos),
[`Dataset.from_videos_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_videos_dir), and
[`Dataset.from_videos_patt()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_videos_patt)
factory methods to load your videos into FiftyOne:

```python
import fiftyone as fo

# Create a dataset from a list of videos
dataset = fo.Dataset.from_videos(
    ["/path/to/video1.mp4", "/path/to/video2.mp4", ...]
)

# Create a dataset from a directory of videos
dataset = fo.Dataset.from_videos_dir("/path/to/videos")

# Create a dataset from a glob pattern of videos
dataset = fo.Dataset.from_videos_patt("/path/to/videos/*.mp4")

session = fo.launch_app(dataset)
```

You can also use
[`Dataset.add_videos()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.add_videos),
[`Dataset.add_videos_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.add_videos_dir), and
[`Dataset.add_videos_patt()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.add_videos_patt)
to add videos to an existing dataset.

You can use the [fiftyone app view](../cli/index.md#cli-fiftyone-app-view) command
from the CLI to quickly browse videos in the App without creating a
(persistent) FiftyOne dataset:

```shell
# View a glob pattern of videos in the App
fiftyone app view --videos-patt '/path/to/videos/*.mp4'

# View a directory of videos in the App
fiftyone app view --videos-dir '/path/to/videos'
```

<a id="adding-model-predictions"></a>

## Adding model predictions


<div class="available-in">
    <div class="available-in-row">
        <span class="available-in-label">Available in:</span>
        <span class="available-in-pill available-in-pill--oss">Open Source</span><span class="available-in-pill available-in-pill--enterprise">Enterprise</span>
    </div>
    <div class="available-in-row">
        <span class="available-in-versions">Introduced in <a href="../release-notes.html#fiftyone-0-14-1">FiftyOne 0.14.1</a> &middot; <a href="../release-notes.html#fiftyone-enterprise-1-0">FiftyOne Enterprise 1.0</a></span>
    </div>
    
</div>

Once you’ve created a dataset and ground truth labels, you can easily add model
predictions to take advantage of FiftyOne’s
[evaluation capabilities](evaluation/index.md#evaluating-models).

COCO

YOLO

Other formats

If you have model predictions stored in
[COCO format](#cocodetectiondataset-import), then you can use
[`add_coco_labels()`](../api/fiftyone.utils.coco.md#fiftyone.utils.coco.add_coco_labels) to
conveniently add the labels to an existing dataset.

The example below demonstrates a round-trip export and then re-import of
both images-and-labels and labels-only data in COCO format:

```python
import fiftyone as fo
import fiftyone.zoo as foz
import fiftyone.utils.coco as fouc

dataset = foz.load_zoo_dataset("quickstart")
classes = dataset.distinct("predictions.detections.label")

# Export images and ground truth labels to disk
dataset.export(
    export_dir="/tmp/coco",
    dataset_type=fo.types.COCODetectionDataset,
    label_field="ground_truth",
    classes=classes,
)

# Export predictions
dataset.export(
    dataset_type=fo.types.COCODetectionDataset,
    labels_path="/tmp/coco/predictions.json",
    label_field="predictions",
    classes=classes,
)

# Now load ground truth labels into a new dataset
dataset2 = fo.Dataset.from_dir(
    dataset_dir="/tmp/coco",
    dataset_type=fo.types.COCODetectionDataset,
    label_field="ground_truth",
    label_types="detections",
)

# And add model predictions
fouc.add_coco_labels(
    dataset2,
    "predictions",
    "/tmp/coco/predictions.json",
    classes,
)

# Verify that ground truth and predictions were imported as expected
print(dataset.count("ground_truth.detections"))
print(dataset2.count("ground_truth.detections"))
print(dataset.count("predictions.detections"))
print(dataset2.count("predictions.detections"))
```

#### NOTE
See [`add_coco_labels()`](../api/fiftyone.utils.coco.md#fiftyone.utils.coco.add_coco_labels) for
a complete description of the available syntaxes for loading
COCO-formatted predictions to an existing dataset.

If you have model predictions stored in
[YOLO format](#yolov4dataset-import), then you can use
[`add_yolo_labels()`](../api/fiftyone.utils.yolo.md#fiftyone.utils.yolo.add_yolo_labels) to
conveniently add the labels to an existing dataset.

The example below demonstrates a round-trip export and then re-import of
both images-and-labels and labels-only data in YOLO format:

```python
import fiftyone as fo
import fiftyone.zoo as foz
import fiftyone.utils.yolo as fouy

dataset = foz.load_zoo_dataset("quickstart")
classes = dataset.distinct("predictions.detections.label")

# Export images and ground truth labels to disk
dataset.export(
    export_dir="/tmp/yolov4",
    dataset_type=fo.types.YOLOv4Dataset,
    label_field="ground_truth",
    classes=classes,
)

# Export predictions
dataset.export(
    dataset_type=fo.types.YOLOv4Dataset,
    labels_path="/tmp/yolov4/predictions",
    label_field="predictions",
    classes=classes,
)

# Now load ground truth labels into a new dataset
dataset2 = fo.Dataset.from_dir(
    dataset_dir="/tmp/yolov4",
    dataset_type=fo.types.YOLOv4Dataset,
    label_field="ground_truth",
)

# And add model predictions
fouy.add_yolo_labels(
    dataset2,
    "predictions",
    "/tmp/yolov4/predictions",
    classes,
)

# Verify that ground truth and predictions were imported as expected
print(dataset.count("ground_truth.detections"))
print(dataset2.count("ground_truth.detections"))
print(dataset.count("predictions.detections"))
print(dataset2.count("predictions.detections"))
```

#### NOTE
See [`add_yolo_labels()`](../api/fiftyone.utils.yolo.md#fiftyone.utils.yolo.add_yolo_labels) for
a complete description of the available syntaxes for loading
YOLO-formatted predictions to an existing dataset.

Model predictions stored in other formats can always be
[loaded iteratively](#loading-custom-datasets) through a simple Python
loop.

The example below shows how to add object detection predictions to a
dataset, but many [other label types](using_datasets.md#using-labels) are also
supported.

```python
import fiftyone as fo

# Ex: your custom predictions format
predictions = {
    "/path/to/images/000001.jpg": [
        {"bbox": ..., "label": ..., "score": ...},
        ...
    ],
    ...
}

# Add predictions to your samples
for sample in dataset:
    filepath = sample.filepath

    # Convert predictions to FiftyOne format
    detections = []
    for obj in predictions[filepath]:
        label = obj["label"]
        confidence = obj["score"]

        # Bounding box coordinates should be relative values
        # in [0, 1] in the following format:
        # [top-left-x, top-left-y, width, height]
        bounding_box = obj["bbox"]

        detections.append(
            fo.Detection(
                label=label,
                bounding_box=bounding_box,
                confidence=confidence,
            )
        )

    # Store detections in a field name of your choice
    sample["predictions"] = fo.Detections(detections=detections)

    sample.save()
```

#### NOTE
If you are in need of a model to run on your dataset, check out the
[FiftyOne Model Zoo](../model_zoo/index.md#model-zoo).

<a id="supported-import-formats"></a>

## Built-in formats

FiftyOne provides a variety of built-in importers for common data formats.

Each data format is represented by a subclass of
[`fiftyone.types.Dataset`](../api/fiftyone.types.md#fiftyone.types.Dataset), which is used by the Python library and CLI to
refer to the corresponding dataset format when reading the dataset from disk.

<a id="imagedirectory-import"></a>

## Image Directory

The [`fiftyone.types.ImageDirectory`](../api/fiftyone.types.md#fiftyone.types.ImageDirectory) type represents a directory of
images.

Datasets of this type are read in the following format:

```text
<dataset_dir>/
    <filename1>.<ext>
    <filename2>.<ext>
```

where files with non-image MIME types are omitted.

By default, the dataset may contain nested subfolders of images, which are
recursively listed.

#### NOTE
See [`ImageDirectoryImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.ImageDirectoryImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from a directory of images as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/images-dir"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.ImageDirectory,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/images-dir

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.ImageDirectory

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

To view a directory of images in the FiftyOne App without creating
a persistent FiftyOne dataset, you can execute:

```shell
DATASET_DIR=/path/to/images-dir

# View the dataset in the App
fiftyone app view \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.ImageDirectory
```

<a id="videodirectory-import"></a>

## Video Directory


<div class="available-in">
    <div class="available-in-row">
        <span class="available-in-label">Available in:</span>
        <span class="available-in-pill available-in-pill--oss">Open Source</span><span class="available-in-pill available-in-pill--enterprise">Enterprise</span>
    </div>
    <div class="available-in-row">
        <span class="available-in-versions">Introduced in <a href="../release-notes.html#fiftyone-0-6-0">FiftyOne 0.6.0</a> &middot; <a href="../release-notes.html#fiftyone-enterprise-1-0">FiftyOne Enterprise 1.0</a></span>
    </div>
    
</div>

The [`fiftyone.types.VideoDirectory`](../api/fiftyone.types.md#fiftyone.types.VideoDirectory) type represents a directory of
videos.

Datasets of this type are read in the following format:

```text
<dataset_dir>/
    <filename1>.<ext>
    <filename2>.<ext>
```

where files with non-video MIME types are omitted.

By default, the dataset may contain nested subfolders of videos, which are
recursively listed.

#### NOTE
See [`VideoDirectoryImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.VideoDirectoryImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from a directory of videos as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/videos-dir"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.VideoDirectory,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/videos-dir

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.VideoDirectory

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

To view a directory of videos in the FiftyOne App without creating
a persistent FiftyOne dataset, you can execute:

```shell
DATASET_DIR=/path/to/videos-dir

# View the dataset in the App
fiftyone app view \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.VideoDirectory
```

<a id="mediadirectory-import"></a>

## Media Directory


<div class="available-in">
    <div class="available-in-row">
        <span class="available-in-label">Available in:</span>
        <span class="available-in-pill available-in-pill--oss">Open Source</span><span class="available-in-pill available-in-pill--enterprise">Enterprise</span>
    </div>
    <div class="available-in-row">
        <span class="available-in-versions">Introduced in <a href="../release-notes.html#fiftyone-0-19-0">FiftyOne 0.19.0</a> &middot; <a href="../release-notes.html#fiftyone-enterprise-1-1">FiftyOne Enterprise 1.1</a></span>
    </div>
    
</div>

The [`fiftyone.types.MediaDirectory`](../api/fiftyone.types.md#fiftyone.types.MediaDirectory) type represents a directory of media
files.

Datasets of this type are read in the following format:

```text
<dataset_dir>/
    <filename1>.<ext>
    <filename2>.<ext>
```

#### NOTE
All files must have the same media type (image, video, point cloud, etc.)

By default, the dataset may contain nested subfolders of media files, which are
recursively listed.

#### NOTE
See [`MediaDirectoryImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.MediaDirectoryImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from a directory of media files as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/media-dir"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.MediaDirectory,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/media-dir

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.MediaDirectory

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

To view a directory of media in the FiftyOne App without creating
a persistent FiftyOne dataset, you can execute:

```shell
DATASET_DIR=/path/to/media-dir

# View the dataset in the App
fiftyone app view \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.MediaDirectory
```

<a id="imageclassificationdirectorytree-import"></a>

## Image Classification Dir Tree

The [`fiftyone.types.ImageClassificationDirectoryTree`](../api/fiftyone.types.md#fiftyone.types.ImageClassificationDirectoryTree) type represents a
directory tree whose subfolders define an image classification dataset.

Datasets of this type are read in the following format:

```text
<dataset_dir>/
    <classA>/
        <image1>.<ext>
        <image2>.<ext>
        ...
    <classB>/
        <image1>.<ext>
        <image2>.<ext>
        ...
    ...
```

Unlabeled images are stored in a subdirectory named `_unlabeled`.

Each class folder may contain nested subfolders of images.

#### NOTE
See [`ImageClassificationDirectoryTreeImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.ImageClassificationDirectoryTreeImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from an image classification directory tree
stored in the above format as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/image-classification-dir-tree"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.ImageClassificationDirectoryTree,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/image-classification-dir-tree

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.ImageClassificationDirectoryTree

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

To view an image classification directory tree in the FiftyOne App
without creating a persistent FiftyOne dataset, you can execute:

```shell
DATASET_DIR=/path/to/image-classification-dir-tree

# View the dataset in the App
fiftyone app view \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.ImageClassificationDirectoryTree
```

<a id="videoclassificationdirectorytree-import"></a>

## Video Classification Dir Tree


<div class="available-in">
    <div class="available-in-row">
        <span class="available-in-label">Available in:</span>
        <span class="available-in-pill available-in-pill--oss">Open Source</span><span class="available-in-pill available-in-pill--enterprise">Enterprise</span>
    </div>
    <div class="available-in-row">
        <span class="available-in-versions">Introduced in <a href="../release-notes.html#fiftyone-0-6-3">FiftyOne 0.6.3</a> &middot; <a href="../release-notes.html#fiftyone-enterprise-1-0">FiftyOne Enterprise 1.0</a></span>
    </div>
    
</div>

The [`fiftyone.types.VideoClassificationDirectoryTree`](../api/fiftyone.types.md#fiftyone.types.VideoClassificationDirectoryTree) type represents a
directory tree whose subfolders define a video classification dataset.

Datasets of this type are read in the following format:

```text
<dataset_dir>/
    <classA>/
        <video1>.<ext>
        <video2>.<ext>
        ...
    <classB>/
        <video1>.<ext>
        <video2>.<ext>
        ...
    ...
```

Unlabeled videos are stored in a subdirectory named `_unlabeled`.

Each class folder may contain nested subfolders of videos.

#### NOTE
See [`VideoClassificationDirectoryTreeImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.VideoClassificationDirectoryTreeImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from a video classification directory tree
stored in the above format as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/video-classification-dir-tree"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.VideoClassificationDirectoryTree,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/video-classification-dir-tree

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.VideoClassificationDirectoryTree

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

To view a video classification directory tree in the FiftyOne App without
creating a persistent FiftyOne dataset, you can execute:

```shell
DATASET_DIR=/path/to/video-classification-dir-tree

# View the dataset in the App
fiftyone app view \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.VideoClassificationDirectoryTree
```

<a id="fiftyoneimageclassificationdataset-import"></a>

## FiftyOne Image Classification


<div class="available-in">
    <div class="available-in-row">
        <span class="available-in-label">Available in:</span>
        <span class="available-in-pill available-in-pill--oss">Open Source</span><span class="available-in-pill available-in-pill--enterprise">Enterprise</span>
    </div>
    <div class="available-in-row">
        <span class="available-in-versions">Introduced in <a href="../release-notes.html#fiftyone-0-14-1">FiftyOne 0.14.1</a> &middot; <a href="../release-notes.html#fiftyone-enterprise-1-0">FiftyOne Enterprise 1.0</a></span>
    </div>
    
</div>

The [`fiftyone.types.FiftyOneImageClassificationDataset`](../api/fiftyone.types.md#fiftyone.types.FiftyOneImageClassificationDataset) type represents
a labeled dataset consisting of images and their associated classification
label(s) stored in a simple JSON format.

Datasets of this type are read in the following format:

```text
<dataset_dir>/
    data/
        <uuid1>.<ext>
        <uuid2>.<ext>
        ...
    labels.json
```

In the simplest case, `labels.json` can be a JSON file in the following format:

```text
{
    "classes": [
        "<labelA>",
        "<labelB>",
        ...
    ],
    "labels": {
        "<uuid1>": <target>,
        "<uuid2>": <target>,
        ...
    }
}
```

If the `classes` field is provided, the `target` values are class IDs that are
mapped to class label strings via `classes[target]`. If no `classes` field is
provided, then the `target` values directly store the label strings.

The target value in `labels` for unlabeled images is `None` (or missing).

The UUIDs can also be relative paths like `path/to/uuid`, in which case the
images in `data/` should be arranged in nested subfolders with the
corresponding names, or they can be absolute paths, in which case the images
may or may not be in `data/`.

Alternatively, `labels.json` can contain predictions with associated
confidences and additional attributes in the following format:

```text
{
    "classes": [
        "<labelA>",
        "<labelB>",
        ...
    ],
    "labels": {
        "<uuid1>": {
            "label": <target>,
            "confidence": <optional-confidence>,
            "attributes": {
                <optional-name>: <optional-value>,
                ...
            }
        },
        "<uuid2>": {
            "label": <target>,
            "confidence": <optional-confidence>,
            "attributes": {
                <optional-name>: <optional-value>,
                ...
            }
        },
        ...
    }
}
```

You can also load multilabel classifications in this format by storing lists
of targets in `labels.json`:

```text
{
    "classes": [
        "<labelA>",
        "<labelB>",
        ...
    ],
    "labels": {
        "<uuid1>": [<target1>, <target2>, ...],
        "<uuid2>": [<target1>, <target2>, ...],
        ...
    }
}
```

where the target values in `labels` can be class strings, class IDs, or dicts
in the format described above defining class labels, confidences, and optional
attributes.

#### NOTE
See [`FiftyOneImageClassificationDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.FiftyOneImageClassificationDatasetImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from an image classification dataset stored
in the above format as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/image-classification-dataset"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.FiftyOneImageClassificationDataset,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/image-classification-dataset

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.FiftyOneImageClassificationDataset

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

To view an image classification dataset in the FiftyOne App without
creating a persistent FiftyOne dataset, you can execute:

```shell
DATASET_DIR=/path/to/image-classification-dataset

# View the dataset in the App
fiftyone app view \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.FiftyOneImageClassificationDataset
```

You can also independently specify the locations of the labels and the root
directory containing the corresponding media files by providing the
`labels_path` and `data_path` parameters rather than `dataset_dir`:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
data_path = "/path/to/images"
labels_path = "/path/to/labels.json"

# Import dataset by explicitly providing paths to the source media and labels
dataset = fo.Dataset.from_dir(
    dataset_type=fo.types.FiftyOneImageClassificationDataset,
    data_path=data_path,
    labels_path=labels_path,
    name=name,
)
```

```shell
NAME=my-dataset
DATA_PATH=/path/to/images
LABELS_PATH=/path/to/labels.json

# Import dataset by explicitly providing paths to the source media and labels
fiftyone datasets create \
    --name $NAME \
    --type fiftyone.types.FiftyOneImageClassificationDataset \
    --kwargs \
        data_path=$DATA_PATH \
        labels_path=$LABELS_PATH
```

#### NOTE
If the UUIDs in your labels are absolute paths to the source media, then
you can omit the `data_path` parameter from the example above.

<a id="tfimageclassificationdataset-import"></a>

## TF Image Classification

The [`fiftyone.types.TFImageClassificationDataset`](../api/fiftyone.types.md#fiftyone.types.TFImageClassificationDataset) type represents a
labeled dataset consisting of images and their associated classification labels
stored as
[TFRecords](https://www.tensorflow.org/tutorials/load_data/tfrecord).

Datasets of this type are read in the following format:

```text
<dataset_dir>/
    tf.records-?????-of-?????
```

where the features of the (possibly sharded) TFRecords are stored in the
following format:

```python
{
    # Image dimensions
    "height": tf.io.FixedLenFeature([], tf.int64),
    "width": tf.io.FixedLenFeature([], tf.int64),
    "depth": tf.io.FixedLenFeature([], tf.int64),
    # Image filename
    "filename": tf.io.FixedLenFeature([], tf.int64),
    # The image extension
    "format": tf.io.FixedLenFeature([], tf.string),
    # Encoded image bytes
    "image_bytes": tf.io.FixedLenFeature([], tf.string),
    # Class label string
    "label": tf.io.FixedLenFeature([], tf.string, default_value=""),
}
```

For unlabeled samples, the TFRecords do not contain `label` features.

#### NOTE
See [`TFImageClassificationDatasetImporter`](../api/fiftyone.utils.tf.md#fiftyone.utils.tf.TFImageClassificationDatasetImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from an image classification dataset stored
as a directory of TFRecords in the above format as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/tf-image-classification-dataset"
images_dir = "/path/for/images"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.TFImageClassificationDataset,
    images_dir=images_dir,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

When the above command is executed, the images in the TFRecords will be
written to the provided `images_dir`, which is required because FiftyOne
datasets must make their images available as individual files on disk.

```shell
NAME=my-dataset
DATASET_DIR=/path/to/tf-image-classification-dataset
IMAGES_DIR=/path/for/images

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.TFImageClassificationDataset \
    --kwargs images_dir=$IMAGES_DIR

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

When the above command is executed, the images in the TFRecords will be
written to the provided `IMAGES_DIR`, which is required because FiftyOne
datasets must make their images available as individual files on disk.

To view an image classification dataset stored as a directory of TFRecords
in the FiftyOne App without creating a persistent FiftyOne dataset,
you can execute:

```shell
DATASET_DIR=/path/to/tf-image-classification-dataset

# View the dataset in the App
fiftyone app view \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.TFImageClassificationDataset
```

#### NOTE
You can provide the `tf_records_path` argument instead of `dataset_dir` in
the examples above to directly specify the path to the TFRecord(s) to load.
See [`TFImageClassificationDatasetImporter`](../api/fiftyone.utils.tf.md#fiftyone.utils.tf.TFImageClassificationDatasetImporter)
for details.

<a id="cocodetectiondataset-import"></a>

## COCO


<div class="available-in">
    <div class="available-in-row">
        <span class="available-in-label">Available in:</span>
        <span class="available-in-pill available-in-pill--oss">Open Source</span><span class="available-in-pill available-in-pill--enterprise">Enterprise</span>
    </div>
    <div class="available-in-row">
        <span class="available-in-versions">Introduced in <a href="../release-notes.html#fiftyone-0-3-0">FiftyOne 0.3.0</a> &middot; <a href="../release-notes.html#fiftyone-enterprise-1-0">FiftyOne Enterprise 1.0</a></span>
    </div>
    
</div>

The [`fiftyone.types.COCODetectionDataset`](../api/fiftyone.types.md#fiftyone.types.COCODetectionDataset) type represents a labeled
dataset consisting of images and their associated object detections saved in
[COCO Object Detection Format](https://cocodataset.org/#format-data).

Datasets of this type are read in the following format:

```text
<dataset_dir>/
    data/
        <filename0>.<ext>
        <filename1>.<ext>
        ...
    labels.json
```

where `labels.json` is a JSON file in the following format:

```text
{
    "info": {...},
    "licenses": [
        {
            "id": 1,
            "name": "Attribution-NonCommercial-ShareAlike License",
            "url": "http://creativecommons.org/licenses/by-nc-sa/2.0/",
        },
        ...
    ],
    "categories": [
        {
            "id": 1,
            "name": "cat",
            "supercategory": "animal",
            "keypoints": ["nose", "head", ...],
            "skeleton": [[12, 14], [14, 16], ...]
        },
        ...
    ],
    "images": [
        {
            "id": 1,
            "license": 1,
            "file_name": "<filename0>.<ext>",
            "height": 480,
            "width": 640,
            "date_captured": null
        },
        ...
    ],
    "annotations": [
        {
            "id": 1,
            "image_id": 1,
            "category_id": 1,
            "bbox": [260, 177, 231, 199],
            "segmentation": [...],
            "keypoints": [224, 226, 2, ...],
            "num_keypoints": 10,
            "score": 0.95,
            "area": 45969,
            "iscrowd": 0
        },
        ...
    ]
}
```

See [this page](https://cocodataset.org/#format-data) for a full
specification of the `segmentation` field.

For unlabeled datasets, `labels.json` does not contain an `annotations` field.

The `file_name` attribute of the labels file encodes the location of the
corresponding images, which can be any of the following:

- The filename of an image in the `data/` folder
- A relative path like `data/sub/folder/filename.ext` specifying the relative
  path to the image in a nested subfolder of `data/`
- An absolute path to an image, which may or may not be in the `data/` folder

#### NOTE
See [`COCODetectionDatasetImporter`](../api/fiftyone.utils.coco.md#fiftyone.utils.coco.COCODetectionDatasetImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from a COCO detection dataset stored in the
above format as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/coco-detection-dataset"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.COCODetectionDataset,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/coco-detection-dataset

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.COCODetectionDataset

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

To view a COCO detection dataset stored in the above format in the FiftyOne
App without creating a persistent FiftyOne dataset, you can execute:

```shell
DATASET_DIR=/path/to/coco-detection-dataset

# View the dataset in the App
fiftyone app view \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.COCODetectionDataset
```

#### NOTE
By default, all supported label types are loaded (detections,
segmentations, and keypoints). However, you can choose specific type(s) to
load by passing the optional `label_types` argument to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir):

```python
# Only load bounding boxes
dataset = fo.Dataset.from_dir(
    dataset_type=fo.types.COCODetectionDataset,
    label_types=["detections"],
    ...
)
```

See [`COCODetectionDatasetImporter`](../api/fiftyone.utils.coco.md#fiftyone.utils.coco.COCODetectionDatasetImporter)
for complete documentation of the available COCO import options.

You can also independently specify the locations of the labels and the root
directory containing the corresponding media files by providing the
`labels_path` and `data_path` parameters rather than `dataset_dir`:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
data_path = "/path/to/images"
labels_path = "/path/to/coco-labels.json"

# Import dataset by explicitly providing paths to the source media and labels
dataset = fo.Dataset.from_dir(
    dataset_type=fo.types.COCODetectionDataset,
    data_path=data_path,
    labels_path=labels_path,
    name=name,
)
```

```shell
NAME=my-dataset
DATA_PATH=/path/to/images
LABELS_PATH=/path/to/coco-labels.json

# Import dataset by explicitly providing paths to the source media and labels
fiftyone datasets create \
    --name $NAME \
    --type fiftyone.types.COCODetectionDataset \
    --kwargs \
        data_path=$DATA_PATH \
        labels_path=$LABELS_PATH
```

#### NOTE
If the `file_name` key of your labels contains absolute paths to the source
media, then you can omit the `data_path` parameter from the example above.

If you have an existing dataset and corresponding model predictions stored in
COCO format, then you can use
[`add_coco_labels()`](../api/fiftyone.utils.coco.md#fiftyone.utils.coco.add_coco_labels) to conveniently
add the labels to the dataset. The example below demonstrates a round-trip
export and then re-import of both images-and-labels and labels-only data in
COCO format:

```python
import fiftyone as fo
import fiftyone.zoo as foz
import fiftyone.utils.coco as fouc

dataset = foz.load_zoo_dataset("quickstart")
classes = dataset.distinct("predictions.detections.label")

# Export images and ground truth labels to disk
dataset.export(
    export_dir="/tmp/coco",
    dataset_type=fo.types.COCODetectionDataset,
    label_field="ground_truth",
    classes=classes,
)

# Export predictions
dataset.export(
    dataset_type=fo.types.COCODetectionDataset,
    labels_path="/tmp/coco/predictions.json",
    label_field="predictions",
    classes=classes,
)

# Now load ground truth labels into a new dataset
dataset2 = fo.Dataset.from_dir(
    dataset_dir="/tmp/coco",
    dataset_type=fo.types.COCODetectionDataset,
    label_field="ground_truth",
)

# And add model predictions
fouc.add_coco_labels(
    dataset2,
    "predictions",
    "/tmp/coco/predictions.json",
    classes,
)

# Verify that ground truth and predictions were imported as expected
print(dataset.count("ground_truth.detections"))
print(dataset2.count("ground_truth.detections"))
print(dataset.count("predictions.detections"))
print(dataset2.count("predictions.detections"))
```

#### NOTE
See [`add_coco_labels()`](../api/fiftyone.utils.coco.md#fiftyone.utils.coco.add_coco_labels) for a
complete description of the available syntaxes for loading COCO-formatted
predictions to an existing dataset.

<a id="vocdetectiondataset-import"></a>

## VOC

The [`fiftyone.types.VOCDetectionDataset`](../api/fiftyone.types.md#fiftyone.types.VOCDetectionDataset) type represents a labeled
dataset consisting of images and their associated object detections saved in
[VOC format](http://host.robots.ox.ac.uk/pascal/VOC).

Datasets of this type are read in the following format:

```text
<dataset_dir>/
    data/
        <uuid1>.<ext>
        <uuid2>.<ext>
        ...
    labels/
        <uuid1>.xml
        <uuid2>.xml
        ...
```

where the labels XML files are in the following format:

```xml
<annotation>
    <folder></folder>
    <filename>image.ext</filename>
    <path>/path/to/dataset-dir/data/image.ext</path>
    <source>
        <database></database>
    </source>
    <size>
        <width>640</width>
        <height>480</height>
        <depth>3</depth>
    </size>
    <segmented></segmented>
    <object>
        <name>cat</name>
        <pose></pose>
        <truncated>0</truncated>
        <difficult>0</difficult>
        <occluded>0</occluded>
        <bndbox>
            <xmin>256</xmin>
            <ymin>200</ymin>
            <xmax>450</xmax>
            <ymax>400</ymax>
        </bndbox>
    </object>
    <object>
        <name>dog</name>
        <pose></pose>
        <truncated>1</truncated>
        <difficult>1</difficult>
        <occluded>1</occluded>
        <bndbox>
            <xmin>128</xmin>
            <ymin>100</ymin>
            <xmax>350</xmax>
            <ymax>300</ymax>
        </bndbox>
    </object>
    ...
</annotation>
```

where either the `<filename>` and/or `<path>` field of the annotations may be
populated to specify the corresponding source image.

Unlabeled images have no corresponding file in `labels/`.

The `data/` and `labels/` files may contain nested subfolders of parallelly
organized images and masks.

#### NOTE
See [`VOCDetectionDatasetImporter`](../api/fiftyone.utils.voc.md#fiftyone.utils.voc.VOCDetectionDatasetImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from a VOC detection dataset stored in the
above format as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/voc-detection-dataset"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.VOCDetectionDataset,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/voc-detection-dataset

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.VOCDetectionDataset

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

To view a VOC detection dataset stored in the above format in the FiftyOne
App without creating a persistent FiftyOne dataset, you can execute:

```shell
DATASET_DIR=/path/to/voc-detection-dataset

# View the dataset in the App
fiftyone app view \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.VOCDetectionDataset
```

You can also independently specify the locations of the labels and the root
directory containing the corresponding media files by providing the
`labels_path` and `data_path` parameters rather than `dataset_dir`:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
data_path = "/path/to/images"
labels_path = "/path/to/voc-labels"

# Import dataset by explicitly providing paths to the source media and labels
dataset = fo.Dataset.from_dir(
    dataset_type=fo.types.VOCDetectionDataset,
    data_path=data_path,
    labels_path=labels_path,
    name=name,
)
```

```shell
NAME=my-dataset
DATA_PATH=/path/to/images
LABELS_PATH=/path/to/voc-labels

# Import dataset by explicitly providing paths to the source media and labels
fiftyone datasets create \
    --name $NAME \
    --type fiftyone.types.VOCDetectionDataset \
    --kwargs \
        data_path=$DATA_PATH \
        labels_path=$LABELS_PATH
```

#### NOTE
If the `<path>` field of your labels are populated with the absolute paths
to the source media, then you can omit the `data_path` parameter from the
example above.

<a id="kittidetectiondataset-import"></a>

## KITTI

The [`fiftyone.types.KITTIDetectionDataset`](../api/fiftyone.types.md#fiftyone.types.KITTIDetectionDataset) type represents a labeled
dataset consisting of images and their associated object detections saved in
[KITTI format](http://www.cvlibs.net/datasets/kitti/eval_object.php).

Datasets of this type are read in the following format:

```text
<dataset_dir>/
    data/
        <uuid1>.<ext>
        <uuid2>.<ext>
        ...
    labels/
        <uuid1>.txt
        <uuid2>.txt
        ...
```

where the labels TXT files are space-delimited files where each row corresponds
to an object and the 15 (and optional 16th score) columns have the following
meanings:

|   # of<br/>columns | Name       | Description                                                                                                                                    |   Default |
|--------------------|------------|------------------------------------------------------------------------------------------------------------------------------------------------|-----------|
|                  1 | type       | The object label                                                                                                                               |           |
|                  1 | truncated  | A float in `[0, 1]`, where 0 is non-truncated and<br/>1 is fully truncated. Here, truncation refers to the object<br/>leaving image boundaries |         0 |
|                  1 | occluded   | An int in `(0, 1, 2, 3)` indicating occlusion state,<br/>where:- 0 = fully visible- 1 = partly occluded- 2 =<br/>largely occluded- 3 = unknown |         0 |
|                  1 | alpha      | Observation angle of the object, in `[-pi, pi]`                                                                                                |         0 |
|                  4 | bbox       | 2D bounding box of object in the image in pixels, in the<br/>format `[xtl, ytl, xbr, ybr]`                                                     |           |
|                  1 | dimensions | 3D object dimensions, in meters, in the format<br/>`[height, width, length]`                                                                   |         0 |
|                  1 | location   | 3D object location `(x, y, z)` in camera coordinates<br/>(in meters)                                                                           |         0 |
|                  1 | rotation_y | Rotation around the y-axis in camera coordinates, in<br/>`[-pi, pi]`                                                                           |         0 |
|                  1 | score      | `(optional)` A float confidence for the detection                                                                                              |           |

When reading datasets of this type, all columns after the four `bbox` columns
are optional.

Unlabeled images have no corresponding file in `labels/`.

The `data/` and `labels/` files may contain nested subfolders of parallelly
organized images and masks.

#### NOTE
See [`KITTIDetectionDatasetImporter`](../api/fiftyone.utils.kitti.md#fiftyone.utils.kitti.KITTIDetectionDatasetImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from a KITTI detection dataset stored in the
above format as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/kitti-detection-dataset"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.KITTIDetectionDataset,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/kitti-detection-dataset

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.KITTIDetectionDataset

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

To view a KITTI detection dataset stored in the above format in the
FiftyOne App without creating a persistent FiftyOne dataset, you can
execute:

```shell
DATASET_DIR=/path/to/kitti-detection-dataset

# View the dataset in the App
fiftyone app view \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.KITTIDetectionDataset
```

You can also independently specify the locations of the labels and the root
directory containing the corresponding media files by providing the
`labels_path` and `data_path` parameters rather than `dataset_dir`:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
data_path = "/path/to/images"
labels_path = "/path/to/kitti-labels"

# Import dataset by explicitly providing paths to the source media and labels
dataset = fo.Dataset.from_dir(
    dataset_type=fo.types.KITTIDetectionDataset,
    data_path=data_path,
    labels_path=labels_path,
    name=name,
)
```

```shell
NAME=my-dataset
DATA_PATH=/path/to/images
LABELS_PATH=/path/to/kitti-labels

# Import dataset by explicitly providing paths to the source media and labels
fiftyone datasets create \
    --name $NAME \
    --type fiftyone.types.KITTIDetectionDataset \
    --kwargs \
        data_path=$DATA_PATH \
        labels_path=$LABELS_PATH
```

<a id="yolov4dataset-import"></a>

## YOLOv4


<div class="available-in">
    <div class="available-in-row">
        <span class="available-in-label">Available in:</span>
        <span class="available-in-pill available-in-pill--oss">Open Source</span><span class="available-in-pill available-in-pill--enterprise">Enterprise</span>
    </div>
    <div class="available-in-row">
        <span class="available-in-versions">Introduced in <a href="../release-notes.html#fiftyone-0-6-1">FiftyOne 0.6.1</a> &middot; <a href="../release-notes.html#fiftyone-enterprise-1-0">FiftyOne Enterprise 1.0</a></span>
    </div>
    
</div>

The [`fiftyone.types.YOLOv4Dataset`](../api/fiftyone.types.md#fiftyone.types.YOLOv4Dataset) type represents a labeled dataset
consisting of images and their associated object detections saved in
[YOLOv4 format](https://github.com/AlexeyAB/darknet).

Datasets of this type are read in the following format:

```text
<dataset_dir>/
    obj.names
    images.txt
    data/
        <uuid1>.<ext>
        <uuid1>.txt
        <uuid2>.<ext>
        <uuid2>.txt
        ...
```

where `obj.names` contains the object class labels:

```text
<label-0>
<label-1>
...
```

and `images.txt` contains the list of images in `data/`:

```text
data/<uuid1>.<ext>
data/<uuid2>.<ext>
...
```

The image paths in `images.txt` can be specified as either relative (to the
location of file) or as absolute paths. Alternatively, this file can be
omitted, in which case the `data/` directory is listed to determine the
available images.

The TXT files in `data/` are space-delimited files where each row corresponds
to an object in the image of the same name, in one of the following formats:

```text
# Detections
<target> <x-center> <y-center> <width> <height>
<target> <x-center> <y-center> <width> <height> <confidence>

# Instance segmentations or polygons
<target> <x1> <y1> <x2> <y2> <x3> <y3> ...
```

where `<target>` is the zero-based integer index of the object class label from
`obj.names`, all coordinates are expressed as relative values in
`[0, 1] x [0, 1]`, and `<confidence>` is an optional confidence in `[0, 1]`.

Unlabeled images have no corresponding TXT file in `data/`.

The `data/` folder may contain nested subfolders.

#### NOTE
By default, all annotations are loaded as [`Detections`](../api/fiftyone.core.labels.md#fiftyone.core.labels.Detections), converting any
polylines to tight bounding boxes if necessary. However, you can choose to
load YOLO annotations as instance segmentations or polygons by passing the
optional `label_type` argument to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir):

```python
# Load annotations as instance segmentations
dataset = fo.Dataset.from_dir(
    dataset_type=fo.types.YOLOv4Dataset,
    label_type="instances",
    mask_size=(width, height),  # optional size for each dense mask
    ...
)

# Load annotations as polygons
dataset = fo.Dataset.from_dir(
    dataset_type=fo.types.YOLOv4Dataset,
    label_type="polylines",
    ...
)
```

See [`YOLOv4DatasetImporter`](../api/fiftyone.utils.yolo.md#fiftyone.utils.yolo.YOLOv4DatasetImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from a YOLOv4 dataset stored in the above
format as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/yolov4-dataset"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.YOLOv4Dataset,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/yolov4-dataset

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.YOLOv4Dataset

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

To view a YOLOv4 dataset stored in the above format in the FiftyOne App
without creating a persistent FiftyOne dataset, you can execute:

```shell
DATASET_DIR=/path/to/yolov4-dataset

# View the dataset in the App
fiftyone app view \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.YOLOv4Dataset
```

You can also independently specify the locations of the labels and the root
directory containing the corresponding media files by providing the
`labels_path` and `data_path` parameters rather than `dataset_dir`:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
data_path = "/path/to/images"
labels_path = "/path/to/yolo-labels"
classes = ["list", "of", "classes"]

# Import dataset by explicitly providing paths to the source media and labels
dataset = fo.Dataset.from_dir(
    dataset_type=fo.types.YOLOv4Dataset,
    data_path=data_path,
    labels_path=labels_path,
    classes=classes,
    name=name,
)
```

```shell
NAME=my-dataset
DATA_PATH=/path/to/images
LABELS_PATH=/path/to/yolo-labels
OBJECTS_PATH=/path/to/obj.names

# Import dataset by explicitly providing paths to the source media and labels
fiftyone datasets create \
    --name $NAME \
    --type fiftyone.types.YOLOv4Dataset \
    --kwargs \
        data_path=$DATA_PATH \
        labels_path=$LABELS_PATH \
        objects_path=$OBJECTS_PATH
```

If you have an existing dataset and corresponding model predictions stored in
YOLO format, then you can use
[`add_yolo_labels()`](../api/fiftyone.utils.yolo.md#fiftyone.utils.yolo.add_yolo_labels) to conveniently
add the labels to the dataset.

The example below demonstrates a round-trip export and then re-import of both
images-and-labels and labels-only data in YOLO format:

```python
import fiftyone as fo
import fiftyone.zoo as foz
import fiftyone.utils.yolo as fouy

dataset = foz.load_zoo_dataset("quickstart")
classes = dataset.distinct("predictions.detections.label")

# Export images and ground truth labels to disk
dataset.export(
    export_dir="/tmp/yolov4",
    dataset_type=fo.types.YOLOv4Dataset,
    label_field="ground_truth",
    classes=classes,
)

# Export predictions
dataset.export(
    dataset_type=fo.types.YOLOv4Dataset,
    labels_path="/tmp/yolov4/predictions",
    label_field="predictions",
    classes=classes,
)

# Now load ground truth labels into a new dataset
dataset2 = fo.Dataset.from_dir(
    dataset_dir="/tmp/yolov4",
    dataset_type=fo.types.YOLOv4Dataset,
    label_field="ground_truth",
)

# And add model predictions
fouy.add_yolo_labels(
    dataset2,
    "predictions",
    "/tmp/yolov4/predictions",
    classes,
)

# Verify that ground truth and predictions were imported as expected
print(dataset.count("ground_truth.detections"))
print(dataset2.count("ground_truth.detections"))
print(dataset.count("predictions.detections"))
print(dataset2.count("predictions.detections"))
```

#### NOTE
See [`add_yolo_labels()`](../api/fiftyone.utils.yolo.md#fiftyone.utils.yolo.add_yolo_labels) for a
complete description of the available syntaxes for loading YOLO-formatted
predictions to an existing dataset.

<a id="yolov5dataset-import"></a>

## YOLOv5


<div class="available-in">
    <div class="available-in-row">
        <span class="available-in-label">Available in:</span>
        <span class="available-in-pill available-in-pill--oss">Open Source</span><span class="available-in-pill available-in-pill--enterprise">Enterprise</span>
    </div>
    <div class="available-in-row">
        <span class="available-in-versions">Introduced in <a href="../release-notes.html#fiftyone-0-11-0">FiftyOne 0.11.0</a> &middot; <a href="../release-notes.html#fiftyone-enterprise-1-0">FiftyOne Enterprise 1.0</a></span>
    </div>
    
</div>

The [`fiftyone.types.YOLOv5Dataset`](../api/fiftyone.types.md#fiftyone.types.YOLOv5Dataset) type represents a labeled dataset
consisting of images and their associated object detections saved in
[YOLOv5 format](https://github.com/ultralytics/yolov5).

Datasets of this type are read in the following format:

```text
<dataset_dir>/
    dataset.yaml
    images/
        train/
            <uuid1>.<ext>
            <uuid2>.<ext>
            ...
        val/
            <uuid3>.<ext>
            <uuid4>.<ext>
            ...
    labels/
        train/
            <uuid1>.txt
            <uuid2>.txt
            ...
        val/
            <uuid3>.txt
            <uuid4>.txt
            ...
```

where `dataset.yaml` contains the following information:

```text
path: <dataset_dir>  # optional
train: ./images/train/
val: ./images/val/

names:
  0: list
  1: of
  2: classes
  ...
```

See [this page](https://docs.ultralytics.com/datasets/detect) for a full
description of the possible format of `dataset.yaml`. In particular, the
dataset may contain one or more splits with arbitrary names, as the specific
split being imported or exported is specified by the `split` argument to
[`fiftyone.utils.yolo.YOLOv5DatasetImporter`](../api/fiftyone.utils.yolo.md#fiftyone.utils.yolo.YOLOv5DatasetImporter). Also, `dataset.yaml` can be
located outside of `<dataset_dir>` as long as the optional `path` is provided.

#### NOTE
Any relative paths in `dataset.yaml` or per-split TXT files are interpreted
relative to the directory containing these files, not your current working
directory.

The TXT files in `labels/` are space-delimited files where each row corresponds
to an object in the image of the same name, in one of the following formats:

```text
# Detections
<target> <x-center> <y-center> <width> <height>
<target> <x-center> <y-center> <width> <height> <confidence>

# Instance segmentations or polygons
<target> <x1> <y1> <x2> <y2> <x3> <y3> ...
```

where `<target>` is the zero-based integer index of the object class label from
`names`, all coordinates are expressed as relative values in `[0, 1] x [0, 1]`,
and `<confidence>` is an optional confidence in `[0, 1]`.

Unlabeled images have no corresponding TXT file in `labels/`. The label file
path for each image is obtained by replacing `images/` with `labels/` in the
respective image path.

The image and labels directories for a given split may contain nested
subfolders of parallelly organized images and labels.

#### NOTE
By default, all annotations are loaded as [`Detections`](../api/fiftyone.core.labels.md#fiftyone.core.labels.Detections), converting any
polylines to tight bounding boxes if necessary. However, you can choose to
load YOLO annotations as instance segmentations or polygons by passing the
optional `label_type` argument to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir):

```python
# Load annotations as instance segmentations
dataset = fo.Dataset.from_dir(
    dataset_type=fo.types.YOLOv5Dataset,
    label_type="instances",
    mask_size=(width, height),  # optional size for each dense mask
    ...
)

# Load annotations as polygons
dataset = fo.Dataset.from_dir(
    dataset_type=fo.types.YOLOv5Dataset,
    label_type="polylines",
    ...
)
```

See [`YOLOv5DatasetImporter`](../api/fiftyone.utils.yolo.md#fiftyone.utils.yolo.YOLOv5DatasetImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from a YOLOv5 dataset stored in the above
format as follows:

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/yolov5-dataset"

# The splits to load
splits = ["train", "val"]

# Load the dataset, using tags to mark the samples in each split
dataset = fo.Dataset(name)
for split in splits:
    dataset.add_dir(
        dataset_dir=dataset_dir,
        dataset_type=fo.types.YOLOv5Dataset,
        split=split,
        tags=split,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

If you have an existing dataset and corresponding model predictions stored in
YOLO format, then you can use
[`add_yolo_labels()`](../api/fiftyone.utils.yolo.md#fiftyone.utils.yolo.add_yolo_labels) to conveniently
add the labels to the dataset.

The example below demonstrates a round-trip export and then re-import of both
images-and-labels and labels-only data in YOLO format:

```python
import fiftyone as fo
import fiftyone.zoo as foz
import fiftyone.utils.yolo as fouy

dataset = foz.load_zoo_dataset("quickstart")
classes = dataset.distinct("predictions.detections.label")

# YOLOv5 format supports splits, so let's grab only the `validation` split
view = dataset.match_tags("validation")

# Export images and ground truth labels to disk
view.export(
    export_dir="/tmp/yolov5",
    dataset_type=fo.types.YOLOv5Dataset,
    split="validation",
    label_field="ground_truth",
    classes=classes,
)

# Export predictions
view.export(
    dataset_type=fo.types.YOLOv5Dataset,
    labels_path="/tmp/yolov5/predictions/validation",
    label_field="predictions",
    classes=classes,
)

# Now load ground truth labels into a new dataset
dataset2 = fo.Dataset.from_dir(
    dataset_dir="/tmp/yolov5",
    dataset_type=fo.types.YOLOv5Dataset,
    split="validation",
    label_field="ground_truth",
)

# And add model predictions
fouy.add_yolo_labels(
    dataset2,
    "predictions",
    "/tmp/yolov5/predictions/validation",
    classes,
)

# Verify that ground truth and predictions were imported as expected
print(view.count("ground_truth.detections"))
print(dataset2.count("ground_truth.detections"))
print(view.count("predictions.detections"))
print(dataset2.count("predictions.detections"))
```

#### NOTE
See [`add_yolo_labels()`](../api/fiftyone.utils.yolo.md#fiftyone.utils.yolo.add_yolo_labels) for a
complete description of the available syntaxes for loading YOLO-formatted
predictions to an existing dataset.

<a id="fiftyoneimagedetectiondataset-import"></a>

## FiftyOne Object Detection

The [`fiftyone.types.FiftyOneImageDetectionDataset`](../api/fiftyone.types.md#fiftyone.types.FiftyOneImageDetectionDataset) type represents a
labeled dataset consisting of images and their associated object detections
stored in a simple JSON format.

Datasets of this type are read in the following format:

```text
<dataset_dir>/
    data/
        <uuid1>.<ext>
        <uuid2>.<ext>
        ...
    labels.json
```

where `labels.json` is a JSON file in the following format:

```text
{
    "classes": [
        <labelA>,
        <labelB>,
        ...
    ],
    "labels": {
        <uuid1>: [
            {
                "label": <target>,
                "bounding_box": [
                    <top-left-x>, <top-left-y>, <width>, <height>
                ],
                "confidence": <optional-confidence>,
                "attributes": {
                    <optional-name>: <optional-value>,
                    ...
                }
            },
            ...
        ],
        <uuid2>: [
            ...
        ],
        ...
    }
}
```

and where the bounding box coordinates are expressed as relative values in
`[0, 1] x [0, 1]`.

If the `classes` field is provided, the `target` values are class IDs that are
mapped to class label strings via `classes[target]`. If no `classes` field is
provided, then the `target` values directly store the label strings.

The target value in `labels` for unlabeled images is `None` (or missing).

The UUIDs can also be relative paths like `path/to/uuid`, in which case the
images in `data/` should be arranged in nested subfolders with the
corresponding names, or they can be absolute paths, in which case the images
may or may not be in `data/`.

#### NOTE
See [`FiftyOneImageDetectionDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.FiftyOneImageDetectionDatasetImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from an image detection dataset stored in the
above format as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/image-detection-dataset"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.FiftyOneImageDetectionDataset,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/image-detection-dataset

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.FiftyOneImageDetectionDataset

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

To view an image detection dataset stored in the above format in the
FiftyOne App without creating a persistent FiftyOne dataset, you
can execute:

```shell
DATASET_DIR=/path/to/image-detection-dataset

# View the dataset in the App
fiftyone app view \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.FiftyOneImageDetectionDataset
```

You can also independently specify the locations of the labels and the root
directory containing the corresponding media files by providing the
`labels_path` and `data_path` parameters rather than `dataset_dir`:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
data_path = "/path/to/images"
labels_path = "/path/to/labels.json"

# Import dataset by explicitly providing paths to the source media and labels
dataset = fo.Dataset.from_dir(
    dataset_type=fo.types.FiftyOneImageDetectionDataset,
    data_path=data_path,
    labels_path=labels_path,
    name=name,
)
```

```shell
NAME=my-dataset
DATA_PATH=/path/to/images
LABELS_PATH=/path/to/labels.json

# Import dataset by explicitly providing paths to the source media and labels
fiftyone datasets create \
    --name $NAME \
    --type fiftyone.types.FiftyOneImageDetectionDataset \
    --kwargs \
        data_path=$DATA_PATH \
        labels_path=$LABELS_PATH
```

#### NOTE
If the UUIDs in your labels are absolute paths to the source media, then
you can omit the `data_path` parameter from the example above.

<a id="fiftyonetemporaldetectiondataset-import"></a>

## FiftyOne Temporal Detection

The [`fiftyone.types.FiftyOneTemporalDetectionDataset`](../api/fiftyone.types.md#fiftyone.types.FiftyOneTemporalDetectionDataset) type represents a
labeled dataset consisting of videos and their associated temporal detections
stored in a simple JSON format.

Datasets of this type are read in the following format:

```text
<dataset_dir>/
    data/
        <uuid1>.<ext>
        <uuid2>.<ext>
        ...
    labels.json
```

where `labels.json` is a JSON file in the following format:

```text
{
    "classes": [
        "<labelA>",
        "<labelB>",
        ...
    ],
    "labels": {
        "<uuid1>": [
            {
                "label": <target>,
                "support": [<first-frame>, <last-frame>],
                "confidence": <optional-confidence>,
                "attributes": {
                    <optional-name>: <optional-value>,
                    ...
                }
            },
            {
                "label": <target>,
                "support": [<first-frame>, <last-frame>],
                "confidence": <optional-confidence>,
                "attributes": {
                    <optional-name>: <optional-value>,
                    ...
                }
            },
            ...
        ],
        "<uuid2>": [
            {
                "label": <target>,
                "timestamps": [<start-timestamp>, <stop-timestamp>],
                "confidence": <optional-confidence>,
                "attributes": {
                    <optional-name>: <optional-value>,
                    ...
                }
            },
            {
                "label": <target>,
                "timestamps": [<start-timestamp>, <stop-timestamp>],
                "confidence": <optional-confidence>,
                "attributes": {
                    <optional-name>: <optional-value>,
                    ...
                }
            },
        ],
        ...
    }
}
```

The temporal range of each detection can be specified either via the `support`
key, which should contain the `[first, last]` frame numbers of the detection,
or the `timestamps` key, which should contain the `[start, stop]` timestamps of
the detection in seconds.

If the `classes` field is provided, the `target` values are class IDs that are
mapped to class label strings via `classes[target]`. If no `classes` field is
provided, then the `target` values directly store the label strings.

Unlabeled videos can have a `None` (or missing) key in `labels`.

The UUIDs can also be relative paths like `path/to/uuid`, in which case the
images in `data/` should be arranged in nested subfolders with the
corresponding names, or they can be absolute paths, in which case the images
may or may not be in `data/`.

#### NOTE
See [`FiftyOneTemporalDetectionDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.FiftyOneTemporalDetectionDatasetImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from a temporal detection dataset stored in
the above format as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/temporal-detection-dataset"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.FiftyOneTemporalDetectionDataset,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/temporal-detection-dataset

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.FiftyOneTemporalDetectionDataset

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

To view a temporal detection dataset in the FiftyOne App without creating
a persistent FiftyOne dataset, you can execute:

```shell
DATASET_DIR=/path/to/temporal-detection-dataset

# View the dataset in the App
fiftyone app view \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.FiftyOneTemporalDetectionDataset
```

You can also independently specify the locations of the labels and the root
directory containing the corresponding media files by providing the
`labels_path` and `data_path` parameters rather than `dataset_dir`:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
data_path = "/path/to/images"
labels_path = "/path/to/labels.json"

# Import dataset by explicitly providing paths to the source media and labels
dataset = fo.Dataset.from_dir(
    dataset_type=fo.types.FiftyOneTemporalDetectionDataset,
    data_path=data_path,
    labels_path=labels_path,
    name=name,
)
```

```shell
NAME=my-dataset
DATA_PATH=/path/to/images
LABELS_PATH=/path/to/labels.json

# Import dataset by explicitly providing paths to the source media and labels
fiftyone datasets create \
    --name $NAME \
    --type fiftyone.types.FiftyOneTemporalDetectionDataset \
    --kwargs \
        data_path=$DATA_PATH \
        labels_path=$LABELS_PATH
```

#### NOTE
If the UUIDs in your labels are absolute paths to the source media, then
you can omit the `data_path` parameter from the example above.

<a id="tfobjectdetectiondataset-import"></a>

## TF Object Detection

The [`fiftyone.types.TFObjectDetectionDataset`](../api/fiftyone.types.md#fiftyone.types.TFObjectDetectionDataset) type represents a labeled
dataset consisting of images and their associated object detections stored as
[TFRecords](https://www.tensorflow.org/tutorials/load_data/tfrecord) in
[TF Object Detection API format](https://github.com/tensorflow/models/blob/master/research/object_detection).

Datasets of this type are read in the following format:

```text
<dataset_dir>/
    tf.records-?????-of-?????
```

where the features of the (possibly sharded) TFRecords are stored in the
following format:

```python
{
    # Image dimensions
    "image/height": tf.io.FixedLenFeature([], tf.int64),
    "image/width": tf.io.FixedLenFeature([], tf.int64),

    # Image filename is used for both of these when writing
    "image/filename": tf.io.FixedLenFeature([], tf.string),
    "image/source_id": tf.io.FixedLenFeature([], tf.string),

    # Encoded image bytes
    "image/encoded": tf.io.FixedLenFeature([], tf.string),

    # Image format, either `jpeg` or `png`
    "image/format": tf.io.FixedLenFeature([], tf.string),

    # Normalized bounding box coordinates in `[0, 1]`
    "image/object/bbox/xmin": tf.io.FixedLenSequenceFeature(
        [], tf.float32, allow_missing=True
    ),
    "image/object/bbox/xmax": tf.io.FixedLenSequenceFeature(
        [], tf.float32, allow_missing=True
    ),
    "image/object/bbox/ymin": tf.io.FixedLenSequenceFeature(
        [], tf.float32, allow_missing=True
    ),
    "image/object/bbox/ymax": tf.io.FixedLenSequenceFeature(
        [], tf.float32, allow_missing=True
    ),

    # Class label string
    "image/object/class/text": tf.io.FixedLenSequenceFeature(
        [], tf.string, allow_missing=True
    ),

    # Integer class ID
    "image/object/class/label": tf.io.FixedLenSequenceFeature(
        [], tf.int64, allow_missing=True
    ),
}
```

The TFRecords for unlabeled samples do not contain `image/object/*` features.

#### NOTE
See [`TFObjectDetectionDatasetImporter`](../api/fiftyone.utils.tf.md#fiftyone.utils.tf.TFObjectDetectionDatasetImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from an object detection dataset stored as a
directory of TFRecords in the above format as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/tf-object-detection-dataset"
images_dir = "/path/for/images"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.TFObjectDetectionDataset,
    images_dir=images_dir,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

When the above command is executed, the images in the TFRecords will be
written to the provided `images_dir`, which is required because FiftyOne
datasets must make their images available as individual files on disk.

```shell
NAME=my-dataset
DATASET_DIR=/path/to/tf-object-detection-dataset
IMAGES_DIR=/path/for/images

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.TFObjectDetectionDataset \
    --kwargs images_dir=$IMAGES_DIR

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

When the above command is executed, the images in the TFRecords will be
written to the provided `IMAGES_DIR`, which is required because FiftyOne
datasets must make their images available as individual files on disk.

To view an object detection dataset stored as a directory of TFRecords in
the FiftyOne App without creating a persistent FiftyOne dataset, you can
execute:

```shell
DATASET_DIR=/path/to/tf-object-detection-dataset

# View the dataset in the App
fiftyone app view \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.TFObjectDetectionDataset
```

#### NOTE
You can provide the `tf_records_path` argument instead of `dataset_dir` in
the examples above to directly specify the path to the TFRecord(s) to load.
See [`TFObjectDetectionDatasetImporter`](../api/fiftyone.utils.tf.md#fiftyone.utils.tf.TFObjectDetectionDatasetImporter)
for details.

<a id="imagesegmentationdirectory-import"></a>

## Image Segmentation Directory

The [`fiftyone.types.ImageSegmentationDirectory`](../api/fiftyone.types.md#fiftyone.types.ImageSegmentationDirectory) type represents a
labeled dataset consisting of images and their associated semantic
segmentations stored as images on disk.

Datasets of this type are read in the following format:

```text
<dataset_dir>/
    data/
        <filename1>.<ext>
        <filename2>.<ext>
        ...
    labels/
        <filename1>.<ext>
        <filename2>.<ext>
        ...
```

where `labels/` contains the semantic segmentations stored as images.

Unlabeled images have no corresponding file in `labels/`.

The `data/` and `labels/` files may contain nested subfolders of parallelly
organized images and masks.

#### NOTE
See [`ImageSegmentationDirectoryImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.ImageSegmentationDirectoryImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from an image segmentation dataset stored in
the above format as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/image-segmentation-dataset"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.ImageSegmentationDirectory,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/image-segmentation-dataset

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.ImageSegmentationDirectory

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

To view an image segmentation dataset stored in the above format in the
FiftyOne App without creating a persistent FiftyOne dataset, you
can execute:

```shell
DATASET_DIR=/path/to/image-segmentation-dataset

# View the dataset in the App
fiftyone app view \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.ImageSegmentationDirectory
```

You can also independently specify the locations of the masks and the root
directory containing the corresponding media files by providing the
`labels_path` and `data_path` parameters rather than `dataset_dir`:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
data_path = "/path/to/images"
labels_path = "/path/to/masks"

# Import dataset by explicitly providing paths to the source media and masks
dataset = fo.Dataset.from_dir(
    dataset_type=fo.types.ImageSegmentationDirectory,
    data_path=data_path,
    labels_path=labels_path,
    name=name,
)
```

```shell
NAME=my-dataset
DATA_PATH=/path/to/images
LABELS_PATH=/path/to/masks

# Import dataset by explicitly providing paths to the source media and masks
fiftyone datasets create \
    --name $NAME \
    --type fiftyone.types.ImageSegmentationDirectory \
    --kwargs \
        data_path=$DATA_PATH \
        labels_path=$LABELS_PATH
```

<a id="cvatimagedataset-import"></a>

## CVAT Image


<div class="available-in">
    <div class="available-in-row">
        <span class="available-in-label">Available in:</span>
        <span class="available-in-pill available-in-pill--oss">Open Source</span><span class="available-in-pill available-in-pill--enterprise">Enterprise</span>
    </div>
    <div class="available-in-row">
        <span class="available-in-versions">Introduced in <a href="../release-notes.html#fiftyone-0-3-0">FiftyOne 0.3.0</a> &middot; <a href="../release-notes.html#fiftyone-enterprise-1-0">FiftyOne Enterprise 1.0</a></span>
    </div>
    
</div>

The [`fiftyone.types.CVATImageDataset`](../api/fiftyone.types.md#fiftyone.types.CVATImageDataset) type represents a labeled dataset
consisting of images and their associated tags and object detections stored in
[CVAT image format](https://github.com/opencv/cvat).

Datasets of this type are read in the following format:

```text
<dataset_dir>/
    data/
        <uuid1>.<ext>
        <uuid2>.<ext>
        ...
    labels.xml
```

where `labels.xml` is an XML file in the following format:

```xml
<?xml version="1.0" encoding="utf-8"?>
<annotations>
    <version>1.1</version>
    <meta>
        <task>
            <id>0</id>
            <name>task-name</name>
            <size>51</size>
            <mode>annotation</mode>
            <overlap></overlap>
            <bugtracker></bugtracker>
            <flipped>False</flipped>
            <created>2017-11-20 11:51:51.000000+00:00</created>
            <updated>2017-11-20 11:51:51.000000+00:00</updated>
            <labels>
                <label>
                    <name>car</name>
                    <attributes>
                        <attribute>
                            <name>type</name>
                            <values>coupe\\nsedan\\ntruck</values>
                        </attribute>
                        ...
                    </attributes>
                </label>
                <label>
                    <name>traffic_line</name>
                    <attributes>
                        <attribute>
                            <name>color</name>
                            <values>white\\nyellow</values>
                        </attribute>
                        ...
                    </attributes>
                </label>
                ...
            </labels>
        </task>
        <segments>
            <segment>
                <id>0</id>
                <start>0</start>
                <stop>50</stop>
                <url></url>
            </segment>
        </segments>
        <owner>
            <username></username>
            <email></email>
        </owner>
        <dumped>2017-11-20 11:51:51.000000+00:00</dumped>
    </meta>
    <image id="0" name="<uuid1>.<ext>" width="640" height="480">
        <tag label="urban"></tag>
        ...
        <box label="car" xtl="100" ytl="50" xbr="325" ybr="190" occluded="0">
            <attribute name="type">sedan</attribute>
            ...
        </box>
        ...
        <polygon label="car" points="561.30,916.23;561.30,842.77;...;560.20,966.67" occluded="0">
            <attribute name="make">Honda</attribute>
            ...
        </polygon>
        ...
        <polyline label="traffic_line" points="462.10,0.00;126.80,1200.00" occluded="0">
            <attribute name="color">yellow</attribute>
            ...
        </polyline>
        ...
        <points label="wheel" points="574.90,939.48;1170.16,907.90;...;600.16,459.48" occluded="0">
            <attribute name="location">front_driver_side</attribute>
            ...
        </points>
        ...
    </image>
    ...
    <image id="50" name="<uuid51>.<ext>" width="640" height="480">
        ...
    </image>
</annotations>
```

Unlabeled images have no corresponding `image` tag in `labels.xml`.

The `name` field of the `<image>` tags in the labels file encodes the location
of the corresponding images, which can be any of the following:

- The filename of an image in the `data/` folder
- A relative path like `data/sub/folder/filename.ext` specifying the relative
  path to the image in a nested subfolder of `data/`
- An absolute path to an image, which may or may not be in the `data/` folder

#### NOTE
See [`CVATImageDatasetImporter`](../api/fiftyone.utils.cvat.md#fiftyone.utils.cvat.CVATImageDatasetImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from a CVAT image dataset stored in the above
format as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/cvat-image-dataset"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.CVATImageDataset,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/cvat-image-dataset

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.CVATImageDataset

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

To view a CVAT image dataset stored in the above format in the FiftyOne
App without creating a persistent FiftyOne dataset, you can execute:

```shell
DATASET_DIR=/path/to/cvat-image-dataset

# View the dataset in the App
fiftyone app view \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.CVATImageDataset
```

You can also independently specify the locations of the labels and the root
directory containing the corresponding media files by providing the
`labels_path` and `data_path` parameters rather than `dataset_dir`:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
data_path = "/path/to/images"
labels_path = "/path/to/cvat-labels.xml"

# Import dataset by explicitly providing paths to the source media and labels
dataset = fo.Dataset.from_dir(
    dataset_type=fo.types.CVATImageDataset,
    data_path=data_path,
    labels_path=labels_path,
    name=name,
)
```

```shell
NAME=my-dataset
DATA_PATH=/path/to/images
LABELS_PATH=/path/to/cvat-labels.xml

# Import dataset by explicitly providing paths to the source media and labels
fiftyone datasets create \
    --name $NAME \
    --type fiftyone.types.CVATImageDataset \
    --kwargs \
        data_path=$DATA_PATH \
        labels_path=$LABELS_PATH
```

#### NOTE
If the `name` key of your labels contains absolute paths to the source
media, then you can omit the `data_path` parameter from the example above.

<a id="cvatvideodataset-import"></a>

## CVAT Video


<div class="available-in">
    <div class="available-in-row">
        <span class="available-in-label">Available in:</span>
        <span class="available-in-pill available-in-pill--oss">Open Source</span><span class="available-in-pill available-in-pill--enterprise">Enterprise</span>
    </div>
    <div class="available-in-row">
        <span class="available-in-versions">Introduced in <a href="../release-notes.html#fiftyone-0-6-1">FiftyOne 0.6.1</a> &middot; <a href="../release-notes.html#fiftyone-enterprise-1-0">FiftyOne Enterprise 1.0</a></span>
    </div>
    
</div>

The [`fiftyone.types.CVATVideoDataset`](../api/fiftyone.types.md#fiftyone.types.CVATVideoDataset) type represents a labeled dataset
consisting of videos and their associated object detections stored in
[CVAT video format](https://github.com/opencv/cvat).

Datasets of this type are read in the following format:

```text
<dataset_dir>/
    data/
        <uuid1>.<ext>
        <uuid2>.<ext>
        ...
    labels/
        <uuid1>.xml
        <uuid2>.xml
        ...
```

where the labels XML files are stored in the following format:

```xml
<?xml version="1.0" encoding="utf-8"?>
<annotations>
    <version>1.1</version>
    <meta>
        <task>
            <id>task-id</id>
            <name>task-name</name>
            <size>51</size>
            <mode>interpolation</mode>
            <overlap></overlap>
            <bugtracker></bugtracker>
            <flipped>False</flipped>
            <created>2017-11-20 11:51:51.000000+00:00</created>
            <updated>2017-11-20 11:51:51.000000+00:00</updated>
            <labels>
                <label>
                    <name>car</name>
                    <attributes>
                        <attribute>
                            <name>type</name>
                            <values>coupe\\nsedan\\ntruck</values>
                        </attribute>
                        ...
                    </attributes>
                </label>
                <label>
                    <name>traffic_line</name>
                    <attributes>
                        <attribute>
                            <name>color</name>
                            <values>white\\nyellow</values>
                        </attribute>
                        ...
                    </attributes>
                </label>
                ...
            </labels>
        </task>
        <segments>
            <segment>
                <id>0</id>
                <start>0</start>
                <stop>50</stop>
                <url></url>
            </segment>
        </segments>
        <owner>
            <username></username>
            <email></email>
        </owner>
        <original_size>
            <width>640</width>
            <height>480</height>
        </original_size>
        <dumped>2017-11-20 11:51:51.000000+00:00</dumped>
    </meta>
    <track id="0" label="car">
        <box frame="0" xtl="100" ytl="50" xbr="325" ybr="190" outside="0" occluded="0" keyframe="1">
            <attribute name="type">sedan</attribute>
            ...
        </box>
        ...
    </track>
    <track id="1" label="car">
        <polygon frame="0" points="561.30,916.23;561.30,842.77;...;560.20,966.67" outside="0" occluded="0" keyframe="1">
            <attribute name="make">Honda</attribute>
            ...
        </polygon>
        ...
    </track>
    ...
    <track id="10" label="traffic_line">
        <polyline frame="10" points="462.10,0.00;126.80,1200.00" outside="0" occluded="0" keyframe="1">
            <attribute name="color">yellow</attribute>
            ...
        </polyline>
        ...
    </track>
    ...
    <track id="88" label="wheel">
        <points frame="176" points="574.90,939.48;1170.16,907.90;...;600.16,459.48" outside="0" occluded="0" keyframe="1">
            <attribute name="location">front_driver_side</attribute>
            ...
        </points>
        ...
    </track>
</annotations>
```

Unlabeled videos have no corresponding file in `labels/`.

The `data/` and `labels/` files may contain nested subfolders of parallelly
organized images and labels.

#### NOTE
See [`CVATVideoDatasetImporter`](../api/fiftyone.utils.cvat.md#fiftyone.utils.cvat.CVATVideoDatasetImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from a CVAT video dataset stored in the above
format as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/cvat-video-dataset"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.CVATVideoDataset,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/cvat-video-dataset

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.CVATVideoDataset

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

To view a CVAT video dataset stored in the above format in the FiftyOne
App without creating a persistent FiftyOne dataset, you can execute:

```shell
DATASET_DIR=/path/to/cvat-video-dataset

# View the dataset in the App
fiftyone app view \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.CVATVideoDataset
```

You can also independently specify the locations of the labels and the root
directory containing the corresponding media files by providing the
`labels_path` and `data_path` parameters rather than `dataset_dir`:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
data_path = "/path/to/images"
labels_path = "/path/to/cvat-labels"

# Import dataset by explicitly providing paths to the source media and labels
dataset = fo.Dataset.from_dir(
    dataset_type=fo.types.CVATVideoDataset,
    data_path=data_path,
    labels_path=labels_path,
    name=name,
)
```

```shell
NAME=my-dataset
DATA_PATH=/path/to/images
LABELS_PATH=/path/to/cvat-labels

# Import dataset by explicitly providing paths to the source media and labels
fiftyone datasets create \
    --name $NAME \
    --type fiftyone.types.CVATVideoDataset \
    --kwargs \
        data_path=$DATA_PATH \
        labels_path=$LABELS_PATH
```

<a id="openlabelimagedataset-import"></a>

## OpenLABEL Image


<div class="available-in">
    <div class="available-in-row">
        <span class="available-in-label">Available in:</span>
        <span class="available-in-pill available-in-pill--oss">Open Source</span><span class="available-in-pill available-in-pill--enterprise">Enterprise</span>
    </div>
    <div class="available-in-row">
        <span class="available-in-versions">Introduced in <a href="../release-notes.html#fiftyone-0-15-0">FiftyOne 0.15.0</a> &middot; <a href="../release-notes.html#fiftyone-enterprise-1-0">FiftyOne Enterprise 1.0</a></span>
    </div>
    
</div>

The [`fiftyone.types.OpenLABELImageDataset`](../api/fiftyone.types.md#fiftyone.types.OpenLABELImageDataset) type represents a labeled
dataset consisting of images and their associated multitask predictions stored =
in [OpenLABEL format](https://www.asam.net/index.php?eID=dumpFile&t=f&f=3876&token=413e8c85031ae64cc35cf42d0768627514868b2f).

OpenLABEL is a flexible format which allows labels to be stored in a variety of
different ways with respect to the corresponding media files. The following
enumerates the possible structures in which media data and OpenLABEL formatted
label files can be stored in ways that is understood by FiftyOne:

1. One label file per image. Each label contains only the metadata and labels
   associated with the image of the same name. In this case, the `labels_path`
   argument is expected to be a directory, if provided:

```text
<dataset_dir>/
    data/
        <uuid1>.<ext>
        <uuid2>.<ext>
        ...
    labels/
        <uuid1>.json
        <uuid2>.json
        ...
```

1. One label file for all images. The label file contains all of the metadata
   and labels associated with every image. In this case, there needs to be
   additional information provided in the label file to match labels to
   images. Specifically, the image filepath corresponding to a label must be
   stored as a stream:

```text
<dataset_dir>/
    data/
        <uuid1>.<ext>
        <uuid2>.<ext>
        ...
    labels.json
```

1. Multiple label files, each corresponding to one or more images. This case is
   similar to when there is a single label file, except that the label
   information may be spread out over multiple files. Since the filenames
   cannot be used to match labels to images, the image filepaths must again be
   stored as streams in the labels files:

```text
<dataset_dir>/
    data/
        <uuid1>.<ext>
        <uuid2>.<ext>
        ...
    labels/
        <labels-filename1>.json
        <labels-filename2>.json
        ...
```

As for the actual structure of the labels files themselves, labels are stored
in one or more JSON files and can follow a variety of formats. In general
following this format:

#### NOTE
All object information stored in the `frames` key is applied to the
corresponding image.

```text
{
    "openlabel": {
        "metadata": {
            "schema_version": "1.0.0",
            "uri": "/path/to/<uuid>.<ext>",
        },
        "objects": {
            "object_uuid1": {
                "name": "instance1",
                "type": "label1",
                "object_data": {
                    "bbox": [
                        {
                            "name": "shape",
                            "val": [
                                center-x,
                                center-y,
                                width,
                                height
                            ]
                        }
                    ]
                }
            },
            "object_uuid2": {
                "name": "instance1",
                "type": "label2",
                "object_data": {},  # DEFINED IN FRAMES
            }
        },
        "frames": {
            "0": {
               "frame_properties": {
                  "streams": {
                     "Camera1": {
                        "uri": "<uuid>.<ext>"
                     }
                  }
               },
               "objects": {
                  "object_uuid2": {
                     "object_data": {
                        "poly2d": [
                           {
                              "attributes": {
                                 "boolean": [
                                    {
                                       "name": "is_hole",
                                       "val": false
                                    }
                                 ],
                                 "text": [
                                    {  # IF NOT PROVIDED OTHERWISE
                                       "name": "stream",
                                       "val": "Camera1"
                                    }
                                 ]
                              },
                              "closed": true,
                              "mode": "MODE_POLY2D_ABSOLUTE",
                              "name": "polygon_name",
                              "stream": "Camera1",  # IF NOT IN ATTRIBUTES
                              "val": [
                                 point1-x,
                                 point1-y,
                                 point2-x,
                                 point2-y,
                                 ...
                              ]
                           }
                        ]
                     }
                  }
              }
           }
        },
        "streams": {
           "Camera1": {
              "description": "",
              "stream_properties": {
                 "height": 480,
                 "width": 640
              },
              "type": "camera"
           }
        },
        "ontologies": ... # NOT PARSED
        "relations": ... # NOT PARSED
        "resources": ... # NOT PARSED
        "tags": ... # NOT PARSED
    }
}
```

#### NOTE
See [`OpenLABELImageDatasetImporter`](../api/fiftyone.utils.openlabel.md#fiftyone.utils.openlabel.OpenLABELImageDatasetImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

If loading [`Keypoints`](../api/fiftyone.core.labels.md#fiftyone.core.labels.Keypoints) related to a given [`KeypointSkeleton`](../api/fiftyone.core.odm.dataset.md#fiftyone.core.odm.dataset.KeypointSkeleton), then you can
provide a `skeleton` and `skeleton_key` argument to the
[`OpenLABELImageDatasetImporter`](../api/fiftyone.utils.openlabel.md#fiftyone.utils.openlabel.OpenLABELImageDatasetImporter)
allowing you to match points in your annotations file to labels in the
[`KeypointSkeleton`](../api/fiftyone.core.odm.dataset.md#fiftyone.core.odm.dataset.KeypointSkeleton) and load the points and their attributes in the correct
order.

You can create a FiftyOne dataset from a OpenLABEL image dataset stored in the
above format as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/openlabel-image-dataset"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.OpenLABELImageDataset,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/openlabel-image-dataset

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.OpenLABELImageDataset

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

To view a OpenLABEL image dataset stored in the above format in the
FiftyOne App without creating a persistent FiftyOne dataset, you can
execute:

```shell
DATASET_DIR=/path/to/openlabel-image-dataset

# View the dataset in the App
fiftyone app view \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.OpenLABELImageDataset
```

You can also independently specify the locations of the labels and the root
directory containing the corresponding media files by providing the
`labels_path` and `data_path` parameters rather than `dataset_dir`:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
data_path = "/path/to/images"

labels_path = "/path/to/openlabel-labels.json"
# labels_path = "/path/to/openlabel-labels"

# Import dataset by explicitly providing paths to the source media and labels
dataset = fo.Dataset.from_dir(
    dataset_type=fo.types.OpenLABELImageDataset,
    data_path=data_path,
    labels_path=labels_path,
    name=name,
)
```

```shell
NAME=my-dataset
DATA_PATH=/path/to/images

LABELS_PATH=/path/to/openlabel-labels.json
# LABELS_PATH=/path/to/openlabel-labels

# Import dataset by explicitly providing paths to the source media and labels
fiftyone datasets create \
    --name $NAME \
    --type fiftyone.types.OpenLABELImageDataset \
    --kwargs \
        data_path=$DATA_PATH \
        labels_path=$LABELS_PATH
```

#### NOTE
OpenLABEL is a flexible format that allows for many user-specific
decisions about how to represent labels and metadata. If you have
OpenLABEL-compliant data in a format not understood by the current
importers, please make an issue or contribute a pull request!

<a id="openlabelvideodataset-import"></a>

## OpenLABEL Video


<div class="available-in">
    <div class="available-in-row">
        <span class="available-in-label">Available in:</span>
        <span class="available-in-pill available-in-pill--oss">Open Source</span><span class="available-in-pill available-in-pill--enterprise">Enterprise</span>
    </div>
    <div class="available-in-row">
        <span class="available-in-versions">Introduced in <a href="../release-notes.html#fiftyone-0-15-0">FiftyOne 0.15.0</a> &middot; <a href="../release-notes.html#fiftyone-enterprise-1-0">FiftyOne Enterprise 1.0</a></span>
    </div>
    
</div>

The [`fiftyone.types.OpenLABELVideoDataset`](../api/fiftyone.types.md#fiftyone.types.OpenLABELVideoDataset) type represents a labeled
dataset consisting of videos and their associated multitask predictions stored
in [OpenLABEL format](https://www.asam.net/index.php?eID=dumpFile&t=f&f=3876&token=413e8c85031ae64cc35cf42d0768627514868b2f).

OpenLABEL is a flexible format which allows labels to be stored in a variety of
different ways with respect to the corresponding media files. The following
enumerates the possible structures in which media data and OpenLABEL formatted
label files can be stored in ways that is understood by FiftyOne:

1. One label file per video. Each label contains only the metadata and labels
   associated with the video of the same name. In this case, the `labels_path`
   argument is expected to be a directory, if provided:

```text
<dataset_dir>/
    data/
        <uuid1>.<ext>
        <uuid2>.<ext>
        ...
    labels/
        <uuid1>.json
        <uuid2>.json
        ...
```

1. One label file for all videos. The label file contains all of the metadata
   and labels associated with every video. In this case, there needs to be
   additional information provided in the label file to match labels to
   videos. Specifically, the video filepath corresponding to a label must be
   stored as a stream:

```text
<dataset_dir>/
    data/
        <uuid1>.<ext>
        <uuid2>.<ext>
        ...
    labels.json
```

1. Multiple label files, each corresponding to one or more videos. This case is
   similar to when there is a single label file, except that the label
   information may be spread out over multiple files. Since the filenames
   cannot be used to match labels to videos, the video filepaths must again be
   stored as streams in the labels files:

```text
<dataset_dir>/
    data/
        <uuid1>.<ext>
        <uuid2>.<ext>
        ...
    labels/
        <labaels-filename1>.json
        <labaels-filename2>.json
        ...
```

As for the actual structure of the labels files themselves, labels are stored
in one or more JSON files and can follow a variety of formats. In general
following this format:

```text
{
    "openlabel": {
        "metadata": {
            "schema_version": "1.0.0",
            "uri": "/path/to/<uuid>.<ext>",
        },
        "objects": {
            "object_uuid1": {
                "name": "instance1",
                "type": "label1",
                "object_data": {
                    "bbox": [
                        {
                            "name": "shape",
                            "val": [
                                center-x,
                                center-y,
                                width,
                                height
                            ]
                        }
                    ]
                }
                "frame_intervals": [{"frame_start": 0, "frame_end": 10}],
            },
            "object_uuid2": {
                "name": "instance1",
                "type": "label2",
                "object_data": {},  # DEFINED IN FRAMES
            }
        },
        "frames": {
            "0": {
               "frame_properties": {
                  "streams": {
                     "Camera1": {
                        "uri":"<uuid>.<ext>"
                     }
                  }
               },
               "objects": {
                  "object_uuid2": {
                     "object_data": {
                        "poly2d": [
                           {
                              "attributes": {
                                 "boolean": [
                                    {
                                       "name": "is_hole",
                                       "val": false
                                    }
                                 ],
                                 "text": [
                                    {  # IF NOT PROVIDED OTHERWISE
                                       "name": "stream",
                                       "val": "Camera1"
                                    }
                                 ]
                              },
                              "closed": true,
                              "mode": "MODE_POLY2D_ABSOLUTE",
                              "name": "polygon_name",
                              "stream": "Camera1",  # IF NOT IN ATTRIBUTES
                              "val": [
                                 point1-x,
                                 point1-y,
                                 point2-x,
                                 point2-y,
                                 ...
                              ]
                           }
                        ]
                     }
                  }
              },
              ...
           }
        },
        "streams": {
           "Camera1": {
              "description": "",
              "stream_properties": {
                 "height": 480,
                 "width": 640
              },
              "type": "camera"
           }
        },
        "ontologies": ...  # NOT PARSED
        "relations" ...  # NOT PARSED
        "resources" ...  # NOT PARSED
        "tags": ...  # NOT PARSED
    }
}
```

#### NOTE
See [`OpenLABELVideoDatasetImporter`](../api/fiftyone.utils.openlabel.md#fiftyone.utils.openlabel.OpenLABELVideoDatasetImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

If loading [`Keypoints`](../api/fiftyone.core.labels.md#fiftyone.core.labels.Keypoints) related to a given [`KeypointSkeleton`](../api/fiftyone.core.odm.dataset.md#fiftyone.core.odm.dataset.KeypointSkeleton), then you can
provide a `skeleton` and `skeleton_key` argument to the
[`OpenLABELVideoDatasetImporter`](../api/fiftyone.utils.openlabel.md#fiftyone.utils.openlabel.OpenLABELVideoDatasetImporter)
allowing you to match points in your annotations file to labels in the
[`KeypointSkeleton`](../api/fiftyone.core.odm.dataset.md#fiftyone.core.odm.dataset.KeypointSkeleton) and load the points and their attributes in the correct
order.

You can create a FiftyOne dataset from a OpenLABEL video dataset stored in the
above format as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/openlabel-video-dataset"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.OpenLABELVideoDataset,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/openlabel-video-dataset

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.OpenLABELVideoDataset

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

To view a OpenLABEL video dataset stored in the above format in the FiftyOne
App without creating a persistent FiftyOne dataset, you can execute:

```shell
DATASET_DIR=/path/to/openlabel-video-dataset

# View the dataset in the App
fiftyone app view \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.OpenLABELVideoDataset
```

You can also independently specify the locations of the labels and the root
directory containing the corresponding media files by providing the
`labels_path` and `data_path` parameters rather than `dataset_dir`:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
data_path = "/path/to/videos"

labels_path = "/path/to/openlabel-labels.json"
# labels_path = "/path/to/openlabel-labels"

# Import dataset by explicitly providing paths to the source media and labels
dataset = fo.Dataset.from_dir(
    dataset_type=fo.types.OpenLABELVideoDataset,
    data_path=data_path,
    labels_path=labels_path,
    name=name,
)
```

```shell
NAME=my-dataset
DATA_PATH=/path/to/videos

LABELS_PATH=/path/to/openlabel-labels.json
# LABELS_PATH=/path/to/openlabel-labels

# Import dataset by explicitly providing paths to the source media and labels
fiftyone datasets create \
    --name $NAME \
    --type fiftyone.types.OpenLABELVideoDataset \
    --kwargs \
        data_path=$DATA_PATH \
        labels_path=$LABELS_PATH
```

#### NOTE
OpenLABEL is a flexible format that allows for many user-specific
decisions about how to represent labels and metadata. If you have
OpenLABEL-compliant data in a format not understood by the current
importers, please make an issue or contribute a pull request!

<a id="bdddataset-import"></a>

## BDD


<div class="available-in">
    <div class="available-in-row">
        <span class="available-in-label">Available in:</span>
        <span class="available-in-pill available-in-pill--oss">Open Source</span><span class="available-in-pill available-in-pill--enterprise">Enterprise</span>
    </div>
    <div class="available-in-row">
        <span class="available-in-versions">Introduced in <a href="../release-notes.html#fiftyone-0-3-0">FiftyOne 0.3.0</a> &middot; <a href="../release-notes.html#fiftyone-enterprise-1-0">FiftyOne Enterprise 1.0</a></span>
    </div>
    
</div>

The [`fiftyone.types.BDDDataset`](../api/fiftyone.types.md#fiftyone.types.BDDDataset) type represents a labeled dataset
consisting of images and their associated multitask predictions saved in
[Berkeley DeepDrive (BDD) format](http://bdd-data.berkeley.edu).

Datasets of this type are read in the following format:

```text
<dataset_dir>/
    data/
        <filename0>.<ext>
        <filename1>.<ext>
        ...
    labels.json
```

where `labels.json` is a JSON file in the following format:

```text
[
    {
        "name": "<filename0>.<ext>",
        "attributes": {
            "scene": "city street",
            "timeofday": "daytime",
            "weather": "overcast"
        },
        "labels": [
            {
                "id": 0,
                "category": "traffic sign",
                "manualAttributes": true,
                "manualShape": true,
                "attributes": {
                    "occluded": false,
                    "trafficLightColor": "none",
                    "truncated": false
                },
                "box2d": {
                    "x1": 1000.698742,
                    "x2": 1040.626872,
                    "y1": 281.992415,
                    "y2": 326.91156
                },
                "score": 0.95
            },
            ...
            {
                "id": 34,
                "category": "drivable area",
                "manualAttributes": true,
                "manualShape": true,
                "attributes": {
                    "areaType": "direct"
                },
                "poly2d": [
                    {
                        "types": "LLLLCCC",
                        "closed": true,
                        "vertices": [
                            [241.143645, 697.923453],
                            [541.525255, 380.564983],
                            ...
                        ]
                    }
                ],
                "score": 0.87
            },
            ...
            {
                "id": 109356,
                "category": "lane",
                "attributes": {
                    "laneDirection": "parallel",
                    "laneStyle": "dashed",
                    "laneType": "single white"
                },
                "manualShape": true,
                "manualAttributes": true,
                "poly2d": [
                    {
                        "types": "LL",
                        "closed": false,
                        "vertices": [
                            [492.879546, 331.939543],
                            [0, 471.076658],
                            ...
                        ]
                    }
                ],
                "score": 0.98
            },
            ...
        }
    }
    ...
]
```

Unlabeled images have no corresponding entry in `labels.json`.

The `name` attribute of the labels file encodes the location of the
corresponding images, which can be any of the following:

- The filename of an image in the `data/` folder
- A relative path like `data/sub/folder/filename.ext` specifying the relative
  path to the image in a nested subfolder of `data/`
- An absolute path to an image, which may or may not be in the `data/` folder

#### NOTE
See [`BDDDatasetImporter`](../api/fiftyone.utils.bdd.md#fiftyone.utils.bdd.BDDDatasetImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from a BDD dataset stored in the above format
as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/bdd-dataset"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.BDDDataset,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/bdd-dataset

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.BDDDataset

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

To view a BDD dataset stored in the above format in the FiftyOne App
without creating a persistent FiftyOne dataset, you can execute:

```shell
DATASET_DIR=/path/to/bdd-dataset

# View the dataset in the App
fiftyone app view \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.BDDDataset
```

You can also independently specify the locations of the labels and the root
directory containing the corresponding media files by providing the
`labels_path` and `data_path` parameters rather than `dataset_dir`:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
data_path = "/path/to/images"
labels_path = "/path/to/bdd-labels.json"

# Import dataset by explicitly providing paths to the source media and labels
dataset = fo.Dataset.from_dir(
    dataset_type=fo.types.BDDDataset,
    data_path=data_path,
    labels_path=labels_path,
    name=name,
)
```

```shell
NAME=my-dataset
DATA_PATH=/path/to/images
LABELS_PATH=/path/to/bdd-labels.json

# Import dataset by explicitly providing paths to the source media and labels
fiftyone datasets create \
    --name $NAME \
    --type fiftyone.types.BDDDataset \
    --kwargs \
        data_path=$DATA_PATH \
        labels_path=$LABELS_PATH
```

#### NOTE
If the `name` key of your labels contains absolute paths to the source
media, then you can omit the `data_path` parameter from the example above.

<a id="csvdataset-import"></a>

## CSV


<div class="available-in">
    <div class="available-in-row">
        <span class="available-in-label">Available in:</span>
        <span class="available-in-pill available-in-pill--oss">Open Source</span><span class="available-in-pill available-in-pill--enterprise">Enterprise</span>
    </div>
    <div class="available-in-row">
        <span class="available-in-versions">Introduced in <a href="../release-notes.html#fiftyone-0-19-0">FiftyOne 0.19.0</a> &middot; <a href="../release-notes.html#fiftyone-enterprise-1-1">FiftyOne Enterprise 1.1</a></span>
    </div>
    
</div>

The [`fiftyone.types.CSVDataset`](../api/fiftyone.types.md#fiftyone.types.CSVDataset) type represents a dataset consisting
of images or videos and their associated field values stored as columns of a
CSV file.

Datasets of this type are read in the following format:

```text
<dataset_dir>/
    data/
        <filename1>.<ext>
        <filename2>.<ext>
        ...
    labels.csv
```

where `labels.csv` is a CSV file in the following format:

```text
field1,field2,field3,...
value1,value2,value3,...
value1,value2,value3,...
...
```

One sample will be generated per row in the CSV file (excluding the header
row).

One column of the CSV file must contain media paths, which may be either:

- filenames or relative paths to media files in `data/`
- absolute paths to media files

By default it is assumed that a `filepath` column exists and contains the
media paths, but you can customize this via the optional `media_field`
parameter.

By default all columns are loaded as string fields, but you can provide the
optional `fields` parameter to select a subset of columns to load or provide
custom parsing functions for each field, as demonstrated below.

#### NOTE
See [`CSVDatasetImporter`](../api/fiftyone.utils.csv.md#fiftyone.utils.csv.CSVDatasetImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from a CSV dataset stored in the above
format as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/csv-dataset"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.CSVDataset,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/csv-dataset

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.CSVDataset

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

To view a CSV dataset stored in the above format in the FiftyOne App
without creating a persistent FiftyOne dataset, you can execute:

```shell
DATASET_DIR=/path/to/csv-dataset

# View the dataset in the App
fiftyone app view \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.CSVDataset
```

If your CSV file contains absolute media paths, then you can directly specify
the path to the CSV file itself by providing the `labels_path` parameter.

Additionally, you can use the `fields` parameter to customize how each field is
parsed, as demonstrated below:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
labels_path = "/path/to/labels.csv"

fields = {
    "filepath": None,  # load as strings
    "tags": lambda v: v.strip("").split(","),
    "float_field": lambda v: float(v),
    "weather": lambda v: fo.Classification(label=v) if v else None,
}

# Import CSV file with absolute media paths and custom field parsers
dataset = fo.Dataset.from_dir(
    dataset_type=fo.types.CSVDataset,
    labels_path=labels_path,
    fields=fields,
    name=name,
)
```

```shell
NAME=my-dataset
LABELS_PATH=/path/to/labels.csv

# Import CSV file with absolute media paths
fiftyone datasets create \
    --name $NAME \
    --type fiftyone.types.CSVDataset \
    --kwargs labels_path=$LABELS_PATH
```

<a id="dicomdataset-import"></a>

## DICOM


<div class="available-in">
    <div class="available-in-row">
        <span class="available-in-label">Available in:</span>
        <span class="available-in-pill available-in-pill--oss">Open Source</span><span class="available-in-pill available-in-pill--enterprise">Enterprise</span>
    </div>
    <div class="available-in-row">
        <span class="available-in-versions">Introduced in <a href="../release-notes.html#fiftyone-0-12-0">FiftyOne 0.12.0</a> &middot; <a href="../release-notes.html#fiftyone-enterprise-1-0">FiftyOne Enterprise 1.0</a></span>
    </div>
    
</div>

The [`fiftyone.types.DICOMDataset`](../api/fiftyone.types.md#fiftyone.types.DICOMDataset) type represents a dataset consisting
of images and their associated properties stored in
[DICOM format](https://en.wikipedia.org/wiki/DICOM).

#### NOTE
You must have [pydicom<3](https://github.com/pydicom/pydicom) installed
in order to load DICOM datasets.

The standard format for datasets of this type is the following:

```text
<dataset_dir>/
    <filename1>.dcm
    <filename2>.dcm
```

where each `.dcm` file is a DICOM file that can be read via
[`pydicom.dcmread`](https://pydicom.github.io/pydicom/stable/reference/generated/pydicom.filereader.dcmread.html#pydicom.filereader.dcmread).

Alternatively, rather than providing a `dataset_dir`, you can provide the
`dicom_path` argument, which can directly specify a glob pattern of DICOM
files or the path to a
[DICOMDIR](https://pydicom.github.io/pydicom/stable/tutorials/filesets.html)
file.

By default, all attributes in the DICOM files discoverable via
[`pydicom.dataset.Dataset.dir()`](https://pydicom.github.io/pydicom/stable/reference/generated/pydicom.dataset.Dataset.html#pydicom.dataset.Dataset.dir) with supported types are loaded
into sample-level fields, but you can select only specific attributes by
passing the optional `keywords` argument.

#### NOTE
When importing DICOM datasets, the pixel data are converted to 8-bit
images, using the `SmallestImagePixelValue` and
`LargestImagePixelValue` attributes (if present), to inform the
conversion.

The images are written to a backing directory that you can configure by
passing the `images_dir` argument. By default, the images are written to
`dataset_dir`.

Currently, only single frame images are supported, but a community
contribution to support 3D or 4D image types (e.g., CT scans) is welcomed!

#### NOTE
See `DICOMDatasetImporter`
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from a DICOM dataset stored in standard
format as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/dicom-dataset"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.DICOMDataset,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/dicom-dataset

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.DICOMDataset

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

You can create a FiftyOne dataset from a glob pattern of DICOM files or the
path to a DICOMDIR file as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"

dicom_path = "/path/to/*.dcm"  # glob pattern of DICOM files
# dicom_path = "/path/to/DICOMDIR"  # DICOMDIR file

# Create the dataset
dataset = fo.Dataset.from_dir(
    dicom_path=dicom_path,
    dataset_type=fo.types.DICOMDataset,
    keywords=["PatientName", "StudyID"],  # load specific attributes
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset

DICOM_PATH='/path/to/*.dcm'  # glob pattern of DICOM files
# DICOM_PATH='/path/to/DICOMDIR'  # DICOMDIR file

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --type fiftyone.types.DICOMDataset \
    --kwargs \
        dicom_path=$DICOM_PATH \
        keywords=PatientName,StudyID  # load specific attributes

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

<a id="geojsondataset-import"></a>

## GeoJSON

The [`fiftyone.types.GeoJSONDataset`](../api/fiftyone.types.md#fiftyone.types.GeoJSONDataset) type represents a dataset consisting
of images or videos and their associated geolocation data and optional
properties stored in [GeoJSON format](https://en.wikipedia.org/wiki/GeoJSON).

Datasets of this type are read in the following format:

```text
<dataset_dir>/
    data/
        <filename1>.<ext>
        <filename2>.<ext>
        ...
    labels.json
```

where `labels.json` is a GeoJSON file containing a `FeatureCollection` in the
following format:

```text
{
    "type": "FeatureCollection",
    "features": [
        {
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [
                    -73.99496451958454,
                    40.66338032487842
                ]
            },
            "properties": {
                "filename": <filename1>.<ext>,
                ...
            }
        },
        {
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [
                    -73.80992143421788,
                    40.65611832778962
                ]
            },
            "properties": {
                "filename": <filename2>.<ext>,
                ...
            }
        },
        ...
    ]
}
```

where the `geometry` field may contain any valid GeoJSON geometry object, and
the `filename` property encodes the name of the corresponding media in the
`data/` folder. The `filename` property can also be an absolute path, which
may or may not be in the `data/` folder.

Samples with no location data will have a null `geometry` field.

The `properties` field of each feature can contain additional labels that
can be imported.

#### NOTE
See [`GeoJSONDatasetImporter`](../api/fiftyone.utils.geojson.md#fiftyone.utils.geojson.GeoJSONDatasetImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from a GeoJSON dataset stored in the above
format as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/geojson-dataset"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.GeoJSONDataset,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/geojson-dataset

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.GeoJSONDataset

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

To view a GeoJSON dataset stored in the above format in the FiftyOne App
without creating a persistent FiftyOne dataset, you can execute:

```shell
DATASET_DIR=/path/to/geojson-dataset

# View the dataset in the App
fiftyone app view \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.GeoJSONDataset
```

You can also independently specify the locations of the labels and the root
directory containing the corresponding media files by providing the
`labels_path` and `data_path` parameters rather than `dataset_dir`:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
data_path = "/path/to/images"
labels_path = "/path/to/geo-labels.json"

# Import dataset by explicitly providing paths to the source media and labels
dataset = fo.Dataset.from_dir(
    dataset_type=fo.types.GeoJSONDataset,
    data_path=data_path,
    labels_path=labels_path,
    name=name,
)
```

```shell
NAME=my-dataset
DATA_PATH=/path/to/images
LABELS_PATH=/path/to/geo-labels.json

# Import dataset by explicitly providing paths to the source media and labels
fiftyone datasets create \
    --name $NAME \
    --type fiftyone.types.GeoJSONDataset \
    --kwargs \
        data_path=$DATA_PATH \
        labels_path=$LABELS_PATH
```

#### NOTE
If the `filename` key of your labels contains absolute paths to the source
media, then you can omit the `data_path` parameter from the example above.

<a id="geotiffdataset-import"></a>

## GeoTIFF


<div class="available-in">
    <div class="available-in-row">
        <span class="available-in-label">Available in:</span>
        <span class="available-in-pill available-in-pill--oss">Open Source</span><span class="available-in-pill available-in-pill--enterprise">Enterprise</span>
    </div>
    <div class="available-in-row">
        <span class="available-in-versions">Introduced in <a href="../release-notes.html#fiftyone-0-13-3">FiftyOne 0.13.3</a> &middot; <a href="../release-notes.html#fiftyone-enterprise-1-0">FiftyOne Enterprise 1.0</a></span>
    </div>
    
</div>

The [`fiftyone.types.GeoTIFFDataset`](../api/fiftyone.types.md#fiftyone.types.GeoTIFFDataset) type represents a dataset consisting
of images and their associated geolocation data stored in
[GeoTIFF format](https://en.wikipedia.org/wiki/GeoTIFF).

#### NOTE
You must have [rasterio](https://github.com/mapbox/rasterio) installed in
order to load GeoTIFF datasets.

The standard format for datasets of this type is the following:

```text
<dataset_dir>/
    <filename1>.tif
    <filename2>.tif
```

where each `.tif` file is a GeoTIFF image that can be read via
[`rasterio.open`](https://rasterio.readthedocs.io/en/latest/api/rasterio.html#rasterio.open).

Alternatively, rather than providing a `dataset_dir`, you can provide the
`image_path` argument, which can directly specify a list or glob pattern of
GeoTIFF images to load.

The dataset will contain a [`GeoLocation`](../api/fiftyone.core.labels.md#fiftyone.core.labels.GeoLocation) field whose
[`point`](../api/fiftyone.core.labels.md#fiftyone.core.labels.GeoLocation.point) attribute contains the
`(longitude, latitude)` coordinates of each image center and whose
[`polygon`](../api/fiftyone.core.labels.md#fiftyone.core.labels.GeoLocation.polygon) attribute contains
the `(longitude, latitude)` coordinates of the corners of the image (clockwise,
starting from the top-left corner).

#### NOTE
See [`GeoTIFFDatasetImporter`](../api/fiftyone.utils.geotiff.md#fiftyone.utils.geotiff.GeoTIFFDatasetImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from a GeoTIFF dataset stored in standard
format as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/geotiff-dataset"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.GeoTIFFDataset,
    label_field="location",
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/geotiff-dataset

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.GeoTIFFDataset \
    --kwargs label_field=location

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

You can create a FiftyOne dataset from a list or glob pattern of GeoTIFF images
as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
image_path = "/path/to/*.tif"  # glob pattern of GeoTIFF images
# image_path = ["/path/to/image1.tif", ...]  # list of GeoTIFF images

# Create the dataset
dataset = fo.Dataset.from_dir(
    image_path=image_path,
    dataset_type=fo.types.GeoTIFFDataset,
    label_field="location",
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
IMAGE_PATH='/path/to/*.tif'  # glob pattern of GeoTIFF images
# IMAGE_PATH='/path/to/image1.tif,...'  # list of GeoTIFF images

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --type fiftyone.types.GeoTIFFDataset \
    --kwargs \
        image_path=$IMAGE_PATH \
        label_field=location

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

<a id="fiftyonedataset-import"></a>

## FiftyOne Dataset


<div class="available-in">
    <div class="available-in-row">
        <span class="available-in-label">Available in:</span>
        <span class="available-in-pill available-in-pill--oss">Open Source</span><span class="available-in-pill available-in-pill--enterprise">Enterprise</span>
    </div>
    <div class="available-in-row">
        <span class="available-in-versions">Introduced in <a href="../release-notes.html#fiftyone-0-5-0">FiftyOne 0.5.0</a> &middot; <a href="../release-notes.html#fiftyone-enterprise-1-0">FiftyOne Enterprise 1.0</a></span>
    </div>
    
</div>

The [`fiftyone.types.FiftyOneDataset`](../api/fiftyone.types.md#fiftyone.types.FiftyOneDataset) provides a disk representation of
an entire [`Dataset`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset) in a serialized JSON format along with its source media.

Datasets of this type are read in the following format:

```text
<dataset_dir>/
    metadata.json
    samples.json
    data/
        <filename1>.<ext>
        <filename2>.<ext>
        ...
    annotations/
        <anno_key1>.json
        <anno_key2>.json
        ...
    brain/
        <brain_key1>.json
        <brain_key2>.json
        ...
    evaluations/
        <eval_key1>.json
        <eval_key2>.json
        ...
```

where `metadata.json` is a JSON file containing metadata associated with the
dataset, `samples.json` is a JSON file containing a serialized representation
of the samples in the dataset, `annotations/` contains any serialized
[`AnnotationResults`](../api/fiftyone.core.annotation.md#fiftyone.core.annotation.AnnotationResults), `brain/` contains any serialized [`BrainResults`](../api/fiftyone.core.brain.md#fiftyone.core.brain.BrainResults), and
`evaluations/` contains any serialized [`EvaluationResults`](../api/fiftyone.core.evaluation.md#fiftyone.core.evaluation.EvaluationResults).

The contents of the `data/` directory may also be organized in nested
subfolders, depending on how the dataset was exported, in which case the
filepaths in `samples.json` should contain correspondingly nested paths.

Video datasets have an additional `frames.json` file that contains a serialized
representation of the frame labels for each video in the dataset.

#### NOTE
See [`FiftyOneDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.FiftyOneDatasetImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from a directory in the above format as
follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/fiftyone-dataset"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.FiftyOneDataset,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/fiftyone-dataset

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.FiftyOneDataset

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

To view a dataset stored on disk in the FiftyOne App without creating a
persistent FiftyOne dataset, you can execute:

```shell
DATASET_DIR=/path/to/fiftyone-dataset

# View the dataset in the App
fiftyone app view \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.FiftyOneDataset
```

If you performed a [FiftyOneDataset export](export_datasets.md#fiftyonedataset-export)
using the `rel_dir` parameter to strip a common prefix from the media filepaths
in the dataset, then simply include the `rel_dir` parameter when importing back
into FiftyOne to prepend the appropriate prefix to each media path:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/fiftyone-dataset"

# Import dataset, prepending `rel_dir` to each media path
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.FiftyOneDataset,
    rel_dir="/common/images/dir",
    name=name,
)
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/fiftyone-dataset

# Import dataset, prepending `rel_dir` to each media path
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.FiftyOneDataset \
    --kwargs rel_dir=/common/images/dir
```

#### NOTE
Exporting in [FiftyOneDataset format](export_datasets.md#fiftyonedataset-export) using
the `export_media=False` and `rel_dir` parameters is a convenient way to
transfer datasets between work environments, since this enables you to
store the media files wherever you wish in each environment and then simply
provide the appropriate `rel_dir` value as shown above when importing the
dataset into FiftyOne in a new environment.

<a id="fiftyoneimagelabelsdataset-import"></a>

## FiftyOne Image Labels


<div class="available-in">
    <div class="available-in-row">
        <span class="available-in-label">Available in:</span>
        <span class="available-in-pill available-in-pill--oss">Open Source</span><span class="available-in-pill available-in-pill--enterprise">Enterprise</span>
    </div>
    <div class="available-in-row">
        <span class="available-in-versions">Introduced in <a href="../release-notes.html#fiftyone-0-5-2">FiftyOne 0.5.2</a> &middot; <a href="../release-notes.html#fiftyone-enterprise-1-0">FiftyOne Enterprise 1.0</a></span>
    </div>
    
</div>

The [`fiftyone.types.FiftyOneImageLabelsDataset`](../api/fiftyone.types.md#fiftyone.types.FiftyOneImageLabelsDataset) type represents a
labeled dataset consisting of images and their associated multitask predictions
stored in
[ETA ImageLabels format](https://github.com/voxel51/eta/blob/main/docs/image_labels_guide.md).

Datasets of this type are read in the following format:

```text
<dataset_dir>/
    data/
        <uuid1>.<ext>
        <uuid2>.<ext>
        ...
    labels/
        <uuid1>.json
        <uuid2>.json
        ...
    manifest.json
```

where `manifest.json` is a JSON file in the following format:

```text
{
    "type": "eta.core.datasets.LabeledImageDataset",
    "description": "",
    "index": [
        {
            "data": "data/<uuid1>.<ext>",
            "labels": "labels/<uuid1>.json"
        },
        {
            "data": "data/<uuid2>.<ext>",
            "labels": "labels/<uuid2>.json"
        },
        ...
    ]
}
```

and where each labels JSON file is stored in
[ETA ImageLabels format](https://github.com/voxel51/eta/blob/main/docs/image_labels_guide.md).

For unlabeled images, an empty `eta.core.image.ImageLabels` file is stored.

#### NOTE
See [`FiftyOneImageLabelsDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.FiftyOneImageLabelsDatasetImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from an image labels dataset stored in the
above format as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/image-labels-dataset"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.FiftyOneImageLabelsDataset,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/image-labels-dataset

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.FiftyOneImageLabelsDataset

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

To view an image labels dataset stored in the above format in the FiftyOne
App without creating a persistent FiftyOne dataset, you can execute:

```shell
DATASET_DIR=/path/to/image-labels-dataset

# View the dataset in the App
fiftyone app view \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.FiftyOneImageLabelsDataset
```

<a id="fiftyonevideolabelsdataset-import"></a>

## FiftyOne Video Labels


<div class="available-in">
    <div class="available-in-row">
        <span class="available-in-label">Available in:</span>
        <span class="available-in-pill available-in-pill--oss">Open Source</span><span class="available-in-pill available-in-pill--enterprise">Enterprise</span>
    </div>
    <div class="available-in-row">
        <span class="available-in-versions">Introduced in <a href="../release-notes.html#fiftyone-0-6-0">FiftyOne 0.6.0</a> &middot; <a href="../release-notes.html#fiftyone-enterprise-1-0">FiftyOne Enterprise 1.0</a></span>
    </div>
    
</div>

The [`fiftyone.types.FiftyOneVideoLabelsDataset`](../api/fiftyone.types.md#fiftyone.types.FiftyOneVideoLabelsDataset) type represents a
labeled dataset consisting of videos and their associated labels stored in
[ETA VideoLabels format](https://github.com/voxel51/eta/blob/main/docs/video_labels_guide.md).

Datasets of this type are read in the following format:

```text
<dataset_dir>/
    data/
        <uuid1>.<ext>
        <uuid2>.<ext>
        ...
    labels/
        <uuid1>.json
        <uuid2>.json
        ...
    manifest.json
```

where `manifest.json` is a JSON file in the following format:

```text
{
    "type": "eta.core.datasets.LabeledVideoDataset",
    "description": "",
    "index": [
        {
            "data": "data/<uuid1>.<ext>",
            "labels": "labels/<uuid1>.json"
        },
        {
            "data": "data/<uuid2>.<ext>",
            "labels": "labels/<uuid2>.json"
        },
        ...
    ]
}
```

and where each labels JSON file is stored in
[ETA VideoLabels format](https://github.com/voxel51/eta/blob/main/docs/video_labels_guide.md).

For unlabeled videos, an empty `eta.core.video.VideoLabels` file is written.

#### NOTE
See [`FiftyOneVideoLabelsDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.FiftyOneVideoLabelsDatasetImporter)
for parameters that can be passed to methods like
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) to
customize the import of datasets of this type.

You can create a FiftyOne dataset from a video labels dataset stored in the
above format as follows:

Python

CLI

```python
import fiftyone as fo

name = "my-dataset"
dataset_dir = "/path/to/video-labels-dataset"

# Create the dataset
dataset = fo.Dataset.from_dir(
    dataset_dir=dataset_dir,
    dataset_type=fo.types.FiftyOneVideoLabelsDataset,
    name=name,
)

# View summary info about the dataset
print(dataset)

# Print the first few samples in the dataset
print(dataset.head())
```

```shell
NAME=my-dataset
DATASET_DIR=/path/to/video-labels-dataset

# Create the dataset
fiftyone datasets create \
    --name $NAME \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.FiftyOneVideoLabelsDataset

# View summary info about the dataset
fiftyone datasets info $NAME

# Print the first few samples in the dataset
fiftyone datasets head $NAME
```

To view a video labels dataset stored in the above format in the FiftyOne
App without creating a persistent FiftyOne dataset, you can execute:

```shell
DATASET_DIR=/path/to/video-labels-dataset

# View the dataset in the App
fiftyone app view \
    --dataset-dir $DATASET_DIR \
    --type fiftyone.types.FiftyOneVideoLabelsDataset
```

<a id="custom-dataset-importer"></a>

## Custom formats

If your data does not follow one of the previous formats, then the simplest and
most flexible approach to loading your data into FiftyOne is
[to iterate over your data in a loop](#loading-custom-datasets)
and add it to a [`Dataset`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset).

Alternatively, the [`Dataset`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset) class provides a
[`Dataset.from_importer()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_importer)
factory method that can be used to import a dataset using any [`DatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.DatasetImporter)
instance.

This means that you can define your own [`DatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.DatasetImporter) class and then import
a dataset from disk in your custom format using the following recipe:

```python
import fiftyone as fo

# Create an instance of your custom dataset importer
importer = CustomDatasetImporter(...)

# Import the dataset
dataset = fo.Dataset.from_importer(importer)
```

You can also define a custom [`Dataset`](../api/fiftyone.types.md#fiftyone.types.Dataset) type, which enables you to import
datasets in your custom format using the
[`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) factory
method:

```python
import fiftyone as fo

# The `fiftyone.types.Dataset` subclass for your custom dataset
dataset_type = CustomDataset

# Import the dataset
dataset = fo.Dataset.from_dir(dataset_type=dataset_type, ...)
```

<a id="writing-a-custom-dataset-importer"></a>

### Writing a custom DatasetImporter


<div class="available-in">
    <div class="available-in-row">
        <span class="available-in-label">Available in:</span>
        <span class="available-in-pill available-in-pill--oss">Open Source</span><span class="available-in-pill available-in-pill--enterprise">Enterprise</span>
    </div>
    <div class="available-in-row">
        <span class="available-in-versions">Introduced in <a href="../release-notes.html#fiftyone-0-6-0">FiftyOne 0.6.0</a> &middot; <a href="../release-notes.html#fiftyone-enterprise-1-0">FiftyOne Enterprise 1.0</a></span>
    </div>
    
</div>

[`DatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.DatasetImporter) is an abstract interface; the concrete interface that you
should implement is determined by the type of dataset that you are importing.

Generic datasets

Batch imports

Unlabeled image datasets

Labeled image datasets

Unlabeled video datasets

Labeled video datasets

Grouped datasets

The [`GenericSampleDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.GenericSampleDatasetImporter) interface allows you to define
importers that emit a sequence of arbitrary [`Sample`](../api/fiftyone.core.sample.md#fiftyone.core.sample.Sample) objects.

The pseudocode below provides a template for a custom
[`GenericSampleDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.GenericSampleDatasetImporter):

```python
import fiftyone.utils.data as foud

class CustomGenericSampleDatasetImporter(foud.GenericSampleDatasetImporter):
    """Custom importer for generic sample datasets.

    Args:
        dataset_dir (None): the dataset directory. This may be optional for
            some importers
        shuffle (False): whether to randomly shuffle the order in which the
            samples are imported
        seed (None): a random seed to use when shuffling
        max_samples (None): a maximum number of samples to import. By default,
            all samples are imported
        **kwargs: additional keyword arguments for your importer
    """

    def __init__(
        self,
        dataset_dir=None,
        shuffle=False,
        seed=None,
        max_samples=None,
        **kwargs,
    ):
        super().__init__(
            dataset_dir=dataset_dir,
            shuffle=shuffle,
            seed=seed,
            max_samples=max_samples
        )
        # Your initialization here

    def __len__(self):
        """The total number of samples that will be imported.

        Raises:
            TypeError: if the total number is not known
        """
        # Return the total number of samples in the dataset (if known)
        pass

    def __next__(self):
        """Returns information about the next sample in the dataset.

        Returns:
            a :class:`fiftyone.core.sample.Sample` instance

        Raises:
            StopIteration: if there are no more samples to import
        """
        # Implement loading the next sample in your dataset here
        pass

    @property
    def has_dataset_info(self):
        """Whether this importer produces a dataset info dictionary."""
        # Return True or False here
        pass

    @property
    def has_sample_field_schema(self):
        """Whether this importer produces a sample field schema."""
        # Return True or False here
        pass

    def setup(self):
        """Performs any necessary setup before importing the first sample in
        the dataset.

        This method is called when the importer's context manager interface is
        entered, :func:`DatasetImporter.__enter__`.
        """
        # Your custom setup here
        pass

    def get_dataset_info(self):
        """Returns the dataset info for the dataset.

        By convention, this method should be called after all samples in the
        dataset have been imported.

        Returns:
            a dict of dataset info
        """
        # Return a dict of dataset info, if supported by your importer
        pass

    def get_sample_field_schema(self):
        """Returns a dictionary describing the field schema of the samples
        loaded by this importer.

        Returns:
            a dict mapping field names to :class:`fiftyone.core.fields.Field`
            instances or ``str(field)`` representations of them
        """
        # Return the sample schema here, if known
        pass

    def close(self, *args):
        """Performs any necessary actions after the last sample has been
        imported.

        This method is called when the importer's context manager interface is
        exited, :func:`DatasetImporter.__exit__`.

        Args:
            *args: the arguments to :func:`DatasetImporter.__exit__`
        """
        # Your custom code here to complete the import
        pass
```

When [`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) is
called with a custom [`GenericSampleDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.GenericSampleDatasetImporter), the import is effectively
performed via the pseudocode below:

```python
import fiftyone as fo

dataset = fo.Dataset(...)
importer = CustomGenericSampleDatasetImporter(...)

with importer:
    for sample in importer:
        dataset.add_sample(sample)

    if importer.has_dataset_info:
        info = importer.get_dataset_info()
        parse_info(dataset, info)
```

Note that the importer is invoked via its context manager interface, which
automatically calls the
[`setup()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.GenericSampleDatasetImporter.setup)
and
[`close()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.GenericSampleDatasetImporter.close)
methods of the importer to handle setup/completion of the import.

The samples in the dataset are iteratively loaded by invoking the
[`__next__()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.GenericSampleDatasetImporter.__next__)
method of the importer.

The
[`has_dataset_info`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.GenericSampleDatasetImporter.has_dataset_info)
property of the importer allows it to declare whether its
[`get_dataset_info()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.GenericSampleDatasetImporter.get_dataset_info)
method should be called after all samples have been imported to retrieve
dataset-level information to store on the FiftyOne dataset. See
[this section](#importing-dataset-level-info) for more information.

The [`BatchDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.BatchDatasetImporter) interface allows you to define importers
that load all of their [`Sample`](../api/fiftyone.core.sample.md#fiftyone.core.sample.Sample) objects onto a dataset via a single
custom method. This interface allows for greater efficiency for import
formats that handle aggregating over the samples themselves.

The pseudocode below provides a template for a custom
[`BatchDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.BatchDatasetImporter):

```python
import fiftyone.utils.data as foud

class CustomBatchDatasetImporter(foud.BatchDatasetImporter):
    """Custom batch importer for datasets.

    Args:
        dataset_dir (None): the dataset directory. This may be optional for
            some importers
        shuffle (False): whether to randomly shuffle the order in which the
            samples are imported
        seed (None): a random seed to use when shuffling
        max_samples (None): a maximum number of samples to import. By default,
            all samples are imported
        **kwargs: additional keyword arguments for your importer
    """

    def __init__(
        self,
        dataset_dir=None,
        shuffle=False,
        seed=None,
        max_samples=None,
        **kwargs,
    ):
        super().__init__(
            dataset_dir=dataset_dir,
            shuffle=shuffle,
            seed=seed,
            max_samples=max_samples
        )
        # Your initialization here

    def __len__(self):
        """The total number of samples that will be imported.

        Raises:
            TypeError: if the total number is not known
        """
        # Return the total number of samples in the dataset (if known)
        pass

    def import_samples(self, dataset, tags=None, progress=None):
        """Imports the samples into the given dataset.

        Args:
            dataset: a :class:`fiftyone.core.dataset.Dataset`
            tags (None): an optional list of tags to attach to each sample
            progress (None): whether to render a progress bar (True/False), use
                the default value ``fiftyone.config.show_progress_bars``
                (None), or a progress callback function to invoke instead

        Returns:
            a list of IDs of the samples that were added to the dataset
        """
        # Implement adding the samples to the dataset here
        pass

    @property
    def has_dataset_info(self):
        """Whether this importer produces a dataset info dictionary."""
        # Return True or False here
        pass

    @property
    def has_sample_field_schema(self):
        """Whether this importer produces a sample field schema."""
        # Return True or False here
        pass

    def setup(self):
        """Performs any necessary setup before importing the first sample in
        the dataset.

        This method is called when the importer's context manager interface is
        entered, :func:`DatasetImporter.__enter__`.
        """
        # Your custom setup here
        pass

    def get_dataset_info(self):
        """Returns the dataset info for the dataset.

        By convention, this method should be called after all samples in the
        dataset have been imported.

        Returns:
            a dict of dataset info
        """
        # Return a dict of dataset info, if supported by your importer
        pass

    def get_sample_field_schema(self):
        """Returns a dictionary describing the field schema of the samples
        loaded by this importer.

        Returns:
            a dict mapping field names to :class:`fiftyone.core.fields.Field`
            instances or ``str(field)`` representations of them
        """
        # Return the sample schema here, if known
        pass

    def close(self, *args):
        """Performs any necessary actions after the last sample has been
        imported.

        This method is called when the importer's context manager interface is
        exited, :func:`DatasetImporter.__exit__`.

        Args:
            *args: the arguments to :func:`DatasetImporter.__exit__`
        """
        # Your custom code here to complete the import
        pass
```

When [`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) is
called with a custom [`BatchDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.BatchDatasetImporter), the import is effectively
performed via the pseudocode below:

```python
import fiftyone as fo

dataset = fo.Dataset(...)
importer = CustomBatchDatasetImporter(...)

with importer:
    impoter.import_samples(dataset, ...)

    if importer.has_dataset_info:
        info = importer.get_dataset_info()
        parse_info(dataset, info)
```

Note that the importer is invoked via its context manager interface, which
automatically calls the
[`setup()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.BatchDatasetImporter.setup)
and
[`close()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.BatchDatasetImporter.close)
methods of the importer to handle setup/completion of the import.

The samples are then imported via a single call to the
[`import_samples()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.BatchDatasetImporter.import_samples)
method of the importer.

The
[`has_dataset_info`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.BatchDatasetImporter.has_dataset_info)
property of the importer allows it to declare whether its
[`get_dataset_info()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.BatchDatasetImporter.get_dataset_info)
method should be called after all samples have been imported to retrieve
dataset-level information to store on the FiftyOne dataset. See
[this section](#importing-dataset-level-info) for more information.

To define a custom importer for unlabeled image datasets, implement the
[`UnlabeledImageDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.UnlabeledImageDatasetImporter) interface.

The pseudocode below provides a template for a custom
[`UnlabeledImageDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.UnlabeledImageDatasetImporter):

```python
import fiftyone.utils.data as foud

class CustomUnlabeledImageDatasetImporter(foud.UnlabeledImageDatasetImporter):
    """Custom importer for unlabeled image datasets.

    Args:
        dataset_dir (None): the dataset directory. This may be optional for
            some importers
        shuffle (False): whether to randomly shuffle the order in which the
            samples are imported
        seed (None): a random seed to use when shuffling
        max_samples (None): a maximum number of samples to import. By default,
            all samples are imported
        **kwargs: additional keyword arguments for your importer
    """

    def __init__(
        self,
        dataset_dir=None,
        shuffle=False,
        seed=None,
        max_samples=None,
        **kwargs,
    ):
        super().__init__(
            dataset_dir=dataset_dir,
            shuffle=shuffle,
            seed=seed,
            max_samples=max_samples
        )
        # Your initialization here

    def __len__(self):
        """The total number of samples that will be imported.

        Raises:
            TypeError: if the total number is not known
        """
        # Return the total number of samples in the dataset (if known)
        pass

    def __next__(self):
        """Returns information about the next sample in the dataset.

        Returns:
            an ``(image_path, image_metadata)`` tuple, where:
            -   ``image_path`` is the path to the image on disk
            -   ``image_metadata`` is an
                :class:`fiftyone.core.metadata.ImageMetadata` instances for the
                image, or ``None`` if :meth:`has_image_metadata` is ``False``

        Raises:
            StopIteration: if there are no more samples to import
        """
        # Implement loading the next sample in your dataset here
        pass

    @property
    def has_dataset_info(self):
        """Whether this importer produces a dataset info dictionary."""
        # Return True or False here
        pass

    @property
    def has_image_metadata(self):
        """Whether this importer produces
        :class:`fiftyone.core.metadata.ImageMetadata` instances for each image.
        """
        # Return True or False here
        pass

    def setup(self):
        """Performs any necessary setup before importing the first sample in
        the dataset.

        This method is called when the importer's context manager interface is
        entered, :func:`DatasetImporter.__enter__`.
        """
        # Your custom setup here
        pass

    def get_dataset_info(self):
        """Returns the dataset info for the dataset.

        By convention, this method should be called after all samples in the
        dataset have been imported.

        Returns:
            a dict of dataset info
        """
        # Return a dict of dataset info, if supported by your importer
        pass

    def close(self, *args):
        """Performs any necessary actions after the last sample has been
        imported.

        This method is called when the importer's context manager interface is
        exited, :func:`DatasetImporter.__exit__`.

        Args:
            *args: the arguments to :func:`DatasetImporter.__exit__`
        """
        # Your custom code here to complete the import
        pass
```

When [`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) is
called with a custom [`UnlabeledImageDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.UnlabeledImageDatasetImporter), the import is effectively
performed via the pseudocode below:

```python
import fiftyone as fo

dataset = fo.Dataset(...)
importer = CustomUnlabeledImageDatasetImporter(...)

with importer:
    for image_path, image_metadata in importer:
        dataset.add_sample(
            fo.Sample(filepath=image_path, metadata=image_metadata)
        )

    if importer.has_dataset_info:
        info = importer.get_dataset_info()
        parse_info(dataset, info)
```

Note that the importer is invoked via its context manager interface, which
automatically calls the
[`setup()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.UnlabeledImageDatasetImporter.setup)
and
[`close()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.UnlabeledImageDatasetImporter.close)
methods of the importer to handle setup/completion of the import.

The images in the dataset are iteratively loaded by invoking the
[`__next__()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.UnlabeledImageDatasetImporter.__next__)
method of the importer.

The
[`has_dataset_info`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.UnlabeledImageDatasetImporter.has_dataset_info)
property of the importer allows it to declare whether its
[`get_dataset_info()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.UnlabeledImageDatasetImporter.get_dataset_info)
method should be called after all samples have been imported to retrieve
dataset-level information to store on the FiftyOne dataset. See
[this section](#importing-dataset-level-info) for more information.

The
[`has_image_metadata`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.UnlabeledImageDatasetImporter.has_image_metadata)
property of the importer allows it to declare whether it returns
[`ImageMetadata`](../api/fiftyone.core.metadata.md#fiftyone.core.metadata.ImageMetadata) instances for each image that it loads when
[`__next__()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.UnlabeledImageDatasetImporter.__next__)
is called.

To define a custom importer for labeled image datasets, implement the
[`LabeledImageDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.LabeledImageDatasetImporter) interface.

The pseudocode below provides a template for a custom
[`LabeledImageDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.LabeledImageDatasetImporter):

```python
import fiftyone.utils.data as foud

class CustomLabeledImageDatasetImporter(foud.LabeledImageDatasetImporter):
    """Custom importer for labeled image datasets.

    Args:
        dataset_dir (None): the dataset directory. This may be optional for
            some importers
        shuffle (False): whether to randomly shuffle the order in which the
            samples are imported
        seed (None): a random seed to use when shuffling
        max_samples (None): a maximum number of samples to import. By default,
            all samples are imported
        **kwargs: additional keyword arguments for your importer
    """

    def __init__(
        self,
        dataset_dir=None,
        shuffle=False,
        seed=None,
        max_samples=None,
        **kwargs,
    ):
        super().__init__(
            dataset_dir=dataset_dir,
            shuffle=shuffle,
            seed=seed,
            max_samples=max_samples,
        )
        # Your initialization here

    def __len__(self):
        """The total number of samples that will be imported.

        Raises:
            TypeError: if the total number is not known
        """
        # Return the total number of samples in the dataset (if known)
        pass

    def __next__(self):
        """Returns information about the next sample in the dataset.

        Returns:
            an  ``(image_path, image_metadata, label)`` tuple, where

            -   ``image_path``: the path to the image on disk
            -   ``image_metadata``: an
                :class:`fiftyone.core.metadata.ImageMetadata` instances for the
                image, or ``None`` if :meth:`has_image_metadata` is ``False``
            -   ``label``: an instance of :meth:`label_cls`, or a dictionary
                mapping field names to :class:`fiftyone.core.labels.Label`
                instances, or ``None`` if the sample is unlabeled

        Raises:
            StopIteration: if there are no more samples to import
        """
        # Implement loading the next sample in your dataset here
        pass

    @property
    def has_dataset_info(self):
        """Whether this importer produces a dataset info dictionary."""
        # Return True or False here
        pass

    @property
    def has_image_metadata(self):
        """Whether this importer produces
        :class:`fiftyone.core.metadata.ImageMetadata` instances for each image.
        """
        # Return True or False here
        pass

    @property
    def label_cls(self):
        """The :class:`fiftyone.core.labels.Label` class(es) returned by this
        importer.

        This can be any of the following:

        -   a :class:`fiftyone.core.labels.Label` class. In this case, the
            importer is guaranteed to return labels of this type
        -   a list or tuple of :class:`fiftyone.core.labels.Label` classes. In
            this case, the importer can produce a single label field of any of
            these types
        -   a dict mapping keys to :class:`fiftyone.core.labels.Label` classes.
            In this case, the importer will return label dictionaries with keys
            and value-types specified by this dictionary. Not all keys need be
            present in the imported labels
        -   ``None``. In this case, the importer makes no guarantees about the
            labels that it may return
        """
        # Return the appropriate value here
        pass

    def setup(self):
        """Performs any necessary setup before importing the first sample in
        the dataset.

        This method is called when the importer's context manager interface is
        entered, :func:`DatasetImporter.__enter__`.
        """
        # Your custom setup here
        pass

    def get_dataset_info(self):
        """Returns the dataset info for the dataset.

        By convention, this method should be called after all samples in the
        dataset have been imported.

        Returns:
            a dict of dataset info
        """
        # Return a dict of dataset info, if supported by your importer
        pass

    def close(self, *args):
        """Performs any necessary actions after the last sample has been
        imported.

        This method is called when the importer's context manager interface is
        exited, :func:`DatasetImporter.__exit__`.

        Args:
            *args: the arguments to :func:`DatasetImporter.__exit__`
        """
        # Your custom code here to complete the import
        pass
```

When [`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) is
called with a custom [`LabeledImageDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.LabeledImageDatasetImporter), the import is effectively
performed via the pseudocode below:

```python
import fiftyone as fo

dataset = fo.Dataset(...)
importer = CustomLabeledImageDatasetImporter(...)
label_field = ...

if isinstance(label_field, dict):
    label_key = lambda k: label_field.get(k, k)
elif label_field is not None:
    label_key = lambda k: label_field + "_" + k
else:
    label_field = "ground_truth"
    label_key = lambda k: k

with importer:
    for image_path, image_metadata, label in importer:
        sample = fo.Sample(filepath=image_path, metadata=image_metadata)

        if isinstance(label, dict):
            sample.update_fields({label_key(k): v for k, v in label.items()})
        elif label is not None:
            sample[label_field] = label

        dataset.add_sample(sample)

    if importer.has_dataset_info:
        info = importer.get_dataset_info()
        parse_info(dataset, info)
```

Note that the importer is invoked via its context manager interface, which
automatically calls the
[`setup()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.LabeledImageDatasetImporter.setup)
and
[`close()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.LabeledImageDatasetImporter.close)
methods of the importer to handle setup/completion of the import.

The images and their corresponding [`Label`](../api/fiftyone.core.labels.md#fiftyone.core.labels.Label) instances in the dataset are
iteratively loaded by invoking the
[`__next__()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.LabeledImageDatasetImporter.__next__)
method of the importer.

The
[`has_dataset_info`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.LabeledImageDatasetImporter.has_dataset_info)
property of the importer allows it to declare whether its
[`get_dataset_info()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.LabeledImageDatasetImporter.get_dataset_info)
method should be called after all samples have been imported to retrieve
dataset-level information to store on the FiftyOne dataset. See
[this section](#importing-dataset-level-info) for more information.

The
[`label_cls`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.LabeledImageDatasetImporter.label_cls)
property of the importer declares the type of label(s) that the importer
will produce.

The
[`has_image_metadata`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.LabeledImageDatasetImporter.has_image_metadata)
property of the importer allows it to declare whether it returns
[`ImageMetadata`](../api/fiftyone.core.metadata.md#fiftyone.core.metadata.ImageMetadata) instances for each image that it loads when
[`__next__()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.LabeledImageDatasetImporter.__next__)
is called.

To define a custom importer for unlabeled video datasets, implement the
[`UnlabeledVideoDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.UnlabeledVideoDatasetImporter) interface.

The pseudocode below provides a template for a custom
[`UnlabeledVideoDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.UnlabeledVideoDatasetImporter):

```python
import fiftyone.utils.data as foud

class CustomUnlabeledVideoDatasetImporter(foud.UnlabeledVideoDatasetImporter):
    """Custom importer for unlabeled video datasets.

    Args:
        dataset_dir (None): the dataset directory. This may be optional for
            some importers
        shuffle (False): whether to randomly shuffle the order in which the
            samples are imported
        seed (None): a random seed to use when shuffling
        max_samples (None): a maximum number of samples to import. By default,
            all samples are imported
        **kwargs: additional keyword arguments for your importer
    """

    def __init__(
        self,
        dataset_dir=None,
        shuffle=False,
        seed=None,
        max_samples=None,
        **kwargs,
    ):
        super().__init__(
            dataset_dir=dataset_dir,
            shuffle=shuffle,
            seed=seed,
            max_samples=max_samples,
        )
        # Your initialization here

    def __len__(self):
        """The total number of samples that will be imported.

        Raises:
            TypeError: if the total number is not known
        """
        # Return the total number of samples in the dataset (if known)
        pass

    def __next__(self):
        """Returns information about the next sample in the dataset.

        Returns:
            an ``(video_path, video_metadata)`` tuple, where:
            -   ``video_path`` is the path to the video on disk
            -   ``video_metadata`` is an
                :class:`fiftyone.core.metadata.VideoMetadata` instances for the
                video, or ``None`` if :meth:`has_video_metadata` is ``False``

        Raises:
            StopIteration: if there are no more samples to import
        """
        # Implement loading the next sample in your dataset here
        pass

    @property
    def has_dataset_info(self):
        """Whether this importer produces a dataset info dictionary."""
        # Return True or False here
        pass

    @property
    def has_video_metadata(self):
        """Whether this importer produces
        :class:`fiftyone.core.metadata.VideoMetadata` instances for each video.
        """
        # Return True or False here
        pass

    def setup(self):
        """Performs any necessary setup before importing the first sample in
        the dataset.

        This method is called when the importer's context manager interface is
        entered, :func:`DatasetImporter.__enter__`.
        """
        # Your custom setup here
        pass

    def get_dataset_info(self):
        """Returns the dataset info for the dataset.

        By convention, this method should be called after all samples in the
        dataset have been imported.

        Returns:
            a dict of dataset info
        """
        # Return a dict of dataset info, if supported by your importer
        pass

    def close(self, *args):
        """Performs any necessary actions after the last sample has been
        imported.

        This method is called when the importer's context manager interface is
        exited, :func:`DatasetImporter.__exit__`.

        Args:
            *args: the arguments to :func:`DatasetImporter.__exit__`
        """
        # Your custom code here to complete the import
        pass
```

When [`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) is
called with a custom [`UnlabeledVideoDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.UnlabeledVideoDatasetImporter), the import is effectively
performed via the pseudocode below:

```python
import fiftyone as fo

dataset = fo.Dataset(...)
importer = CustomUnlabeledVideoDatasetImporter(...)

with importer:
    for video_path, video_metadata in importer:
        dataset.add_sample(
            fo.Sample(filepath=video_path, metadata=video_metadata)
        )

    if importer.has_dataset_info:
        info = importer.get_dataset_info()
        parse_info(dataset, info)
```

Note that the importer is invoked via its context manager interface, which
automatically calls the
[`setup()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.UnlabeledVideoDatasetImporter.setup)
and
[`close()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.UnlabeledVideoDatasetImporter.close)
methods of the importer to handle setup/completion of the import.

The videos in the dataset are iteratively loaded by invoking the
[`__next__()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.UnlabeledVideoDatasetImporter.__next__)
method of the importer.

The
[`has_dataset_info`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.UnlabeledVideoDatasetImporter.has_dataset_info)
property of the importer allows it to declare whether its
[`get_dataset_info()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.UnlabeledVideoDatasetImporter.get_dataset_info)
method should be called after all samples have been imported to retrieve
dataset-level information to store on the FiftyOne dataset. See
[this section](#importing-dataset-level-info) for more information.

The
[`has_video_metadata`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.UnlabeledVideoDatasetImporter.has_video_metadata)
property of the importer allows it to declare whether it returns
[`VideoMetadata`](../api/fiftyone.core.metadata.md#fiftyone.core.metadata.VideoMetadata) instances for each video that it loads when
[`__next__()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.UnlabeledVideoDatasetImporter.__next__)
is called.

To define a custom importer for labeled video datasets, implement the
[`LabeledVideoDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.LabeledVideoDatasetImporter) interface.

The pseudocode below provides a template for a custom
[`LabeledVideoDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.LabeledVideoDatasetImporter):

```python
import fiftyone.utils.data as foud

class CustomLabeledVideoDatasetImporter(foud.LabeledVideoDatasetImporter):
    """Custom importer for labeled video datasets.

    Args:
        dataset_dir (None): the dataset directory. This may be optional for
            some importers
        shuffle (False): whether to randomly shuffle the order in which the
            samples are imported
        seed (None): a random seed to use when shuffling
        max_samples (None): a maximum number of samples to import. By default,
            all samples are imported
        **kwargs: additional keyword arguments for your importer
    """

    def __init__(
        self,
        dataset_dir=None,
        shuffle=False,
        seed=None,
        max_samples=None,
        **kwargs,
    ):
        super().__init__(
            dataset_dir=dataset_dir,
            shuffle=shuffle,
            seed=seed,
            max_samples=max_samples,
        )
        # Your initialization here

    def __len__(self):
        """The total number of samples that will be imported.

        Raises:
            TypeError: if the total number is not known
        """
        # Return the total number of samples in the dataset (if known)
        pass

    def __next__(self):
    """Returns information about the next sample in the dataset.

        Returns:
            an  ``(video_path, video_metadata, labels, frames)`` tuple, where

            -   ``video_path``: the path to the video on disk
            -   ``video_metadata``: an
                :class:`fiftyone.core.metadata.VideoMetadata` instances for the
                video, or ``None`` if :meth:`has_video_metadata` is ``False``
            -   ``labels``: sample-level labels for the video, which can be any
                of the following::

                -   a :class:`fiftyone.core.labels.Label` instance
                -   a dictionary mapping label fields to
                    :class:`fiftyone.core.labels.Label` instances
                -   ``None`` if the sample has no sample-level labels

            -   ``frames``: frame-level labels for the video, which can
                be any of the following::

                -   a dictionary mapping frame numbers to dictionaries that
                    map label fields to :class:`fiftyone.core.labels.Label`
                    instances for each video frame
                -   ``None`` if the sample has no frame-level labels

        Raises:
            StopIteration: if there are no more samples to import
        """
        # Implement loading the next sample in your dataset here
        pass

    @property
    def has_dataset_info(self):
        """Whether this importer produces a dataset info dictionary."""
        # Return True or False here
        pass

    @property
    def has_video_metadata(self):
        """Whether this importer produces
        :class:`fiftyone.core.metadata.VideoMetadata` instances for each video.
        """
        # Return True or False here
        pass

    @property
    def label_cls(self):
        """The :class:`fiftyone.core.labels.Label` class(es) returned by this
        importer within the sample-level labels that it produces.

        This can be any of the following:

        -   a :class:`fiftyone.core.labels.Label` class. In this case, the
            importer is guaranteed to return sample-level labels of this type
        -   a list or tuple of :class:`fiftyone.core.labels.Label` classes. In
            this case, the importer can produce a single sample-level label
            field of any of these types
        -   a dict mapping keys to :class:`fiftyone.core.labels.Label` classes.
            In this case, the importer will return sample-level label
            dictionaries with keys and value-types specified by this
            dictionary. Not all keys need be present in the imported labels
        -   ``None``. In this case, the importer makes no guarantees about the
            sample-level labels that it may return
        """
        # Return the appropriate value here
        pass

    @property
    def frame_label_cls(self):
        """The :class:`fiftyone.core.labels.Label` class(es) returned by this
        importer within the frame labels that it produces.

        This can be any of the following:

        -   a :class:`fiftyone.core.labels.Label` class. In this case, the
            importer is guaranteed to return frame labels of this type
        -   a list or tuple of :class:`fiftyone.core.labels.Label` classes. In
            this case, the importer can produce a single frame label field of
            any of these types
        -   a dict mapping keys to :class:`fiftyone.core.labels.Label` classes.
            In this case, the importer will return frame label dictionaries
            with keys and value-types specified by this dictionary. Not all
            keys need be present in each frame
        -   ``None``. In this case, the importer makes no guarantees about the
            frame labels that it may return
        """
        # Return the appropriate value here
        pass

    def setup(self):
        """Performs any necessary setup before importing the first sample in
        the dataset.

        This method is called when the importer's context manager interface is
        entered, :func:`DatasetImporter.__enter__`.
        """
        # Your custom setup here
        pass

    def get_dataset_info(self):
        """Returns the dataset info for the dataset.

        By convention, this method should be called after all samples in the
        dataset have been imported.

        Returns:
            a dict of dataset info
        """
        # Return a dict of dataset info, if supported by your importer
        pass

    def close(self, *args):
        """Performs any necessary actions after the last sample has been
        imported.

        This method is called when the importer's context manager interface is
        exited, :func:`DatasetImporter.__exit__`.

        Args:
            *args: the arguments to :func:`DatasetImporter.__exit__`
        """
        # Your custom code here to complete the import
        pass
```

When [`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) is
called with a custom [`LabeledVideoDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.LabeledVideoDatasetImporter), the import is effectively
performed via the pseudocode below:

```python
import fiftyone as fo

dataset = fo.Dataset(...)
importer = CustomLabeledVideoDatasetImporter(...)
label_field = ...

if isinstance(label_field, dict):
    label_key = lambda k: label_field.get(k, k)
elif label_field is not None:
    label_key = lambda k: label_field + "_" + k
else:
    label_field = "ground_truth"
    label_key = lambda k: k

with importer:
    for video_path, video_metadata, label, frames in importer:
        sample = fo.Sample(filepath=video_path, metadata=video_metadata)

        if isinstance(label, dict):
            sample.update_fields({label_key(k): v for k, v in label.items()})
        elif label is not None:
            sample[label_field] = label

        if frames is not None:
            frame_labels = {}

            for frame_number, _label in frames.items():
                if isinstance(_label, dict):
                    frame_labels[frame_number] = {
                        label_key(k): v for k, v in _label.items()
                    }
                elif _label is not None:
                    frame_labels[frame_number] = {label_field: _label}

            sample.frames.merge(frame_labels)

        dataset.add_sample(sample)

    if importer.has_dataset_info:
        info = importer.get_dataset_info()
        parse_info(dataset, info)
```

Note that the importer is invoked via its context manager interface, which
automatically calls the
[`setup()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.LabeledVideoDatasetImporter.setup)
and
[`close()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.LabeledVideoDatasetImporter.close)
methods of the importer to handle setup/completion of the import.

The videos and their corresponding labels in the dataset are iteratively
loaded by invoking the
[`__next__()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.LabeledVideoDatasetImporter.__next__)
method of the importer. In particular, sample-level labels for the video
may be returned in a `label` value (which may contain a single [`Label`](../api/fiftyone.core.labels.md#fiftyone.core.labels.Label)
value or a dictionary that maps field names to labels), and frame-level
labels may be returned in a `frames` dictionary that maps frame numbers
to dictionaries of field names and labels.

The
[`has_dataset_info`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.LabeledVideoDatasetImporter.has_dataset_info)
property of the importer allows it to declare whether its
[`get_dataset_info()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.LabeledVideoDatasetImporter.get_dataset_info)
method should be called after all samples have been imported to retrieve
dataset-level information to store on the FiftyOne dataset. See
[this section](#importing-dataset-level-info) for more information.

The
[`label_cls`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.LabeledVideoDatasetImporter.label_cls)
property of the importer declares the type of sample-level label(s) that
the importer will produce (if any), and the
[`frame_labels_cls`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.LabeledVideoDatasetImporter.frame_labels_cls)
property of the importer declares the type of frame-level label(s) that the
importer will produce (if any).

The
[`has_video_metadata`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.LabeledVideoDatasetImporter.has_video_metadata)
property of the importer allows it to declare whether it returns
[`VideoMetadata`](../api/fiftyone.core.metadata.md#fiftyone.core.metadata.VideoMetadata) instances for each video that it loads when
[`__next__()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.LabeledVideoDatasetImporter.__next__)
is called.

To define a custom importer for grouped datasets, implement the
[`GroupDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.GroupDatasetImporter) interface.

The pseudocode below provides a template for a custom
[`GroupDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.GroupDatasetImporter):

```python
import fiftyone.utils.data as foud

class CustomGroupDatasetImporter(foud.GroupDatasetImporter):
    """Custom importer for grouped datasets.

    Args:
        dataset_dir (None): the dataset directory. This may be optional for
            some importers
        shuffle (False): whether to randomly shuffle the order in which the
            samples are imported
        seed (None): a random seed to use when shuffling
        max_samples (None): a maximum number of samples to import. By default,
            all samples are imported
        **kwargs: additional keyword arguments for your importer
    """

    def __init__(
        self,
        dataset_dir=None,
        shuffle=False,
        seed=None,
        max_samples=None,
        **kwargs,
    ):
        super().__init__(
            dataset_dir=dataset_dir,
            shuffle=shuffle,
            seed=seed,
            max_samples=max_samples
        )
        # Your initialization here

    def __len__(self):
        """The total number of samples that will be imported across all group
        slices.

        Raises:
            TypeError: if the total number is not known
        """
        # Return the total number of samples in the dataset (if known)
        pass

    def __next__(self):
        """Returns information about the next group in the dataset.

        Returns:
            a dict mapping slice names to :class:`fiftyone.core.sample.Sample`
            instances

        Raises:
            StopIteration: if there are no more groups to import
        """
        # Implement loading the next group in your dataset here
        pass

    @property
    def has_dataset_info(self):
        """Whether this importer produces a dataset info dictionary."""
        # Return True or False here
        pass

    @property
    def has_sample_field_schema(self):
        """Whether this importer produces a sample field schema."""
        # Return True or False here
        pass

    @property
    def group_field(self):
        """The name of the group field to populate on each sample."""
        # This is the default, but you can customize if desired
        return "group"

    def setup(self):
        """Performs any necessary setup before importing the first sample in
        the dataset.

        This method is called when the importer's context manager interface is
        entered, :func:`DatasetImporter.__enter__`.
        """
        # Your custom setup here
        pass

    def get_dataset_info(self):
        """Returns the dataset info for the dataset.

        By convention, this method should be called after all samples in the
        dataset have been imported.

        Returns:
            a dict of dataset info
        """
        # Return a dict of dataset info, if supported by your importer
        pass

    def get_sample_field_schema(self):
        """Returns a dictionary describing the field schema of the samples
        loaded by this importer.

        Returns:
            a dict mapping field names to :class:`fiftyone.core.fields.Field`
            instances or ``str(field)`` representations of them
        """
        # Return the sample schema here, if known
        pass

    def get_group_media_types(self):
        """Returns a dictionary describing the group slices of the samples
        loaded by this importer.

        Returns:
            a dict mapping slice names to media types
        """
        # Return the group media types here, if known
        pass

    def close(self, *args):
        """Performs any necessary actions after the last sample has been
        imported.

        This method is called when the importer's context manager interface is
        exited, :func:`DatasetImporter.__exit__`.

        Args:
            *args: the arguments to :func:`DatasetImporter.__exit__`
        """
        # Your custom code here to complete the import
        pass
```

When [`Dataset.from_dir()`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.from_dir) is
called with a custom [`GroupDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.GroupDatasetImporter), the import is effectively
performed via the pseudocode below:

```python
import fiftyone as fo

dataset = fo.Dataset(...)
importer = CustomGroupDatasetImporter(...)
group_field = importer.group_field

with importer:
    for group in importer:
        _group = fo.Group()
        for name, sample in group.items():
            sample[group_field] = _group.element(name)
            dataset.add_sample(sample)

    if importer.has_dataset_info:
        info = importer.get_dataset_info()
        parse_info(dataset, info)
```

Note that the importer is invoked via its context manager interface, which
automatically calls the
[`setup()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.GroupDatasetImporter.setup)
and
[`close()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.GroupDatasetImporter.close)
methods of the importer to handle setup/completion of the import.

The groups in the dataset are iteratively loaded by invoking the
[`__next__()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.GroupDatasetImporter.__next__)
method of the importer.

The
[`has_dataset_info`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.GroupDatasetImporter.has_dataset_info)
property of the importer allows it to declare whether its
[`get_dataset_info()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.GroupDatasetImporter.get_dataset_info)
method should be called after all samples have been imported to retrieve
dataset-level information to store on the FiftyOne dataset. See
[this section](#importing-dataset-level-info) for more information.

The
[`group_field`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.GroupDatasetImporter.group_field)
property of the importer allows it to declare the name of the field in
which to store the [`Group`](../api/fiftyone.core.groups.md#fiftyone.core.groups.Group) information for each sample.

<a id="importing-dataset-level-info"></a>

### Importing dataset-level information

The
[`has_dataset_info`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.DatasetImporter.has_dataset_info)
property of the importer allows it to declare whether its
[`get_dataset_info()`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.DatasetImporter.get_dataset_info)
method should be called after all samples have been imported to retrieve a dict
of dataset-level information to store in the
[`info`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.info) property of the dataset.

As a special case, if the `info` dict contains any of the keys listed below,
these items are popped and stored in the corresponding dedicated dataset field:

- `"classes"` key:
  [`Dataset.classes`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.classes)
- `"default_classes"` key:
  [`Dataset.default_classes`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.default_classes)
- `"mask_targets"` key:
  [`Dataset.mask_targets`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.mask_targets)
- `"default_mask_targets"` key:
  [`Dataset.default_mask_targets`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.default_mask_targets)
- `"skeletons"` key:
  [`Dataset.skeletons`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.skeletons)
- `"default_skeleton"` key:
  [`Dataset.default_skeleton`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.default_skeleton)
- `"app_config"` key:
  [`Dataset.app_config`](../api/fiftyone.core.dataset.md#fiftyone.core.dataset.Dataset.app_config)

<a id="writing-a-custom-dataset-type-importer"></a>

### Writing a custom Dataset type

FiftyOne provides the [`Dataset`](../api/fiftyone.types.md#fiftyone.types.Dataset) type system so that dataset formats can be
conveniently referenced by their type when reading/writing datasets on disk.

The primary function of the [`Dataset`](../api/fiftyone.types.md#fiftyone.types.Dataset) subclasses is to define the
[`DatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.DatasetImporter) that should be used to read instances of the dataset from
disk and the [`DatasetExporter`](../api/fiftyone.utils.data.exporters.md#fiftyone.utils.data.exporters.DatasetExporter) that should be used to write instances of the
dataset to disk.

See [this page](export_datasets.md#writing-a-custom-dataset-exporter) for more information
about defining custom [`DatasetExporter`](../api/fiftyone.utils.data.exporters.md#fiftyone.utils.data.exporters.DatasetExporter) classes.

Custom dataset types can be declared by implementing the [`Dataset`](../api/fiftyone.types.md#fiftyone.types.Dataset) subclass
corresponding to the type of dataset that you are working with.

Generic datasets

Unlabeled image datasets

Labeled image datasets

Unlabeled video datasets

Labeled video datasets

Grouped datasets

The pseudocode below provides a template for a custom [`Dataset`](../api/fiftyone.types.md#fiftyone.types.Dataset)
subclass that represents a collection of arbitrary content:

```python
import fiftyone.types as fot

class CustomDataset(fot.Dataset):
    """Custom dataset type."""

    def get_dataset_importer_cls(self):
        """Returns the
        :class:`fiftyone.utils.data.importers.DatasetImporter`
        class for importing datasets of this type from disk.

        Returns:
            a :class:`fiftyone.utils.data.importers.DatasetImporter`
            class
        """
        # Return your custom DatasetImporter class here
        pass

    def get_dataset_exporter_cls(self):
        """Returns the
        :class:`fiftyone.utils.data.exporters.DatasetExporter`
        class for exporting datasets of this type to disk.

        Returns:
            a :class:`fiftyone.utils.data.exporters.DatasetExporter`
            class
        """
        # Return your custom DatasetExporter class here
        pass
```

Note that, as this type represents a dataset of arbitrary content, its
importer should subclass from the base [`DatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.DatasetImporter), and its exporter
should subclass from the base [`DatasetExporter`](../api/fiftyone.utils.data.exporters.md#fiftyone.utils.data.exporters.DatasetExporter).

The pseudocode below provides a template for a custom
[`UnlabeledImageDataset`](../api/fiftyone.types.md#fiftyone.types.UnlabeledImageDataset) subclass:

```python
import fiftyone.types as fot

class CustomUnlabeledImageDataset(fot.UnlabeledImageDataset):
    """Custom unlabeled image dataset type."""

    def get_dataset_importer_cls(self):
        """Returns the
        :class:`fiftyone.utils.data.importers.UnlabeledImageDatasetImporter`
        class for importing datasets of this type from disk.

        Returns:
            a :class:`fiftyone.utils.data.importers.UnlabeledImageDatasetImporter`
            class
        """
        # Return your custom UnlabeledImageDatasetImporter class here
        pass

    def get_dataset_exporter_cls(self):
        """Returns the
        :class:`fiftyone.utils.data.exporters.UnlabeledImageDatasetExporter`
        class for exporting datasets of this type to disk.

        Returns:
            a :class:`fiftyone.utils.data.exporters.UnlabeledImageDatasetExporter`
            class
        """
        # Return your custom UnlabeledImageDatasetExporter class here
        pass
```

Note that, as this type represents an unlabeled image dataset, its importer
must be a subclass of [`UnlabeledImageDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.UnlabeledImageDatasetImporter), and its exporter
must be a subclass of [`UnlabeledImageDatasetExporter`](../api/fiftyone.utils.data.exporters.md#fiftyone.utils.data.exporters.UnlabeledImageDatasetExporter).

The pseudocode below provides a template for a custom
[`LabeledImageDataset`](../api/fiftyone.types.md#fiftyone.types.LabeledImageDataset) subclass:

```python
import fiftyone.types as fot

class CustomLabeledImageDataset(fot.LabeledImageDataset):
    """Custom labeled image dataset type."""

    def get_dataset_importer_cls(self):
        """Returns the
        :class:`fiftyone.utils.data.importers.LabeledImageDatasetImporter`
        class for importing datasets of this type from disk.

        Returns:
            a :class:`fiftyone.utils.data.importers.LabeledImageDatasetImporter`
            class
        """
        # Return your custom LabeledImageDatasetImporter class here
        pass

    def get_dataset_exporter_cls(self):
        """Returns the
        :class:`fiftyone.utils.data.exporters.LabeledImageDatasetExporter`
        class for exporting datasets of this type to disk.

        Returns:
            a :class:`fiftyone.utils.data.exporters.LabeledImageDatasetExporter`
            class
        """
        # Return your custom LabeledImageDatasetExporter class here
        pass
```

Note that, as this type represents a labeled image dataset, its importer
must be a subclass of [`LabeledImageDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.LabeledImageDatasetImporter), and its exporter must
be a subclass of [`LabeledImageDatasetExporter`](../api/fiftyone.utils.data.exporters.md#fiftyone.utils.data.exporters.LabeledImageDatasetExporter).

The pseudocode below provides a template for a custom
[`UnlabeledVideoDataset`](../api/fiftyone.types.md#fiftyone.types.UnlabeledVideoDataset) subclass:

```python
import fiftyone.types as fot

class CustomUnlabeledVideoDataset(fot.UnlabeledVideoDataset):
    """Custom unlabeled video dataset type."""

    def get_dataset_importer_cls(self):
        """Returns the
        :class:`fiftyone.utils.data.importers.UnlabeledVideoDatasetImporter`
        class for importing datasets of this type from disk.

        Returns:
            a :class:`fiftyone.utils.data.importers.UnlabeledVideoDatasetImporter`
            class
        """
        # Return your custom UnlabeledVideoDatasetImporter class here
        pass

    def get_dataset_exporter_cls(self):
        """Returns the
        :class:`fiftyone.utils.data.exporters.UnlabeledVideoDatasetExporter`
        class for exporting datasets of this type to disk.

        Returns:
            a :class:`fiftyone.utils.data.exporters.UnlabeledVideoDatasetExporter`
            class
        """
        # Return your custom UnlabeledVideoDatasetExporter class here
        pass
```

Note that, as this type represents an unlabeled video dataset, its importer
must be a subclass of [`UnlabeledVideoDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.UnlabeledVideoDatasetImporter), and its exporter
must be a subclass of [`UnlabeledVideoDatasetExporter`](../api/fiftyone.utils.data.exporters.md#fiftyone.utils.data.exporters.UnlabeledVideoDatasetExporter).

The pseudocode below provides a template for a custom
[`LabeledVideoDataset`](../api/fiftyone.types.md#fiftyone.types.LabeledVideoDataset) subclass:

```python
import fiftyone.types as fot

class CustomLabeledVideoDataset(fot.LabeledVideoDataset):
    """Custom labeled video dataset type."""

    def get_dataset_importer_cls(self):
        """Returns the
        :class:`fiftyone.utils.data.importers.LabeledVideoDatasetImporter`
        class for importing datasets of this type from disk.

        Returns:
            a :class:`fiftyone.utils.data.importers.LabeledVideoDatasetImporter`
            class
        """
        # Return your custom LabeledVideoDatasetImporter class here
        pass

    def get_dataset_exporter_cls(self):
        """Returns the
        :class:`fiftyone.utils.data.exporters.LabeledVideoDatasetExporter`
        class for exporting datasets of this type to disk.

        Returns:
            a :class:`fiftyone.utils.data.exporters.LabeledVideoDatasetExporter`
            class
        """
        # Return your custom LabeledVideoDatasetExporter class here
        pass
```

Note that, as this type represents a labeled video dataset, its importer
must be a subclass of [`LabeledVideoDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.LabeledVideoDatasetImporter), and its exporter must
be a subclass of [`LabeledVideoDatasetExporter`](../api/fiftyone.utils.data.exporters.md#fiftyone.utils.data.exporters.LabeledVideoDatasetExporter).

The pseudocode below provides a template for a custom [`GroupDataset`](../api/fiftyone.types.md#fiftyone.types.GroupDataset)
subclass:

```python
import fiftyone.types as fot

class CustomGroupDataset(fot.GroupDataset):
    """Custom grouped dataset type."""

    def get_dataset_importer_cls(self):
        """Returns the
        :class:`fiftyone.utils.data.importers.GroupDatasetImporter`
        class for importing datasets of this type from disk.

        Returns:
            a :class:`fiftyone.utils.data.importers.GroupDatasetImporter`
            class
        """
        # Return your custom GroupDatasetImporter class here
        pass

    def get_dataset_exporter_cls(self):
        """Returns the
        :class:`fiftyone.utils.data.exporters.GroupDatasetExporter`
        class for exporting datasets of this type to disk.

        Returns:
            a :class:`fiftyone.utils.data.exporters.GroupDatasetExporter`
            class
        """
        # Return your custom GroupDatasetExporter class here
        pass
```

Note that, as this type represents a grouped dataset, its importer must be
a subclass of [`GroupDatasetImporter`](../api/fiftyone.utils.data.importers.md#fiftyone.utils.data.importers.GroupDatasetImporter), and its exporter must be a subclass
of [`GroupDatasetExporter`](../api/fiftyone.utils.data.exporters.md#fiftyone.utils.data.exporters.GroupDatasetExporter).
