下面详细讲 RoI Pooling / RoIAlign 是如何提取候选框特征的。
它们解决的核心问题是:
RPN 生成的候选框大小各不相同,但后面的分类器/检测头要求输入是固定大小的特征。
所以 RoI Pooling / RoIAlign 的作用就是:
从共享特征图中,把每个候选框对应的区域"裁出来",并转换成固定尺寸的特征,比如
7×7×C。
1. 为什么需要 RoI 特征提取?
假设 RPN 生成了 3 个候选框:
text
proposal A:100 × 80
proposal B:240 × 160
proposal C:50 × 120
它们大小不同。
但是后面的检测头通常希望输入固定尺寸,比如:
text
7 × 7 × C
其中:
text
7 × 7:固定空间大小
C:通道数,比如 256、512、1024
如果不统一尺寸,后面的全连接层或分类头无法批量处理这些 proposal。
所以需要一种操作,把任意大小的候选框区域变成固定大小特征。
这就是:
text
RoI Pooling / RoIAlign
2. RoI 是什么?
RoI 是 Region of Interest,中文叫"感兴趣区域"。
在两阶段目标检测中:
text
RPN 生成的 proposal
↓
作为 RoI
↓
送入 RoI Pooling / RoIAlign
也就是说,每一个候选框就是一个 RoI。
例如:
text
RoI 1:可能是一只猫
RoI 2:可能是一辆车
RoI 3:可能是背景
RoI Pooling / RoIAlign 要做的是:
在 backbone 生成的共享特征图上,取出每个 RoI 对应的局部特征。
3. 整体流程
以 Faster R-CNN 为例:
text
输入图像
↓
Backbone 提取共享特征图
↓
RPN 生成 proposals
↓
将 proposals 映射到特征图上
↓
RoI Pooling / RoIAlign 提取固定大小特征
↓
检测头分类 + 边框回归
注意:RoI Pooling / RoIAlign 不是在原图像素上裁剪,而是在 特征图 上裁剪。
4. 原图坐标如何映射到特征图?
RPN 生成的 proposal 通常是原图坐标,例如:
text
原图大小:800 × 600
proposal:[x1=160, y1=120, x2=480, y2=360]
但是 backbone 输出的特征图可能比原图小很多,比如下采样 16 倍:
text
原图:800 × 600
特征图:50 × 38
stride = 16
那么原图上的 proposal 要映射到特征图上:
text
x1' = x1 / 16 = 160 / 16 = 10
y1' = y1 / 16 = 120 / 16 = 7.5
x2' = x2 / 16 = 480 / 16 = 30
y2' = y2 / 16 = 360 / 16 = 22.5
所以在特征图上的 RoI 大约是:
text
[10, 7.5, 30, 22.5]
问题来了:特征图上的坐标可能是小数。
这正是 RoI Pooling 和 RoIAlign 的重要区别之一。
5. RoI Pooling 怎么做?
RoI Pooling 的目标是把任意大小的 RoI 转换成固定大小。
比如固定输出:
text
7 × 7 × C
它的步骤可以分成 4 步。
5.1 第一步:把 proposal 映射到特征图
假设原图中的 proposal 映射到特征图后是:
text
左上角:(10.2, 7.6)
右下角:(30.8, 22.4)
RoI Pooling 会先做量化,也就是取整:
text
左上角:(10, 8)
右下角:(31, 22)
这一步会产生一定位置误差。
5.2 第二步:把 RoI 区域划分成固定数量的 bin
如果目标输出是:
text
7 × 7
那么 RoI Pooling 会把这个 RoI 区域划分成:
text
7 行 × 7 列 = 49 个小格子
每个小格子叫一个 bin。
可以想象成把候选框区域切成 49 块:
text
+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
+---+---+---+---+---+---+---+
| 8 | 9 |10 |11 |12 |13 |14 |
+---+---+---+---+---+---+---+
| ... |
+---+---+---+---+---+---+---+
|43 |44 |45 |46 |47 |48 |49 |
+---+---+---+---+---+---+---+
不管原来的 RoI 是大是小,都会被切成 7×7 个 bin。
5.3 第三步:每个 bin 内做池化
在每个 bin 里面,对特征值做池化。
最常见的是 max pooling。
也就是说:
text
每个 bin 中取最大值
对于每一个通道都这样做。
如果特征图有 C 个通道,那么每个 bin 会得到 C 个值。
最终 49 个 bin 得到:
text
7 × 7 × C
这就是该 RoI 的固定大小特征。
5.4 第四步:送入检测头
得到固定大小特征后,检测头就可以处理了:
text
RoI 特征:7 × 7 × C
↓
分类分支
↓
判断类别:猫 / 狗 / 车 / 背景
RoI 特征:7 × 7 × C
↓
回归分支
↓
精修边界框
6. RoI Pooling 的核心问题:量化误差
RoI Pooling 最大的问题是:它会多次取整。
主要有两个地方会量化:
text
1. proposal 坐标映射到特征图时取整
2. RoI 划分成 bin 时,bin 边界也可能取整
例如原本特征图坐标是:
text
[10.2, 7.6, 30.8, 22.4]
RoI Pooling 会变成:
text
[10, 8, 31, 22]
看起来误差不大,但映射回原图后,因为 stride 可能是 16 或 32,误差会被放大。
例如特征图上差 1 个像素,原图可能差:
text
16 个像素
对于普通目标检测,这种误差可能还可以接受;但对于实例分割、关键点检测这种需要精确对齐的任务,误差就比较明显。
7. RoIAlign 怎么做?
RoIAlign 是 Mask R-CNN 提出的改进。
它的核心思想是:
不再粗暴取整,而是保留浮点坐标,用双线性插值精确采样。
RoIAlign 的目标和 RoI Pooling 一样:
text
把任意大小 RoI 转换成固定大小特征
比如:
text
7 × 7 × C
但实现方式更精细。
8. RoIAlign 的步骤
8.1 第一步:保留浮点坐标
假设 RoI 映射到特征图后是:
text
[10.2, 7.6, 30.8, 22.4]
RoI Pooling 会取整。
但 RoIAlign 不取整,直接保留:
text
[10.2, 7.6, 30.8, 22.4]
这一步就是所谓的 align。
它保证 RoI 在特征图上的位置和原图位置更精确对应。
8.2 第二步:划分成固定数量的 bin
和 RoI Pooling 类似,如果输出是:
text
7 × 7
RoIAlign 也会把 RoI 划分成:
text
7 × 7 个 bin
但 bin 的边界也可以是小数。
例如某个 bin 的范围可能是:
text
x: 10.2 到 13.14
y: 7.6 到 9.71
它不会强制取整成整数边界。
8.3 第三步:在每个 bin 内采样
RoIAlign 不直接对整数网格区域做 max pooling。
它会在每个 bin 中选取若干个采样点。
例如每个 bin 采样 2×2 个点:
text
bin 内采样点:
p1, p2
p3, p4
这些采样点的位置通常也是小数坐标。
例如:
text
(11.3, 8.1)
(12.7, 8.1)
(11.3, 9.2)
(12.7, 9.2)
8.4 第四步:双线性插值
问题是:特征图只在整数网格点上有值。
比如特征图上有:
text
(11, 8)
(12, 8)
(11, 9)
(12, 9)
但 RoIAlign 想取的位置可能是:
text
(11.3, 8.6)
这个位置没有直接的特征值。
怎么办?
使用 双线性插值。
它会根据周围 4 个整数点的特征值,按距离加权算出小数点位置的特征值。
可以理解为:
text
离哪个点近,哪个点贡献更大
离哪个点远,哪个点贡献更小
比如采样点更靠近 (11, 9),那 (11, 9) 的特征值权重就更大。
8.5 第五步:聚合采样点
每个 bin 中有多个采样点,每个采样点通过双线性插值得到特征值。
然后 RoIAlign 会把这些采样点的值聚合起来。
常见方式是:
text
平均池化
或者某些实现中使用最大池化。
最终每个 bin 输出一个特征值,每个通道都这样做。
最后得到:
text
7 × 7 × C
9. RoI Pooling 和 RoIAlign 的区别
最核心区别是:
RoI Pooling 会取整,RoIAlign 不取整。
| 对比项 | RoI Pooling | RoIAlign |
|---|---|---|
| 坐标处理 | 会量化取整 | 保留浮点坐标 |
| bin 边界 | 通常取整 | 不取整 |
| 采样方式 | 对整数区域池化 | 对浮点位置采样 |
| 特征获取 | max pooling | 双线性插值 + 聚合 |
| 对齐精度 | 较低 | 更高 |
| 常见模型 | Fast R-CNN / Faster R-CNN | Mask R-CNN |
| 对分割任务 | 不够精细 | 更适合 |
一句话总结:
text
RoI Pooling 是"粗略裁剪 + 池化"
RoIAlign 是"精确对齐 + 插值采样"
10. 一个具体例子
假设某个 proposal 在原图上是:
text
[x1=160, y1=96, x2=416, y2=352]
backbone 的 stride 是 16。
映射到特征图:
text
[x1'=10, y1'=6, x2'=26, y2'=22]
这个 RoI 在特征图上大小是:
text
宽:16
高:16
如果输出是:
text
4 × 4
那么每个 bin 大小就是:
text
宽:16 / 4 = 4
高:16 / 4 = 4
RoI Pooling 会在每个 4×4 的区域里做 max pooling,最后得到:
text
4 × 4 × C
如果这个 RoI 坐标刚好都是整数,RoI Pooling 和 RoIAlign 的差距可能不大。
但如果 RoI 是:
text
[x1'=10.3, y1'=6.7, x2'=26.8, y2'=22.2]
RoI Pooling 会取整,RoIAlign 不取整。
RoIAlign 会在更准确的小数位置采样,所以对齐更精确。
11. 为什么叫 Align?
因为它要解决的是 特征和原图之间的错位问题。
在目标检测中,候选框来自原图坐标,而特征来自下采样后的特征图。
如果映射和池化时不断取整,就会导致:
text
原图中的目标位置
和
特征图中提取的位置
不完全对齐
这种错位对检测框可能影响不算特别大,但对 mask 影响很大。
比如实例分割要判断每个像素属于不属于目标:
text
猫的边缘
人的轮廓
车的边界
如果 RoI 特征和原图位置没有对齐,预测出来的 mask 边缘就可能偏移。
RoIAlign 的目的就是:
text
让 RoI 特征和原图目标位置更准确对齐
12. RoIAlign 为什么对 Mask R-CNN 很重要?
Mask R-CNN 不仅要预测框,还要预测每个目标的像素级 mask。
检测框只需要大概框住目标即可,但 mask 需要更精确的空间信息。
如果使用 RoI Pooling:
text
候选框位置被取整
bin 边界被取整
特征和原图产生偏移
mask 分支就会受到影响。
RoIAlign 通过:
text
不取整
浮点坐标
双线性插值
保留了更准确的空间位置信息,因此实例分割效果明显更好。
13. RoI Pooling / RoIAlign 输出的特征长什么样?
假设 backbone 输出特征图是:
text
50 × 38 × 256
RPN 生成了 300 个 proposal。
RoIAlign 输出大小设为:
text
7 × 7
那么输出就是:
text
300 × 7 × 7 × 256
含义是:
text
300:有 300 个候选框
7 × 7:每个候选框被统一成固定空间大小
256:每个位置有 256 维特征
然后检测头会对每个候选框的 7×7×256 特征进行分类和回归。
14. RoI 特征提取后的检测头怎么用?
得到 RoI 特征后,通常有两种处理方式。
Faster R-CNN 检测头
常见流程:
text
RoI 特征:7 × 7 × C
↓
flatten
↓
全连接层
↓
分类输出
↓
边框回归输出
输出包括:
text
类别概率
边框偏移量
Mask R-CNN 检测头
Mask R-CNN 会有多个分支:
text
RoIAlign 特征
├── 分类分支
├── 边框回归分支
└── mask 分支
其中 mask 分支通常会使用更高分辨率的 RoI 特征,比如:
text
14 × 14
或者经过反卷积上采样后得到:
text
28 × 28
用于预测每个实例的二值 mask。
15. 用类比理解
可以把 RoI Pooling / RoIAlign 想象成从地图上裁剪区域。
RoI Pooling
像是:
text
先把目标区域的坐标四舍五入到网格线上
再裁剪
再压缩成固定大小
好处是简单快。
坏处是:
text
位置可能有偏差
边缘不够精细
RoIAlign
像是:
text
不强行对齐到整数网格
而是保留精确坐标
在小数位置通过插值读取信息
好处是位置更准。
尤其适合:
text
实例分割
关键点检测
高精度定位
16. 最简总结
RoI Pooling / RoIAlign 都是为了:
把不同大小的候选框区域,转换成固定大小的特征,供第二阶段检测头分类和边框回归使用。
区别是:
text
RoI Pooling:
候选框坐标和 bin 边界会取整,使用 max pooling,速度快但有量化误差。
RoIAlign:
不取整,保留浮点坐标,使用双线性插值采样,空间对齐更准确。
在现代两阶段检测器中,尤其是 Mask R-CNN 及其后续模型里,通常更倾向使用:
text
RoIAlign
因为它能更好地保持候选框和特征之间的位置对齐。