# LuxonisDataset

## 概述

LuxonisDataset 类提供了一个简单的 API，用于在 Luxonis 数据格式 (LDF) 中创建和管理数据。它充当抽象层，并提供数据集的方法：

 * 初始化、
 * 数据摄取、
 * 分割、
 * 合并，以及
 * 导出、云同步和删除。

以下部分将指导您创建 LDF
数据集。我们准备了一个简单的玩具数据集，供您跟随示例操作（[ParkingLot.zip](https://drive.google.com/uc?export=download&id=1OAuLlL_4wRSzZ33BuxM6Uw2QYYgv_19N)）。该数据集包含停车场中汽车和摩托车的图像，每张图像都带有边界框、关键点和分割掩码的注释。

## 数据集初始化

数据集的创建过程从初始化 LuxonisDataset 对象开始：

```python
from luxonis_ml.data.datasets import LuxonisDataset

dataset_name: str = ... # 例如 "parking_lot"
dataset = LuxonisDataset(dataset_name)
```

> 数据集可以存储在本地，也可以使用受支持的云存储提供商（包括 GCS、S3 和 Azure Blob 存储）进行存储。默认情况下，初始化的数据集存储在本地。

> 如果已存在名为提供的
> `dataset_name`
> 的数据集，则会自动加载该数据集，而不是初始化新的数据集。因此，请为每个新数据集使用唯一的名称，或者在
> `LuxonisDataset`
> 构造函数中传递
> `delete_local=True`
> 以覆盖现有数据集。

如果您需要对存储行为进行更多控制，构造函数还接受 team_id、bucket_type、bucket_storage 和 delete_remote 参数。当数据集应存储在共享或远程对象存储中（而非仅存储在本地机器上）时，这些参数非常有用。

## 添加数据

数据集初始化后，我们可以开始数据摄取。我们必须首先定义一个生成器函数，该函数逐个生成数据实例。每个数据实例存储图像的路径和单个注释（例如边界框）。因此，如果一张图像有多个注释，则必须分别生成多个数据实例。

我们将数据实例定义为具有以下结构的 Python 字典：

```python
{
    "file": str,  # 图像文件的路径
    "annotation": Optional[dict]  # 单个图像注释
}
```

其中 annotation 字段的内容取决于任务类型。支持以下任务类型：

 * [分类](https://github.com/luxonis/luxonis-ml/tree/main/luxonis_ml/data#classification)
 * [边界框](https://github.com/luxonis/luxonis-ml/tree/main/luxonis_ml/data#bounding-box)
 * [关键点](https://github.com/luxonis/luxonis-ml/tree/main/luxonis_ml/data#keypoints)
 * [分割掩码](https://github.com/luxonis/luxonis-ml/tree/main/luxonis_ml/data#segmentation-mask)
 * [实例分割](https://github.com/luxonis/luxonis-ml/tree/main/luxonis_ml/data#instance-segmentation)
 * [数组](https://github.com/luxonis/luxonis-ml/tree/main/luxonis_ml/data#array)
 * [元数据](https://github.com/luxonis/luxonis-ml/tree/main/luxonis_ml/data#metadata)

下面我们提供一个停车数据集的示例生成器函数，用于生成边界框注释的数据实例。

```python
import json
from pathlib import Path

# 数据集的路径，请根据您系统上的实际路径进行替换
dataset_root = Path("data/parking_lot")

def generator():
    for annotation_dir in dataset_root.iterdir():
        with open(annotation_dir / "annotations.json") as f:
            data = json.load(f)

        # 获取图像的宽度和高度
        W = data["dimensions"]["width"]
        H = data["dimensions"]["height"]

        image_path = annotation_dir / data["filename"]

        for instance_id, bbox in data["BoundingBoxAnnotation"].items():

            # 获取未归一化的边界框坐标
            x, y = bbox["origin"]
            w, h = bbox["dimension"]

            # 获取边界框的类别名称
            class_ = bbox["labelName"]
            yield {
                "file": image_path,
                "annotation": {
                    "class": class_,
                    # 归一化的边界框
                    "boundingbox": {
                        "x": x / W,
                        "y": y / H,
                        "w": w / W,
                        "h": h / H,
                    },
                },
            }
```

然后，将生成器传递给数据集的 add 方法。

```python
dataset.add(generator())
```

> `add`
> 方法接受任何可迭代对象，而不仅仅是生成器。

## 元数据与来源

Luxonis 数据集不仅可以存储图像和标签。数据集元数据还记录：

 * 任务定义与类别映射
 * 关键点骨架
 * 分类元数据编码
 * 数据集来源结构

来源结构由 LuxonisSource 和 LuxonisComponent 表示，使得一个数据集可以描述多组件或多传感器输入。例如，一个来源可以包含多个图像组件，而不仅仅是单个 RGB 图像。

有用的元数据相关方法包括：

 * set_tasks(...) 用于显式定义任务组
 * set_classes(...) 用于注册类别映射
 * get_source_names() 用于检查可用的数据集来源
 * update_source(...) 用于更新来源/组件元数据

## 定义划分

在向数据集添加数据后，我们可以定义其划分。 划分名称没有限制，但大多数情况下应使用 train、val 和 test 集合。 划分通过调用 LuxonisDataset 对象的 make_splits 方法并在其参数中传递所需的划分比例来实现
（默认情况下，数据按 80:10:10 的比例划分为 train、val 和 test 集合）。

```python
dataset.make_splits({
  "train": 0.7,
  "val": 0.2,
  "test": 0.1,
})
```

为了更精细地控制划分，你可以传递一个字典，其中键为划分名称，值为文件名列表：

```python
dataset.make_splits({
  "train": ["file1.jpg", "file2.jpg", ...],
  "val": ["file3.jpg", "file4.jpg", ...],
  "test": ["file5.jpg", "file6.jpg", ...],
})
```

一旦完成划分，再次调用 make_splits 方法将引发错误。 如果你想重新定义划分，请在方法调用中传递 redefine_splits=True。

## 云同步与数据集发现

对于远程工作流，LuxonisDataset 还可以：

 * 使用 LuxonisDataset.list_datasets(...) 列出数据集
 * 使用 pull_from_cloud(...) 将缺失或所有媒体拉取到本地
 * 使用 push_to_cloud(...) 将本地数据推送到远程对象存储

当同一数据集在训练机器或团队环境中共享时，这些方法尤其有用。

## 数据集克隆

你可以克隆现有数据集，以新名称创建副本。 这对于在不影响原始数据集的情况下测试更改非常有用。 克隆通过调用 LuxonisDataset 对象的 clone 方法并传递新数据集的所需名称来完成。

```python
dataset_clone = dataset.clone(new_dataset_name="dataset_clone")
```

## 数据集合并

数据集也可以合并在一起。 这对于将多个数据集合并为一个更大、统一的数据集以进行全面训练或分析非常有益。 合并通过调用第一个 LuxonisDataset 对象的 merge_with 方法并将第二个数据集作为参数传递来完成。 你可以选择两种不同的合并模式：

 * inplace（原地合并）：第一个数据集被修改以包含第二个数据集的数据
 * out-of-place（非原地合并）：从两个现有数据集的组合中创建一个新数据集

```python
# inplace merging
dataset1.merge_with(dataset2, inplace=True)
# OR out-of-place merging
dataset_merge = dataset1.merge_with(dataset2, inplace=False, new_dataset_name="dataset_merge")
```

## 数据集导出

LuxonisDataset 可以将数据从 LDF 导出为常见的数据集格式。 当你希望在 LuxonisML 中准备数据，但在其他工具链中训练或检查数据时，这非常有用。

```python
from luxonis_ml.enums import DatasetType

dataset.export("exports/coco", dataset_type=DatasetType.COCO)
```

当前的导出器支持原生 LDF 导出以及多种标准格式，包括 COCO、Pascal VOC、Darknet、YOLOv4、YOLOv6、YOLOv8 任务特定导出器、TensorFlow CSV、CreateML、FiftyOne 分类、分类目录和分割掩码目录。

## CLI 参考

luxonis_ml CLI 提供了一组用于管理数据集的各种有用命令。 这些命令可通过 luxonis_ml data 命令访问。

可用的命令有：

 * luxonis_ml data ls - 列出所有数据集
 * luxonis_ml data info <dataset_name> - 打印数据集信息
 * luxonis_ml data inspect <dataset_name> - 使用 cv2 在屏幕上渲染数据集中的数据
 * luxonis_ml data delete <dataset_name> - 删除数据集

如需更多信息，请运行 luxonis_ml data --help 或向上述任何命令传递 --help 标志。
