# 快照

快照功能让您的应用程序可靠地将视觉数据（图像、视频、点云）以及可选注释发送到Luxonis
Hub。存储为快照后，这些数据可被浏览、筛选或下载，使其成为Luxonis应用中进行数据收集的主要构建块。有关快照的更多信息，请参阅[Hub的快照页面](https://docs.luxonis.com/cloud/features/event-storage/snaps.md)。

在底层，快照是通过EventsManager发送的一种特殊事件。直接发送原始事件是不支持的；作为DepthAI用户，您只与快照交互。

## 快速开始

要从您的应用发送快照，首先创建并复用单个[EventsManager](https://docs.luxonis.com/software-v3/depthai/api/python.md)实例。在典型应用程序中，您应：

 1. 仅创建一次EventsManager（例如，在管道/设备设置旁）。
 2. 在主应用类或上下文中保留对其的引用。
 3. 每当需要将视觉数据上传到Luxonis Hub时，调用sendSnap()。

每个快照必须有一个定义的名称和至少一个关联文件。可包含以下参数：

| 参数 | 描述 | 示例 |
| --- | --- | --- |
| `Name` | 快照的主要标识符 | `car_detected`, `gate_open`, `image` |
| `Files` | 随快照上传的附件 | `ImgFrame`, `ImgDetections` |
| `Tags`（可选） | 用于分组和筛选的额外过滤器 | `dataset_collection`, `数据集1`, `位置72`, `装配线17` |
| `Extras`（可选） | 用于分组和筛选的额外过滤器 | `汽车品牌:volvo`, `状态:开启`, `位置:纽约` |
| `Success Callback`（可选） | 上传尝试成功后的回调 | `on_success` |
| `Failure Callback`（可选） | 上传尝试失败后的回调 | `on_failure` |

### 快照限制

发送快照时，请注意以下限制：

| 字段 | 规则 |
| --- | --- |
| `Name` | 1–56 字符（必填） |
| `Files` | 每个快照最多20个文件（最少1个必填） |
| `Tags` | 最多20个标签；每个1–56字符 |
| `Extras` | 最多25个条目；extras.key 1–40字符；extras.value 0–100字符 |

此外，如果文件上传超出Luxonis Hub强制执行的团队配额限制（可在Hub账单页面查看），可能会被拒绝：

 * 最大文件大小
 * 剩余存储空间
 * 文件上传的小时限制（文件数量和带宽）
 * 发送的事件和快照的小时限制

> **注意：**
> 调用
> `sendSnap()`
> 仅验证基本参数并将快照排队等待上传。这并不保证快包已发送到Hub。
> `sendSnap()`
> 在快照成功排队时返回本地快照ID（字符串），否则返回
> `None`
> /
> `nullopt`
> 。成功排队不确认投递或文件上传。 要获知上传尝试完成的通知，请向
> `sendSnap()`
> 传递
> `successCallback`
> 和
> `failureCallback`
> 参数。

### 身份验证

如果您使用oakctl运行应用程序，身份验证将为您处理。您不需要自己提供API密钥。

对于oakctl run-script，使用您通过oakctl hub login登录的团队。 对于oakctl app run，使用设备所连接的团队。

要发送快照，必须定义团队的API密钥。通常通过DEPTHAI_HUB_API_KEY环境变量设置，或在创建[EventsManager](https://docs.luxonis.com/software-v3/depthai/api/python.md)实例时传递。在独立模式下运行并采用到Hub时，DEPTHAI_HUB_API_KEY会预填充。

[EventsManager](https://docs.luxonis.com/software-v3/depthai/api/python.md)构造函数的API密钥参数是可选的，未设置时默认为空字符串（""）。如果在构造函数中传递了非空API密钥，则优先使用它而非DEPTHAI_HUB_API_KEY。仅当DEPTHAI_HUB_API_KEY未设置，或您有意覆盖它时，才设置构造函数中的API密钥。

有关API密钥最佳实践及实用示例，请[点击此处](https://docs.luxonis.com/software-v3/oak-apps/apikey-good-practices.md)。

## 使用ImgFrame和ImgDetections发送快照

本节演示在Luxonis
DepthAI应用程序中创建[EventsManager](https://docs.luxonis.com/software-v3/depthai/api/python.md)实例，并使用它发送包含单张图像或带有检测结果的图像的快照。

#### Python

```python
# 创建 EventsManager 实例
eventMan = dai.EventsManager()

# 发送包含 ImgFrame 和 ImgDetections 的快照
localSnapID = eventMan.sendSnap(
    name="snap_name",
    fileTag="file_tag",
    imgFrame=inImgFrame,
    imgDetections=inImgDetections,
    tags=["examples", "python"],
    extras={"confidence": "0.75", "location": "01"},
    successCallback=uploadSuccessCallback,
    failureCallback=uploadFailureCallback
)
```

#### C++

```cpp
// 创建 EventsManager 实例
auto eventsManager = std::make_shared<dai::utility::EventsManager>();

// 发送包含 ImgFrame 和 ImgDetections 的快照
auto localSnapID = eventsManager->sendSnap(
    "snap_name", 
    "file_tag", 
    inImgFrame, 
    inImgDetections, 
    {"examples", "C++"}, 
    {{"confidence", "0.75"}, {"location", "01"}},
    uploadSuccessCallback,
    uploadFailureCallback
);
```

Some parameters in [sendSnap()](https://docs.luxonis.com/software-v3/depthai/api/python.md) 中的一些参数是可选的。例如，您可以仅使用快照名称和
[ImgFrame](https://docs.luxonis.com/software-v3/depthai/depthai-components/messages/img_frame.md) 发送快照，省略文件标签、检测结果、标签、额外参数和回调：

#### Python

```python
# 创建 EventsManager 实例
eventMan = dai.EventsManager()

# 发送带有 ImgFrame 的快照
localSnapID = eventMan.sendSnap(
    name="snap_name",
    fileTag=None,
    imgFrame=inImgFrame,
    imgDetections=None,
    tags=[],
    extras={}
)
```

#### C++

```cpp
// 创建 EventsManager 实例
auto eventsManager = std::make_shared<dai::utility::EventsManager>();

// 发送带有 ImgFrame 的快照
auto localSnapID = eventsManager->sendSnap(
    "snap_name", 
    std::nullopt, 
    inImgFrame
);
```

### 在退出前等待待处理的上传

sendSnap() 会将上传任务排队，然后在后台处理。如果 [EventsManager](https://docs.luxonis.com/software-v3/depthai/api/python.md)
实例在上传仍处于待处理状态时被销毁，这些上传将被丢弃。

在应用程序退出前，请调用 [waitForPendingUploads(timeoutMs)](https://docs.luxonis.com/software-v3/depthai/api/python.md) 以保持 EventsManager
处于活动状态，直到处理完待上传文件。timeoutMs 参数是可选的，默认为 0。值为 0 意味着调用将无限期等待，直到上传完成、连接断开或管理器停止。

waitForPendingUploads(timeoutMs) 在所有待处理上传完成时返回 true。如果达到所选超时时间、连接断开或管理器停止，则返回 false。

> **注意：**
> 当许多快照处于待处理状态（排队或缓存）时，此等待可能需要很长时间。

### 上传回调参数（可选）

sendSnap() 可以在上传尝试完成后调用可选的 successCallback 和 failureCallback 参数。如果您需要确认快照上传或从 SendSnapCallbackResult 获取更多信息（如成功上传后的 Hub
ID、有效载荷或上传状态），请使用这些参数。

#### Python

```python
# 定义可选的回调函数
def uploadSuccessCallback(sendSnapResult):
    print(f"Successfully uploaded Snap: {sendSnapResult.snapName} to the hub.")

def uploadFailureCallback(sendSnapResult):
    print(f"Upload of Snap: {sendSnapResult.snapName} to the hub has failed.")

# 创建 EventsManager 实例
eventMan = dai.EventsManager()

# 发送带有 ImgFrame 和 ImgDetections 的快照
localSnapID = eventMan.sendSnap(
    name="snap_name",
    fileTag="file_tag",
    imgFrame=inImgFrame,
    imgDetections=inImgDetections,
    tags=["examples", "python"],
    extras={"confidence": "0.75", "location": "01"},
    successCallback=uploadSuccessCallback,
    failureCallback=uploadFailureCallback
)
```

#### C++

```cpp
// 定义可选的回调函数
void uploadSuccessCallback(const dai::utility::SendSnapCallbackResult sendSnapResult) {
    std::cout << "Successfully uploaded Snap: " << sendSnapResult.snapName << 
    " to the hub." << std::endl;
}

void uploadFailureCallback(const dai::utility::SendSnapCallbackResult sendSnapResult) {
    std::cout << "Upload of Snap: " << sendSnapResult.snapName << 
    " to the hub has failed." << std::endl;
}

// 创建 EventsManager 实例
auto eventsManager = std::make_shared<dai::utility::EventsManager>();

// 发送带有 ImgFrame 和 ImgDetections 的快照
auto localSnapID = eventsManager->sendSnap(
    "snap_name", 
    "file_tag", 
    inImgFrame, 
    inImgDetections, 
    {"examples", "C++"}, 
    {{"confidence", "0.75"}, {"location", "01"}},
    uploadSuccessCallback,
    uploadFailureCallback
);
```

以下链接中可以找到具体用例的详细示例：

### Sending snaps (python)

[Sending snaps (python)](https://github.com/luxonis/depthai-core/blob/main/examples/python/Events/events.py)

### Sending snaps (C++)

[Sending snaps (C++)](https://github.com/luxonis/depthai-core/blob/main/examples/cpp/Events/events.cpp)

## 高级用法：使用 FileGroup 发送快照

在上述示例中，使用 [sendSnap()](https://docs.luxonis.com/software-v3/depthai/api/python.md) 时会根据图像（和检测结果）自动创建一个 FileGroup。您也可以显式创建
FileGroup。当发送 FileGroup 时，所有包含的文件将一起上传到 Luxonis Hub。该组的上传将完全成功或失败，具体取决于您分配的存储容量——该容量必须足够容纳组中的所有文件。

以下示例使用显式的 FileGroup 对象展示了相同的功能。此方法涉及创建一个 FileGroup 实例，并向其中添加文件。 常见的文件对，如
[ImgFrame](https://docs.luxonis.com/software-v3/depthai/depthai-components/messages/img_frame.md) 和
[ImgDetections](https://docs.luxonis.com/software-v3/depthai/depthai-components/messages/img_detections.md)，
可以同时添加，也可以作为单独的文件分别添加。 更多信息可以在 FileGroup 类文档中找到。

#### Python

```python
# 创建 EventsManager 实例
eventMan = dai.EventsManager()

# 创建 FileGroup 实例
fileGroup = dai.FileGroup()

# 向 fileGroup 添加文件
fileGroup.addImageDetectionsPair("file_tag", inImgFrame, inImgDetections)

# 使用 fileGroup 发送快照
localSnapID = eventMan.sendSnap(
    name="snap_name",
    fileGroup=fileGroup,
    tags=["examples", "python"],
    extras={"confidence": "0.75", "location": "01"}
)
```

#### C++

```cpp
// 创建 EventsManager 实例
auto eventsManager = std::make_shared<dai::utility::EventsManager>();

// 创建 FileGroup 实例
auto fileGroup = std::make_shared<dai::utility::FileGroup>();

// 向 fileGroup 添加文件
fileGroup->addImageDetectionsPair("file_tag", inImgFrame, inImgDetections);

// 使用 fileGroup 发送快照
auto localSnapID = eventsManager->sendSnap(
    "snap_name", 
    fileGroup, 
    {"examples", "C++"}, 
    {{"confidence", "0.75"}, {"location", "01"}}
);
```

具体用例的详细示例可在以下链接中找到：

### 使用 FileGroup 发送快照 (Python)

[使用 FileGroup 发送快照 (Python)](https://github.com/luxonis/depthai-core/blob/main/examples/python/Events/events_file_group.py)

### 使用 FileGroup 发送快照 (C++)

[使用 FileGroup 发送快照 (C++)](https://github.com/luxonis/depthai-core/blob/main/examples/cpp/Events/events_file_group.cpp)

## 缓存

当向 Luxonis Hub 发送快照时，网络连接是一个关键依赖项。然而，DepthAI 提供了强大的缓存机制，以确保 视觉数据不会因临时网络中断或连接失败而丢失。本节将解释如何配置和使用快照缓存。

### 概述

默认情况下，当调用 sendSnap() 且快照经过多次重试尝试后仍无法送达 Hub 时，该快照会被丢弃。如果你希望在 网络中断期间保留快照，可以启用本地缓存。启用后，上传失败的快照将存储在本地，并在连接恢复后重新传输。

### 启用缓存

要启用缓存，请在你的 EventsManager 实例上调用 setCacheIfCannotSend() 方法：

#### Python

```python
# 创建 EventsManager 实例
eventMan = dai.EventsManager()

# 启用无法发送的快照的缓存
eventMan.setCacheIfCannotSend(True)
```

#### C++

```cpp
// 创建 EventsManager 实例
auto eventsManager = std::make_shared<dai::utility::EventsManager>();

// 启用无法发送的快照的缓存
eventsManager->setCacheIfCannotSend(true);
```

### 配置缓存目录

默认情况下，缓存的快照存储在默认目录中。若要指定缓存的自定义位置，请使用 setCacheDir() 方法：

#### Python

```python
# 创建 EventsManager 实例
eventMan = dai.EventsManager()

# 启用缓存
eventMan.setCacheIfCannotSend(True)

# 设置自定义缓存目录
eventMan.setCacheDir("/path/to/cache/directory")
```

#### C++

```cpp
// 创建 EventsManager 实例
auto eventsManager = std::make_shared<dai::utility::EventsManager>();

// 启用缓存
eventsManager->setCacheIfCannotSend(true);

// 设置自定义缓存目录
eventsManager->setCacheDir("/path/to/cache/directory");
```

### 在应用程序启动时上传之前缓存的快照

当应用程序重新启动时，之前缓存的快照（如果有）可以在下一次应用运行时自动上传到 Hub。 要启用此行为，请在构造 EventsManager 时将 uploadCachedOnStart 标志设置为 true：

#### Python

```python
# 创建 EventsManager 实例并启用 uploadCachedOnStart
eventMan = dai.EventsManager(uploadCachedOnStart=True)
```

#### C++

```cpp
// 创建 EventsManager 实例并启用 uploadCachedOnStart
auto eventsManager = std::make_shared<dai::utility::EventsManager>("", true);
```

The uploadCachedOnStart 标志决定缓存的快照是否在应用程序重启后持久保存。如果未启用 uploadCachedOnStart（设置为 false 或未设置），且本地存储中存在缓存数据，则在初始化 EventsManager
时，这些数据将被自动清除。若要在应用程序重启后保留缓存的快照并在下次运行时将它们上传到 Hub，必须将 uploadCachedOnStart 显式设置为 true。否则，缓存数据将被删除。

### 上传速率管理与上传优先级

缓存的快照会与新添加的快照一起逐步上传，且新添加的快照在上传队列中享有优先权。DepthAI 不会尽可能快地上传缓存的快照，而是有意限制上传速率，以遵守 Luxonis Hub 强制执行的上传限制和配额配置。

这种方法确保：

 * 您的应用程序可以继续发送新的快照，而不会被缓存数据的积压所阻塞
 * 不超出 Hub 的速率限制（每小时文件上传次数、带宽限制）
 * 缓存的快照最终会被上传，而不会影响实时数据收集

随着应用程序不断生成新的快照，缓存的快照会在后台逐步上传，从而能够从网络故障中恢复，同时不会中断正常操作或违反 Hub 的约束条件。

## 快照何时以及如何上传到 Hub？

DepthAI 会成批上传和发送事件，这意味着多个 FileGroup 实例或快照会在一次请求中分组发送。这些批次会按固定间隔（默认每 30 秒）发送，这可能导致快照在 Hub 中出现之前有短暂的延迟。如需提高批次发送频率，请联系支持团队。
