☰
detectron2 数据结构全解析:Boxes、Instances、ImageList 与掩码体系的源码级指南
2026/10/14 18:45:54 网站建设 项目流程
  • 人工智能
  • 计算机视觉
  • 深度学习
  • 预训练
  • 微调

【免费下载链接】Vim

[ICML 2024] Vision Mamba: Efficient Visual Representation Learning with Bidirectional State Space Model

项目地址:https://gitcode.com/gh_mirrors/vim2/Vim
点击查看免费下载

在 detectron2(本项目det/目录集成的版本,作为 Vision Mamba / VimDet 检测任务的运行时底座)中,模型前后端之间的数据交换并非依赖零散的裸张量,而是统一由detectron2.structures模块中的一组"结构类"承担。从边界框坐标系的归一化、批量图像的 padding 打包,到一张图像内全部实例的字段管理、关键点热图编解码,乃至多边形/位图/ROI 三种掩码表示的互转,本文将以 structures.rst 所展开的 API 文档为主体,结合仓库源码逐类拆解其设计意图、核心方法与真实调用链。读完本文,你将能熟练使用BoxMode、Boxes、Instances、ImageList、Keypoints、BitMasks/PolygonMasks/ROIMasks、RotatedBoxes完成数据标注解析、训练目标构造与推理结果组织,并理解它们如何贯穿 detectron2 的 DataLoader 与 MetaArch 全流程。

一、模块总览:一张"数据中间件"的全景图

detectron2.structures在文档体系中的地位由 modules/structures.rst 中的automodule指令界定——它不是手写文档,而是直接从源码 docstring 生成的权威 API 参考。从init.py 的导出清单可以看到模块的全部公开成员:

from .boxes import Boxes, BoxMode, pairwise_iou, pairwise_ioa, pairwise_point_box_distance from .image_list import ImageList from .instances import Instances from .keypoints import Keypoints, heatmaps_to_keypoints from .masks import BitMasks, PolygonMasks, polygons_to_bitmask, ROIMasks from .rotated_boxes import RotatedBoxes from .rotated_boxes import pairwise_iou as pairwise_iou_rotated

可以将其划分为四类职责:

类别成员核心职责
框坐标系与转换BoxMode(枚举 +convert)统一 4 种常见 bbox 表示,负责格式互转
框容器与几何计算Boxes、RotatedBoxes、pairwise_iou等函数存储 N 个框并实现面积、裁剪、IoU 等运算
图像级/实例级容器ImageList、Instances批量图像打包、单图多实例字段管理
掩码与关键点BitMasks、PolygonMasks、ROIMasks、Keypoints分割标注与关键点标注的表示与转换

这些结构类共同遵守一个设计契约:行为向torch.Tensor看齐,同时保留领域语义——支持索引、to(device)、.device、len(),部分支持cat批量拼接,从而让模型代码可以用统一接口操作不同形态的数据。

二、BoxMode:边界框坐标系的枚举与转换

2.1 五种模式的定义

BoxMode定义于 boxes.py#L12-L41,是一个IntEnum,五种取值覆盖了检测领域常见的 bbox 表示:

枚举值数值含义
XYXY_ABS0(x0, y0, x1, y1)绝对浮点坐标,范围在[0, width/height]内
XYWH_ABS1(x0, y0, w, h)绝对浮点坐标
XYXY_REL2(x0, y0, x1, y1)相对图像尺寸的[0, 1]归一化坐标(尚未支持)
XYWH_REL3(x0, y0, w, h)相对图像尺寸的[0, 1]归一化坐标(尚未支持)
XYWHA_ABS4(xc, yc, w, h, a)旋转框格式,(xc, yc)为中心,a为逆时针角度(度)

