# 后处理

`POST /v1/segment`、`POST /v1/refine` 和批量分割在构建响应之前，会在服务端对每个实例做后处理。后处理免费且结果确定：同一请求在同一图片上得到同样的结果。默认情况下，每个实例带有含洞的简化多边形，坐标为摆正后的原图的像素，不带蒙版。

## 选项放在哪里

| 选项 | 层级 | 含义 |
|---|---|---|
| `geometry` | defaults、提示 | `["polygon"]`（默认）、`["mask"]`、两者都要，或 `[]`（只要框和分数） |
| `polygon` | defaults、提示 | `tolerance`（默认图片对角线的 0.001）或 `tolerancePx`，`holes`（默认 `true`），`maxParts`（1 至 64） |
| `mask` | defaults、提示 | `encoding`：`coco`（默认）或 `webp` |
| `filter` | defaults、提示 | `minScore`（默认 0.5）、`minSize`、`minArea`；仅用于搜索提示 |
| `cleanup` | defaults、提示 | `dropFragments` 和 `fillHoles`，默认均为图片面积的 0.0001 |
| `merge` | defaults、提示 | `true` 把同一 `group` 的实例合并为一个 |
| `group` | 提示 | 把多个提示关联起来的名称，用于 `nms` 和 `merge` |
| `maxInstances` | 请求 | 返回的搜索实例数，1 至 300，默认 100 |
| `nms` | 请求 | 重叠去除：`iou`（0.7）、`metric`（`mask` 或 `box`）、`scope`（`group` 或 `all`） |
| `maskSize` | 请求 | 蒙版网格的长边，64 至 4096，默认 2048 |
| `report` | 请求 | `droppedBoxes` 返回 `dropped`；`lowScoreFloor` 限定低分的计数和列出范围 |

`options.defaults` 作用于所有提示，`prompts[].options` 为单个提示覆盖它。每个字段依次取内置默认值、`options.defaults`、提示自己的 `options`。对象（`polygon`、`mask`、`filter`、`cleanup`）按字段合并，因此设置 `polygon.tolerance` 不影响 `polygon.holes`。数组（`geometry`）和阈值对象（`minSize`、`minArea`、`dropFragments`、`fillHoles`）整体替换，`null` 关闭阈值。`tolerance` 和 `tolerancePx` 是同一项设置：在同一对象中同时给出返回 `400 invalid_request`，更具体的一层优先。

提示分为搜索提示（`sam3` 文本提示，或没有文本提示时的样例框）和物体提示（`sam3` 点，`sam-lite` 框和点）。`filter` 和 `nms` 只作用于搜索提示，在物体提示上设置 `filter` 返回 `400 invalid_request`。物体提示的实例总是返回，不计入 `maxInstances`。与文本提示一起使用的样例框不带 `group` 和 `options`。

**合并。** 提示的 `merge` 为 `true` 的实例按 `group` 合并为一个实例：蒙版取并集，`score` 取最高值，`prompt` 取最小值（其 `label` 和输出选项生效），`bbox` 和 `area` 为并集的值，`merged` 为合并的实例数。组内只有一个此类实例时，原样返回。

## 处理顺序

每一步只处理上一步保留下来的内容。括号中是该步骤丢弃的内容在 `dropStats` 中对应的计数器。

1. 分数：低于 `filter.minScore` 的搜索候选被丢弃（`lowScore`）。每个请求最多 300 个候选按分数顺序继续（`limit`）。
2. 清理每个实例的碎片和小洞（`cleanup`）。
3. 尺寸：先 `filter.minSize`（`tooSmall`），再 `filter.minArea`（`tooSmallArea`），均针对清理后的实例。
4. 重叠：`nms`（`overlap`）。
5. 在每个 `group` 内合并。
6. 数量：保留分数顺序中前 `maxInstances` 个搜索实例（`limit`）。
7. 几何输出，然后应用下文的响应限制。

每一步和 `instances` 使用的分数顺序为：`score` 降序，然后 `prompt` 升序，然后模型候选的顺序。

## 尺寸过滤与贴边模式

`minSize` 针对清理后实例的紧致框：`ratio`（图片短边的比例）和 `px` 二选一。框的某一边短于阈值 `s` 即为过小。`minArea` 是 `area` 的阈值：`ratio`（图片面积的比例）和 `px` 二选一。

被图片边界截断的物体看起来会比实际小。框的边缘与图片边界的距离在 `edgeTolerancePx`（默认 1）以内时视为贴边，`border` 决定如何判断这样的框。`w` 和 `h` 为框的宽和高，表中条件成立即为过小。

| `border` | 不贴边 | 仅水平贴边 | 仅垂直贴边 | 两者都贴 |
|---|---|---|---|---|
| `strict`（默认） | `min(w, h) < s` | `min(w, h) < s` | `min(w, h) < s` | `min(w, h) < s` |
| `directional` | `min(w, h) < s` | `h < s` | `w < s` | 从不 |
| `cornerLongSide` | `min(w, h) < s` | `h < s` | `w < s` | `max(w, h) < s` |
| `cornerShortSide` | `min(w, h) < s` | `h < s` | `w < s` | `min(w, h) < s` |

## 限制与降级标记

取决于图片内容的限制无法在调用运行前检查。它们按固定规则缩减输出，不会使调用失败，每次缩减都能在响应中看到。`bbox`、`score` 和 `area` 始终保留。

