ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

supervision CompactMask 深度解析:用边界框裁剪 RLE 编码实现内存高效的实例分割掩码存储

supervision CompactMask 深度解析:用边界框裁剪 RLE 编码实现内存高效的实例分割掩码存储 supervision CompactMask 深度解析用边界框裁剪 RLE 编码实现内存高效的实例分割掩码存储【免费下载链接】supervisionWe write your reusable computer vision tools. 项目地址: https://gitcode.com/GitHub_Trending/su/supervision本篇指南基于 supervision 的 CompactMask API 文档与配套源码讲解如何用边界框裁剪 行程长度编码Crop-RLE替代稠密(N, H, W)布尔掩码栈。读完后你将能够直接将 COCO RLE 载荷解析为 CompactMask、用 Roboflow Inference 结果免稠密解码地构建 Detections、让标注器按需跳过掩码物化并安全地混合合并稠密与紧凑两种掩码来源——这是处理 4K 航拍、卫星图、高密度人群场景下实例分割的关键内存优化手段。1. 问题背景稠密掩码栈的内存墙实例分割模型为每个检测对象返回一张布尔掩码supervision 默认把它们堆叠成(N, H, W)的 NumPy 数组。按 模块文档 的估算一张 4K3840×2160图像上检测到 1000 个对象时1000 x 3840 x 2160 x 1 byte ≈ 8.3 GB在这种规模下典型流水线会在标注出第一帧之前就因MemoryError崩溃航拍影像、卫星瓦片、高密度人群场景都会撞上这堵墙。CompactMask 的解决方案是把每个掩码只编码为其边界框裁剪区域crop的行程长度序列而不是全图画布dense (N,H,W) mask → N x crop_RLE N x (x1,y1) offset 8.3 GB → ~280 KB 见 examples/compact_mask/README.md由于Detections.xyxy中本来就携带边界框裁剪边界无需调用方提供额外元数据。内部只保存四样东西见 CompactMask 构造与slotsself._rles: list[npt.NDArray[np.int32]] # N 个 int32 行程长度数组 self._crop_shapes: npt.NDArray[np.int32] # (N, 2) — 每个掩码的 (crop_h, crop_w) self._offsets: npt.NDArray[np.int32] # (N, 2) — 每个掩码的 (x1, y1) 原点 self._image_shape: tuple[int, int] # 全图 (H, W)需要特别强调的是 模块头注释 给出的边界声明CompactMask 降低的是内存占用并不提升计算速度——从 RLE 解码到裁剪再拼接的摄取路径是 Python 级的通常比稠密 NumPy 路径慢。它的首要收益是大图像 大量稀疏掩码时的内存节省而标注、过滤、合并等操作的加速来自避免了整帧稠密数组的分配与拷贝详见第 7 节。2. 内部表示列主序F-orderRLE 与 COCO 兼容约定CompactMask 的 RLE 采用列主序Fortran-orderF-order像素扫描与 COCO API / pycocotools 的扫描顺序一致但编码范围scope是每个掩码的边界框裁剪而不是 pycocotools 使用的全图。这一区别直接决定了互操作方式不能把 CompactMask 的 RLE 直接传给maskUtils.iou()或maskUtils.decode()——必须先重新划定到全图画布与 pycocotools 互操作的标准做法是调用 to_dense 得到标准布尔数组后再编码类文档 明确说明从行主序C-order切换到列主序属于向后不兼容的格式变更任何持久化或 pickled 的旧版._rles在新约定下会解码错误迁移方式是用旧版本加载后to_dense()再重新编码。RLE 的底层编解码函数_mask_to_rle_counts、_rle_counts_to_mask、_base48_decode、_delta_decode位于 converters.py被 CompactMask 与公开转换助手共享。3. 构建路径一from_dense —— 从稠密布尔数组编码CompactMask.from_dense 从(N, H, W)布尔数组加(N, 4)的xyxy边界框构建紧凑掩码import numpy as np import supervision as sv from supervision.detection.compact_mask import CompactMask masks np.zeros((2, 100, 100), dtypebool) masks[0, 10:20, 10:20] True masks[1, 50:70, 50:80] True xyxy np.array([[10, 10, 19, 19], [50, 50, 79, 69]], dtypenp.float32) cm CompactMask.from_dense(masks, xyxy, image_shape(100, 100)) len(cm) # 2 cm.shape # (2, 100, 100)实现要点见 L687-L733边界框先被裁剪clip到图像边界且遵循 supervision 的xyxy含最大坐标约定因此切片时x2/y2需要1对排序非法x2 x1或y2 y1或越界的框退化为1x1全 False 裁剪避免产生退化的 RLEN 0时返回空结构不会报错。注意from_dense要求输入的(N, H, W)数组本身能放进内存——对于真正 OOM 规模的数据正确做法是按检测对象从模型输出裁剪直接构建而不是先堆稠密栈这一限制同样写在 examples/compact_mask/README.md 的 Limitations 一节。4. 构建路径二from_coco_rle —— 免稠密解码地摄取 COCO RLE如果模型或 API 直接返回 COCO RLE 掩码形如{size: [H, W], counts: ...}CompactMask.from_coco_rle 可以不分配任何(N, H, W)布尔数组就完成转换。核心代码来自 docs/how_to/use_compact_masks.mdimport numpy as np import supervision as sv from supervision.detection.compact_mask import CompactMask # 两个 720x1280 帧的 COCO RLE 掩码。 # counts 字符串请替换为你的模型或 API 返回的真实压缩 RLE 载荷 # 如 pycocotools mask.encode() 的输出或 Inference 响应。 rles [ {size: [720, 1280], counts: YOUR_RLE_COUNTS_STRING_HERE}, {size: [720, 1280], counts: YOUR_RLE_COUNTS_STRING_HERE}, ] xyxy np.array([ [100.0, 50.0, 400.0, 300.0], [500.0, 200.0, 900.0, 600.0], ]) compact CompactMask.from_coco_rle(rles, xyxy, image_shape(720, 1280)) detections sv.Detections( xyxyxyxy, maskcompact, class_idnp.array([0, 1]), )行为细节均有源码印证counts既接受 pycocotools 压缩字符串/字节串经_base48_decode_delta_decode还原也接受未压缩的整数行程列表L829 与 _coco_rle_counts_to_array强校验size必须为[height, width]且与image_shape一致行程总和必须等于H*Wimage_shape单边不得超过_MAX_IMAGE_DIMENSION 32768L508-L510防止恶意构造的载荷触发 O(H×W) 分配小图像素数 ≤ 128×128_SMALL_IMAGE_DENSE_THRESHOLDL511-L514走全向量化 NumPy 解码路径大图走纯 Python 的按列切分 行程裁剪路径_rle_split_cols 用np.cumsumnp.searchsorted以 O(log R) 直接跳到目标列避免宽图右缘裁剪时的 O(像素前缀) 遍历转换全程基于行程运算并按xyxy划定裁剪范围稠密像素数组永远不会被创建。5. 与 Detections 的集成from_inference 与 to_compact_masks5.1 Detections.from_inference(compact_masksTrue)Detections.from_inference 增加了compact_masks关键字参数默认False保持向后兼容为True时会把 Roboflow RLE 载荷经CompactMask.from_coco_rle路由而不是解码成稠密栈import supervision as sv # result: Roboflow Inference v2 响应 dict带实例掩码 detections sv.Detections.from_inference(result, compact_masksTrue) from supervision.detection.compact_mask import CompactMask assert isinstance(detections.mask, CompactMask)裁剪策略是这里最重要的行为契约见 from_inference 文档掩码来源裁剪策略是否有损原生尺寸匹配的 COCO-RLERLEsize等于图像尺寸裁剪到检测框xyxy框外像素被静默丢弃是由points多边形派生的掩码保留全帧否尺寸不匹配的 COCO-RLE先解码再缩放到图像保留全帧否源码注释L779-L783还记录了一个设计注记compact_masks标志改变了detections.mask的运行时类型因此每个掩码消费方都要按isinstance(mask, CompactMask)分支处理。5.2 Detections.to_compact_masks()任意时刻可以把已有的稠密掩码 Detections 转为紧凑表示实现detections_compact detections.to_compact_masks()两个注意点若mask已经是CompactMask或为None原样返回self文档明确说明此路径的裁剪边界是全图尺寸而非检测框因此 RLE 稀疏性仍然能压缩存储但不享受from_inference(..., compact_masksTrue)那种O(bbox_area)级别的裁剪收益。想要收紧裁剪可在结果上调用 CompactMask.repack。6. 掩码按需物化crop、索引协议与标注器分支6.1 鸭子类型接口像 ndarray 一样用但不完全等于 ndarrayCompactMask 暴露了一组与稠密掩码习惯对齐的接口类文档表达式返回mask[i]整数稠密(H, W)bool 数组供标注器、迭代器使用mask[slice / list / ndarray]新的CompactMask过滤/切片np.asarray(mask)稠密(N, H, W)bool 数组NumPy 互操作mask.shape/mask.dtype/mask.area与稠密 API 一致关键实现getitem整数索引时只解码该掩码的 crop 再粘回一张空白全图result[y1:y1crop_h, x1:x1crop_w] crop切片/布尔/花式索引先把选择子换算成整数位置再对新CompactMask做浅层重组——不拷贝任何像素crop(index)O(crop_area) 地解码单个掩码的裁剪区域只给标注器它真正需要的那块area直接对每个 RLE 的奇数下标行程求和_rle_areaint(np.sum(rle[1::2]))完全不触碰像素网格sum(axis(1, 2)) 命中 area 快速路径其他 axis 回退到to_dense().sum(...)to_dense() 是唯一的显式物化边界当你需要调用任意 ndarray 方法astype、reshape、ravel、any、all…时先cm.to_dense()再操作不要指望直接调用。6.2 用 requires_mask 跳过不必要的物化不绘制掩码的标注器box、label、circle、ellipse、trace、keypoint 等声明requires_mask False默认值定义在 BaseAnnotatorMaskAnnotator、PolygonAnnotator、HaloAnnotator在 annotators/core.py 中显式置为True。集成侧可以按此标志分支避免在标注前解码任何掩码import supervision as sv annotators [ sv.BoxAnnotator(), sv.LabelAnnotator(), sv.MaskAnnotator(), # requires_mask True ] for ann in annotators: if ann.requires_mask: # 标注器读掩码像素 — CompactMask 只按 crop 惰性解码 scene ann.annotate(scene, detections) else: # 标注器不碰掩码 — 去掉 mask 字段消除一切解码开销 det_no_mask sv.Detections( xyxydetections.xyxy, confidencedetections.confidence, class_iddetections.class_id, ) scene ann.annotate(scene, det_no_mask)其中PolygonAnnotator与MaskAnnotator都能直接操作CompactMask而无需物化整帧——把紧凑掩码的 Detections 传给它们本来就是高效的MaskAnnotator通过offsetscrop()只绘制裁剪区域见 examples/compact_mask/README.md 的 annotate 分析。7. 合并混合掩码Detections.merge 的类型协商规则当合并同时含稠密ndarray掩码与CompactMask的 Detections 时Detections.merge 会自动把稠密输入转为CompactMask且不会分配整块(N, H, W)栈import numpy as np import supervision as sv from supervision.detection.compact_mask import CompactMask H, W 720, 1280 # 来自 RLE 源的紧凑检测counts 请替换为真实载荷 rles [{size: [H, W], counts: YOUR_RLE_COUNTS_STRING_HERE}] xyxy_a np.array([[100.0, 50.0, 400.0, 300.0]]) cm CompactMask.from_coco_rle(rles, xyxy_a, image_shape(H, W)) det_a sv.Detections(xyxyxyxy_a, maskcm, class_idnp.array([0])) # 来自另一来源的稠密检测 masks_b np.zeros((1, H, W), dtypebool) masks_b[0, 200:400, 500:800] True xyxy_b np.array([[500.0, 200.0, 799.0, 399.0]]) det_b sv.Detections(xyxyxyxy_b, maskmasks_b, class_idnp.array([1])) # 输出无论输入顺序如何都是 CompactMask merged sv.Detections.merge([det_a, det_b]) assert isinstance(merged.mask, CompactMask) assert len(merged) 2合并规则merge 文档输入输出掩码类型全部CompactMaskCompactMaskCompactMask 稠密ndarray混合CompactMask稠密侧经from_dense转换全部稠密ndarrayndarray向后兼容约束与警告所有CompactMask输入必须共享同一image_shape不一致抛ValueError底层 CompactMask.merge 同样校验且要求列表非空有损转换警告from_dense会把每张稠密掩码裁剪到其检测框框外 True 像素被静默丢弃——这与from_inference(compact_masksTrue)的行为一致。需要逐像素保真时请确保所有输入已是CompactMask或走全稠密路径with_nms/with_nmm使用的成对合并路径不保留CompactMask混合输入会物化为稠密ndarray。8. 面向 Slicer 的几何操作with_offset 与 resize大图像分块推理InferenceSlicer瓦片拼接是 CompactMask 最重要的落地场景之一类内为此提供了两个几何操作with_offset(dx, dy, new_image_shape)—— 把瓦片局部坐标整体平移到全图坐标。它先向量化地算出所有新边界框并做一次边界检查当没有任何掩码越界时InferenceSlicer的常见情况只更新(N, 2)的偏移量数组RLE 数据完全不动越界的掩码才单独解码、切片、重编码完全落在画布外的掩码退化为1x1全 False 桩不触发解码。resize(new_image_shape)—— 把整个掩码集缩放到新分辨率采用最近邻插值与cv2.INTER_NEAREST位级一致。内部按密度分流_resize_crop全 False 快速路径直接返回单个 False 行程不解码稀疏路径行程密度 _L3_DENSITY_THRESHOLD 0.25L502-L505直接在行程长度上做算术_rle_split_cols→_rle_scale_col→_rle_join_cols全程不分配二维数组稠密路径解码为uint8调用cv2.resize(INTER_NEAREST)再重新编码。此外掩码数量N 8_PARALLEL_THRESHOLD时resize会切换到线程池并行执行各 crop 的缩放——NumPy 与 OpenCV 会释放 GIL多核 CPU 上可获得真实并行收益坐标算术本身也是全向量化的没有对 N 的 Python 循环。9. repack给松散裁剪重新收紧检测器输出的边界框常常是松的带 padding 甚至整图尺寸。此时每个 RLE crop 会编码多余的背景像素。repack() 会把每个 crop 解码出来、裁剪到包含全部 True 像素的最小矩形、再重新编码全 False 掩码被规范为1x1。复杂度是 O(裁剪面积之和)适合作为多次 merge 之后的一次性清理例如 InferenceSlicer 的瓦片结果合并完成后 masks np.zeros((1, 10, 10), dtypebool) masks[0, 3:7, 3:7] True xyxy np.array([[0, 0, 9, 9]], dtypenp.float32) # 故意给一个松散的全图框 cm CompactMask.from_dense(masks, xyxy, image_shape(10, 10)) repacked cm.repack() repacked.offsets.tolist() # 紧裁剪原点x13, y13 [[3, 3]]10. 性能画像省内存是保证提速有条件examples/compact_mask/目录提供了可在任意机器复现的基准脚本无需 GPU 与真实模型uv run python examples/compact_mask/benchmark.pybenchmark.py 覆盖 FHD1920×1080、4K3840×2160、卫星级8192×8192三档分辨率 × 填充率5/20/50%× 多边形顶点数8/128/600的组合矩阵稠密 IoU/NMS 中间数组超过 1 GB 的场景会被自动跳过以防交换颠簸bench_inference_api.py 则针对 Roboflow Inference 解析器做聚焦基准下载 supervision 资产、跑真实分割推理、请求原生 RLE 掩码并对比sv.Detections.from_inference(result)与sv.Detections.from_inference(result, compact_masksTrue)的解析时间与峰值分配需要ROBOFLOW_API_KEY时请先设置可用--asset people-walking之类的参数指定单个资产。官方使用指南 给出的解析/标注阶段提速估算注意这些是解析与标注阶段的收益不是端到端 FPS——模型推理通常主导总耗时优化点现实收益适用条件from_coco_rle摄取解析快 25–60%全帧 COCO RLE 载荷对照现有稠密解码路径MaskAnnotatorROI 绘制标注快 10–35%高分帧上大量小而稀疏的掩码PolygonAnnotatorcrop 路径多边形绘制快 15–45%大量紧凑掩码整帧物化曾是瓶颈混合掩码合并合并快 5–20%紧凑与稠密来源混合如多相机拼接上界收益的假设是≥1080p 帧、数十到数百个实例、掩码总占比低于约 20%。而 examples README 也诚实地给出了反面结论Speedup column 一节Compact 更快仅当稠密(N, H, W)布尔栈的分配开销占主导——大图像、多而稀疏的掩码小图像或稠密/重叠掩码下Python 级 RLE 算术占主导compact 反而更慢基准内置的synthetic-dense-64行就是这种对抗场景内存是无论哪个方向都稳赚的收益典型分割输出下 compact 掩码比稠密栈少约 99% 内存。11. 局限性与迁移注意事项结合 类文档 与 examples README 的 Limitations 一节使用时应牢记不是 ndarray 的完整替身需要任意 ndarray 方法时先to_dense()F-order、crop-scoped 的 RLE 不能直传 pycocotools互操作前先用to_dense()物化全图掩码再重新编码from_dense要求稠密数组先在内存中OOM 规模数据应逐检测对象从模型输出直接构建旧版持久化数据不兼容C-order → F-order 是格式破坏性变更旧 pickled._rles需旧版本解码后重编码尺寸守卫from_coco_rle单边上限 32768 像素超出抛ValueError。典型适用场景摘自 examples README航拍/卫星影像、高密度人群或细胞分割、4K 实时标注流水线、跨帧累积的长程跟踪以及InferenceSlicer瓦片拼接。12. 相关路径索引内容路径CompactMask 核心实现src/supervision/detection/compact_mask.pyDetections.from_inference / to_compact_masks / mergesrc/supervision/detection/core.pyRLE 编解码底层函数src/supervision/detection/utils/converters.py标注器 requires_mask 声明src/supervision/annotators/base.py、src/supervision/annotators/core.py使用指南四大集成点docs/how_to/use_compact_masks.md基准脚本与分析examples/compact_mask/README.md、examples/compact_mask/benchmark.py、examples/compact_mask/bench_inference_api.py单元测试tests/detection/test_compact_mask.py、tests/detection/test_compact_mask_integration.py、tests/detection/test_compact_mask_iou.py【免费下载链接】supervisionWe write your reusable computer vision tools. 项目地址: https://gitcode.com/GitHub_Trending/su/supervision创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表