值得注意:XYXY_REL与XYWH_REL虽被定义,但源码明确标注 "Not yet supported!",convert方法对这两种模式会直接断言拒绝(boxes.py#L72-L75)。因此实际可用的输入/输出模式只有三种:XYXY_ABS、XYWH_ABS、XYWHA_ABS。

2.2 convert 静态方法:一次调用完成坐标系归一化

BoxMode.convert(boxes.py#L43-L127)是整个数据管线中最常用的入口之一。它接受box(k 元组、k 列表或 Nxk 数组/张量,k=4 或 5)、from_mode与to_mode,返回与输入同类型的转换结果。其内部逻辑分三路:

  • XYWHA_ABS → XYXY_ABS:旋转框转水平包围矩形。源码先计算水平外接矩形的宽高new_w = |cos(a)|·w + |sin(a)|·h、new_h = |cos(a)|·h + |sin(a)|·w,再由中心坐标推导左上角与右下角(boxes.py#L77-L100)。这一转换将旋转框"退化"为其轴对齐包围盒,用于 RRPN 等需要水平框的环节。
  • XYWH_ABS → XYWHA_ABS:把(x0, y0, w, h)的左上角换算为中心(x0 + w/2, y0 + h/2),角度补 0,拼成 5 列(boxes.py#L101-L107)。
  • XYWH_ABS ↔ XYXY_ABS:分别执行x2 += x0/x2 -= x0等四则运算完成互转(boxes.py#L108-L120)。

实现细节上,函数会保留输入的 Python 类型(list/tuple 则返回同类型扁平列表)、numpy 类型与 tensor 类型,且对 numpy 输入先 clone 避免原地修改调用方数据。

2.3 在真实数据管线中的调用

BoxMode.convert是数据集加载的"翻译官"。以 COCO 为例,原始标注以XYWH_ABS存储(datasets/coco.py#L205),而模型内部统一使用XYXY_ABS。在 detection_utils.py#L398-L404 中,annotations_to_instances会对一张图的所有标注批量执行:

boxes = ( np.stack( [BoxMode.convert(obj["bbox"], obj["bbox_mode"], BoxMode.XYXY_ABS) for obj in annos] ) if len(annos) else np.zeros((0, 4)) )

类似地,LVIS 数据集同样以XYWH_ABS存储(datasets/lvis.py#L144),而 Pascal VOC 直接以XYXY_ABS提供(datasets/pascal_voc.py#L71)。正是BoxMode的枚举约定让这些异构来源的数据能统一进入同一套模型。

三、Boxes 与 RotatedBoxes:检测框容器

3.1 Boxes:Nx4 水平框容器

Boxes(boxes.py#L130-L309)以Nx4的 float32 张量存储一组水平框,每行(x1, y1, x2, y2)。构造时非 tensor 输入会被torch.as_tensor转成 float32 CPU 张量,空输入会 reshape 为(-1, 4)以保持形状约定(这一细节在 JIT 场景下尤为重要)。

其核心方法包括:

  • area():(x2 - x1) * (y3 - y1)返回每个框的面积向量;
  • clip(box_size):原地将坐标限制到[0, width] × [0, height],并先断言张量无 NaN/Inf(boxes.py#L183-L197);
  • nonempty(threshold=0.0):返回布尔向量,框的任一边长不超过 threshold 即视为空;
  • inside_box(box_size, boundary_threshold=0):判断框是否在参考框内,允许boundary_threshold的越界容忍;
  • get_centers():返回 Nx2 的中心坐标;
  • scale(scale_x, scale_y):按横纵因子缩放(tensor[:, 0::2] *= scale_x,tensor[:, 1::2] *= scale_y);
  • cat(boxes_list):类方法,将多个Boxes拼接为一个,空列表返回空Boxes;
  • to(device)/.device/clone():设备迁移与拷贝。

Boxes还实现了完整的 Tensor 风格接口:__getitem__支持整数(返回单框)、切片与 BoolTensor 布尔掩码索引,__len__返回框数,__iter__逐框 yield 形状(4,)的张量。

3.2 pairwise 系列:IoU 等成对几何运算

模块提供四个与Boxes配套的成对函数:

  • pairwise_intersection(boxes.py#L312-L331):N 框与 M 框两两求交集面积,输出[N, M];
  • pairwise_iou(boxes.py#L336-L358):inter / (area1[:, None] + area2 - inter),并处理空框(inter > 0时计算,否则置 0);
  • pairwise_ioa(boxes.py#L361-L378):交集面积归一化到boxes2面积,用于匹配等需要"重叠占比"的场景;
  • pairwise_point_box_distance(boxes.py#L381-L397):N 个点到 M 个框四条边的距离,输出(N, M, 4),点在框内时距离全为正;
  • matched_pairwise_iou(boxes.py#L400-L425):仅计算两条长度相等的框列表的对角线 IoU,用于逐对匹配场景(如 proposal 与 gt 一一对应时)。

这些函数在 tests/structures/test_boxes.py 中有test_pairwise_iou、test_pairwise_ioa、test_box_convert_*、test_empty_cat、test_scriptability等大量单元测试覆盖,是框运算正确性的第一道防线。

3.3 RotatedBoxes:Nx5 旋转框容器

RotatedBoxes(rotated_boxes.py#L11-L487)继承Boxes,但存储(x_center, y_center, width, height, angle),其中angle为度,推荐主值范围[-180, 180)。其 docstring 对角度约定做了非常详尽的几何说明(rotated_boxes.py#L20-L212):

  • angle == 0时退化为水平框;
  • angle > 0表示绕中心**逆时针(CCW)**旋转|angle|度;
  • angle < 0表示绕中心**顺时针(CW)**旋转|angle|度;
  • 旋转后(X, Y, W, H, A)与(X, Y, W, H, A + 360N)是同一形状,因此(5,3,4,2,270)≡(5,3,4,2,-90);但(5,3,4,2,90)与(5,3,4,2,-90)覆盖相同区域、IoU 为 1,RoI Pooling 结果却不同,不能视为同一框。

RotatedBoxes覆盖并扩展了Boxes的方法:

  • area():w * h,不受角度影响;
  • normalize_angles():将角度规约到[-180, 180)((angle + 180) % 360 - 180);
  • clip(box_size, clip_angle_threshold=1.0):只裁剪近似水平的框——仅对|normalized(angle)| <= clip_angle_threshold的框做水平裁剪,其余交由RoIAlignRotated等算子安全处理(rotated_boxes.py#L255-L303),这是 RRPN 向后兼容的关键设计;
  • scale(scale_x, scale_y):非等比缩放时旋转框不再保持矩形,源码通过拟合旋转矩形到平行四边形来近似,w、h、angle均按解析公式更新(rotated_boxes.py#L393-L456);
  • inside_box:用旋转框的水平外接矩形判断是否在参考框内(rotated_boxes.py#L349-L384)。

模块级pairwise_iou(被__init__.py重导出为pairwise_iou_rotated)直接委托给 CUDA 层算子detectron2.layers.rotated_boxes.pairwise_iou_rotated(rotated_boxes.py#L490-L506),其底层实现在 layers/csrc/box_iou_rotated 目录中。

四、Instances:一张图像内全部实例的字段容器

4.1 字段(field)模型

Instances(instances.py#L8-L194) 是 detectron2 中最核心的容器:它代表一张图像中的所有实例,以"字段"(field)方式存储每个实例的属性(框、掩码、类别、分数等)。设计契约有三条:

  1. 所有字段必须有相同的__len__,即实例数;
  2. 以下划线_开头的属性是私有元数据(如_image_size、_fields),不可由用户直接修改;
  3. 字段属性可以通过点号直接读写——__setattr__对非下划线属性自动转调set,__getattr__对缺失字段抛出AttributeError。

docstring 给出的典型用法:

instances.gt_boxes = Boxes(...) print(instances.pred_masks) # 形状为 (N, H, W) 的张量 print('gt_masks' in instances) # 字段存在性检查 category_3_detections = instances[instances.pred_classes == 3] # 布尔掩码索引 confident_detections = instances[instances.scores > 0.9]

4.2 字段操作方法

  • set(name, value):写入字段,并校验新字段长度与现有字段一致,不一致直接断言失败(instances.py#L69-L81);
  • has(name)/get(name)/remove(name):查询、读取、删除字段;
  • get_fields():返回内部_fields字典,修改该 dict 会直接修改实例;
  • image_size属性:返回(height, width)元组,是所有实例共享的图像空间元数据。

4.3 Tensor-like 行为

  • to(*args, **kwargs):对每个字段调用其自身的to方法(若存在),返回新Instances,实现整批实例的设备迁移(instances.py#L112-L122);
  • __getitem__(item):支持 int、slice、BoolTensor/索引向量,对所有字段统一执行索引;整数索引会转换为等价 slice 以规避越界问题(instances.py#L124-L142);
  • cat(instance_lists):静态方法,按字段类型分别用torch.cat(张量)、itertools.chain(列表)或type(v0).cat(结构类)拼接,要求所有实例的image_size一致(instances.py#L153-L184);
  • __len__:取第一个字段的__len__,空Instances不支持下 len;
  • __iter__:明确抛NotImplementedError,Instances不可迭代,防止误用。

4.4 数据管线中的构造与消费

Instances的两端正是 DataLoader 与 MetaArch。在 detection_utils.py#L382-L454 中,annotations_to_instances将标注字典列表转换为模型可用的Instances:

target = Instances(image_size) target.gt_boxes = Boxes(boxes) # gt_boxes 字段 target.gt_classes = torch.tensor(classes, dtype=torch.int64) if ... "segmentation" in annos[0]: target.gt_masks = masks # PolygonMasks 或 BitMasks if ... "keypoints" in annos[0]: target.gt_keypoints = Keypoints(kpts) # gt_keypoints 字段

旋转框专用版本annotations_to_instances_rotated(detection_utils.py#L457-L483)则用RotatedBoxes承接并直接clip(image_size)。而filter_empty_instances(detection_utils.py#L486-L519)会调用instances.gt_boxes.nonempty(threshold=...)与instances.gt_masks.nonempty()过滤空实例——这正是Boxes.nonempty与BitMasks.nonempty的典型消费点。

在推理端,RPN 与检测头同样通过Instances(image_size)组织输出(如 proposal_generator/proposal_utils.py#L131、rrpn.py#L123),pred_boxes、scores、pred_classes、pred_masks等字段在此汇聚成最终结果,再被Visualizer等消费。

五、ImageList:变长图像的批量打包

5.1 设计动机与结构

ImageList(image_list.py#L11-L129)解决的是"一 batch 内图像尺寸不一"的问题:通过 padding 把多张图对齐到同一尺寸,打包成单一张量,同时保留每张图的原始尺寸image_sizes(list of(h, w)),供后续按原尺寸取回。其__getitem__支持按原始尺寸切回单张图:self.tensor[idx, ..., :size[0], :size[1]]。

5.2 from_tensors:核心工厂方法

ImageList.from_tensors(image_list.py#L58-L128)是批量打包的入口,参数语义如下:

参数默认值说明
tensors必填图像张量列表,每张形状(Hi, Wi)或(C_1,...,C_K, Hi, Wi),且非空间维度必须一致
size_divisibility0大于 0 时,保证 padding 后的公共高宽能被该值整除;依赖该特性的模型通常需要 32 的整除性(对应 FPN 的 5 级 stride)
pad_value0.0padding 填充值,通常为 0(图像归一化后均值填充值)或 128
padding_constraintsNone字典,形如{"size_divisibility": int, "square_size": int};square_size > 0时强制 padding 到square_size × square_size正方形,且其中的size_divisibility会覆盖同名参数

算法流程(image_list.py#L87-L128):取所有图的最大高宽 → 应用 square/divisibility 约束向上取整 → 单图时用F.pad优化路径,多图时用new_full预分配批张量再逐张copy_写入 → 返回ImageList(batched_imgs.contiguous(), image_sizes)。需要注意的是,在 tracing 模式下image_sizes会变为list[Tensor]以支持动态尺寸。

5.3 在 MetaArch 中的调用

ImageList.from_tensors是所有视觉主干(含本项目 Vim 主干 modeling/backbone/vim.py)的输入必经之路。以 R-CNN 家族为例,meta_arch/rcnn.py#L229 与 rcnn.py#L311 分别在训练与推理路径中调用:

images = ImageList.from_tensors( [batched_inputs[i]["image"].to(self.device) for i in range(num_images)], self.backbone.size_divisibility, )

panoptic_fpn.py、semantic_seg.py、dense_detector.py等 meta_arch 也遵循同一模式。VimDet 这类基于纯注意力/SSM 主干的检测器同样依赖此机制把不同分辨率的输入对齐后送入 backbone/vim.py。

六、Keypoints:关键点标注与热图编解码

6.1 (N, K, 3) 存储与 COCO 可见性

Keypoints(keypoints.py#L8-L101)存储标注级关键点数据,张量形状(N, K, 3):N 为实例数,K 为每实例关键点数,最后一维为(x, y, visibility)。可见性遵循 COCO 约定(keypoints.py#L14-L19):

  • v=0:未标注(此时x=y=0);
  • v=1:已标注但不可见;
  • v=2:已标注且可见。

构造时统一转 float32;to_heatmap是训练目标生成的核心入口。

6.2 to_heatmap:标注 → 单热空间标签

Keypoints.to_heatmap(boxes, heatmap_size)(keypoints.py#L43-L58)将关键点坐标映射进每个 ROI,输出两个(N, K)张量:整数空间标签[0, heatmap_size²-1]与有效性标记。底层_keypoints_to_heatmap(keypoints.py#L105-L161)使用 Heckbert 1990 的连续-离散坐标换算约定(d = floor(c),c = d + 0.5),并处理落在 ROI 右/下边界上的关键点(钳到heatmap_size - 1)。

6.3 heatmaps_to_keypoints:热图 → 坐标反算

与to_heatmap互为逆过程的heatmaps_to_keypoints(maps, rois)(keypoints.py#L164-L235)从预测热图提取关键点:对每个 ROI 用 bicubic 插值回原框尺寸 → 对空间区域 softmax 归一化(除以 pooling 尺寸的归一化和,使不同大小目标的分数可比)→argmax取峰值 → 以c = d + 0.5换算连续坐标,并补上偏移量。返回(#ROIs, #keypoints, 4)的(x, y, logit, score)。该函数带@torch.jit.script_if_tracing装饰,兼顾 tracing 导出(ONNX/TorchScript)场景。

七、掩码体系:BitMasks、PolygonMasks 与 ROIMasks

7.1 三种表示及其取舍

分割掩码在 detectron2 中以三种形态存在(masks.py):

  • BitMasks(masks.py#L88-L258):N×H×W的 bool 张量,一张图内 N 个实例的像素级位图。内存占用高,但可直接在 GPU 上参与张量运算;
  • PolygonMasks(masks.py#L261-L463):list[list[ndarray]]多边形表示,第一层为实例、第二层为该实例的所有多边形(可含洞),第三层为扁平坐标[x0, y0, x1, y1, ..., xn, yn](n ≥ 3,坐标个数需为偶数)。始终驻留 CPU,to是 no-op;
  • ROIMasks(masks.py#L466-L534):(N, M, M)的 ROI 局部掩码,配合 ROI 框通过paste_masks_in_image贴回全图。

三者的转换路径:PolygonMasks → BitMasks(BitMasks.from_polygon_masks)、ROIMasks → BitMasks(ROIMasks.to_bitmasks),以及底层的polygons_to_bitmask(polygons, height, width)(masks.py#L22-L36),后者借助pycocotools的frPyObjects→merge→decode完成多边形栅格化。

7.2 训练目标生成:crop_and_resize 与栅格化

Mask R-CNN 类模型的训练目标需要为每个预测框生成mask_size × mask_size的裁剪掩码,两条路径分别对应两种表示:

  • BitMasks.crop_and_resize(boxes, mask_size)(masks.py#L191-L222):把位图转 float32 后经ROIAlign(aligned=True)裁剪缩放,输出>= 0.5二值化。相比多边形栅格化重建误差更小,但位图本身更耗内存;
  • PolygonMasks.crop_and_resize(boxes, mask_size)(masks.py#L394-L424):通过rasterize_polygons_within_box(masks.py#L39-L85)将多边形平移到框内、等比缩放到mask_size,再栅格化。

BitMasks还提供get_bounding_boxes()(由掩码反推紧致外接框,空掩码返回全零框,masks.py#L224-L240)与nonempty();PolygonMasks提供area()(鞋带公式 shoelace formula,masks.py#L426-L443)。annotations_to_instances中的mask_format参数("polygon"或"bitmask")正是两种目标形态的选择开关(detection_utils.py#L412-L448)。

八、正确性保障:测试体系与工程实践

8.1 单元测试覆盖

detectron2.structures的每个类都有对应测试,位于 tests/structures 目录:

  • test_boxes.py:BoxMode.convert的 list/array/tensor 多形态测试、XYWHA_ABS↔XYXY_ABS与XYWH_ABS↔XYWHA_ABS互转、pairwise_iou/pairwise_ioa、空列表cat、设备迁移与 TorchScript 可脚本化性;
  • test_imagelist.py:ImageList在 tracing 模式下的 padding、scripting 兼容性;
  • test_instances.py、test_masks.py、test_keypoints.py、test_rotated_boxes.py:分别验证字段管理、掩码互转与关键点热图往返。

测试中反复出现的test_scriptability/test_scriptability模式,印证了这些结构类在 TorchScript 导出(export 模块)时需保持兼容的工程约束。

8.2 使用建议

结合本项目(VimDet 场景)的实践,几点建议:

  1. 坐标系尽早归一:在自定义数据集注册时,务必给每个标注指定正确的bbox_mode,并依靠BoxMode.convert统一到XYXY_ABS,参考 builtin_datasets.md 的注册流程;
  2. 字段长度一致性:向Instances添加字段时,长度必须等于实例数,否则set会断言失败——这是最常见的报错点之一;
  3. ImageList 的 divisibility:使用 FPN/VimDet 等含多尺度特征的主干时,size_divisibility应设为 32 的倍数以保证跨层对齐;
  4. 掩码格式选型:数据量大的 COCO 训练建议用mask_format="polygon"(省内存),需要低重建误差时可切换"bitmask";
  5. 旋转框注意角度语义:RotatedBoxes的角度约定为图像坐标下的 CCW 度数,负角度与+360N等价但 RoI 池化结果不同,跨库迁移时务必核对标注约定。

结语

detectron2.structures是连接"标注 → 数据加载 → 模型 → 输出"整条流水线的数据中间件:BoxMode负责坐标系统一,Boxes/RotatedBoxes承载几何运算,Instances组织单图多实例,ImageList完成批量打包,Keypoints与三态掩码体系支撑关键点与分割任务。理解这些结构类的设计契约(Tensor 风格行为、字段一致性、设备迁移、JIT 兼容),是深入阅读 detectron2 源码乃至在其上开发 VimDet 等新模型的必要基础。结合本文引用的源码路径与测试用例,你可以随时回到仓库中逐行验证每个 API 的精确行为。

  • 人工智能
  • 计算机视觉
  • 深度学习
  • 预训练
  • 微调

【免费下载链接】Vim

[ICML 2024] Vision Mamba: Efficient Visual Representation Learning with Bidirectional State Space Model

项目地址:https://gitcode.com/gh_mirrors/vim2/Vim
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询