| 限制 | 数值 | 处理方式 | 标记 |
|---|---|---|---|
| 每个请求的提示数 | 50 | `400 invalid_request` | 无 |
| 每个请求的候选数 | 300 | 其余按分数顺序丢弃 | `dropStats.limit` |
| `maxInstances` | 1 至 300，默认 100 | 超出的搜索实例被丢弃 | `dropStats.limit` |
| 每个实例的顶点数 | 8,192 | 容差翻倍，最多 6 次，起点为请求值与 1 px 中的较大者；之后保留最大的若干部分；最大的部分单独超限时，实例没有 `polygon` | 实例上的 `tolerancePx`，或 `omitted: vertices` |
| 每个响应的几何数据量 | 4 MiB | 每个顶点计 4 字节，加上 `counts` 的长度和 WebP 的大小。从分数顺序中第一个放不下的实例起，实例不带几何数据 | `omitted: budget`、`dropStats.budget` |
| 每个响应的 WebP 像素 | 32 百万像素 | 从第一个放不下的裁剪区域起，蒙版改为 `coco` 返回 | `mask.encoding` |
| `dropped` 条目 | 300 | 分数高的在前 | 无 |

## 示例

每个提示使用不同的 `minScore`，并对其中一个加尺寸过滤：

```json
{
  "model": "sam3",
  "image": { "id": "img_..." },
  "prompts": [
    { "text": "car", "options": { "filter": { "minScore": 0.7 } } },
    { "text": "person", "options": { "filter": { "minScore": 0.4, "minSize": { "px": 24 } } } }
  ],
  "options": { "maxInstances": 50 }
}
```

跨提示使用 `group`、`nms` 和 `merge`。`sofa` 与 `couch` 用两个名称找同一类物体，因此放在同一组，`nms` 丢弃每对重复实例中分数较低的一个。`window` 与 `door` 合并为一个 `openings` 实例，`merged` 为合并的实例数，`prompt` 为 2：

```json
{
  "prompts": [
    { "text": "sofa", "group": "seat" },
    { "text": "couch", "group": "seat" },
    { "text": "window", "group": "openings", "options": { "merge": true } },
    { "text": "door", "group": "openings", "options": { "merge": true } }
  ],
  "options": { "nms": { "iou": 0.7 } }
}
```

为所有提示返回 COCO 蒙版，用 pycocotools 解码：

```python
# body["options"] = {"defaults": {"geometry": ["mask"]}}
from pycocotools import mask as coco

m = instance["mask"]  # {"encoding": "coco", "size": [h, w], "counts": "..."}
pixels = coco.decode({"size": m["size"], "counts": m["counts"].encode()})  # h x w uint8，位于蒙版网格上
```

## 多边形、COCO 还是 WebP

| 输出 | 无损 | 实测大小 | 适用 |
|---|---|---|---|
| `polygon`（默认） | 否：简化后的轮廓 | 平均每实例 574 B（71 个顶点）；小物体 117 B，中等 317 B，大物体 1,087 B | 显示、矢量编辑、几何运算 |
| `mask`，`coco` | 是，在蒙版网格上 | 约每实例 4.3 KB（100 个为 428 KB） | 精确像素、pycocotools 流程 |
| `mask`，`webp` | 是，在蒙版网格上 | 约每实例 3.4 KB（100 个为 337 KB，含 base64） | 发丝等极细致的轮廓，此时比 `coco` 小；生成较慢 |

多边形的大小为真实 SAM 3 蒙版在默认容差下的扁平整数 JSON。COCO 和 WebP 的大小为 100 个混合尺寸的合成蒙版在整图网格上的 JSON；绝对数值取决于轮廓，但比例成立。多边形字节随细节增加：容差为对角线的 0.0015 时每实例 356 B，0.0005 时 1,327 B。

默认容差为对角线的 0.001，是多边形相对精确蒙版仍满足质量门槛的最大比例，对象为中等和大物体：IoU 中位数不低于 0.98，第 5 百分位不低于 0.95，在 640、1280 和 2560 px 下均成立（640 px 下中位数为 0.988）。0.0015 两项都未通过（中位数 0.9785，第 5 百分位 0.9404）。默认值可能调整，因此对结果敏感时应显式设置 `tolerance` 或 `tolerancePx`。

`mask` 位于长边不超过 `options.maskSize` 的网格上；`mask.size` 为网格的 `[高, 宽]`。水平方向乘以 `image.width / size[1]`，垂直方向乘以 `image.height / size[0]`。详见[图片](https://stack.foldbun.com/zh/docs/images/#蒙版)。

## 二进制响应

`Accept: application/cbor` 以 CBOR（RFC 8949）代替 JSON 返回同一结果，结构和键相同。适用于 `POST /v1/segment`、`POST /v1/refine`、`POST /v1/remove-background` 和 JSON 模式的 `POST /v1/batch/{endpoint}`。多边形环（`outer` 和 `holes` 的每一项）为 RFC 8746 类型化数组，标签 69：uint16、小端、x 和 y 交错、图片像素的绝对值。WebP `data` 等二进制数据为字节串，不用 base64。

```bash
curl https://api.stack.foldbun.com/v1/segment \
  -H "Authorization: Bearer $FOLDBUN_API_KEY" \
  -H "Accept: application/cbor" \
  -H "Content-Type: application/json" \
  -d @request.json --output result.cbor
```

适用于较大的结果，例如实例很多或多边形很细致。环的每个坐标占 2 字节：100 个混合尺寸多边形实测为 64 KB，JSON 为 139 KB（小 54%）；WebP 蒙版去掉 base64 后小 26%；COCO 字符串只小约 2%。客户端的解码也更快。JSON 仍是默认：其他 `Accept` 值都返回 JSON，请求、错误和事件流始终为 JSON。

全部字段：[API 参考](https://stack.foldbun.com/zh/docs/api/operations/segment/)。各模型的行为：[`sam3`](https://stack.foldbun.com/zh/docs/models/sam3/)、[`sam-lite`](https://stack.foldbun.com/zh/docs/models/sam-lite/)。
