2

我正在研究 OpenCV 的 SIFT 描述符提取实现。我遇到了一些令人费解的代码来获取兴趣点邻域的半径。下面是带注释的代码,变量名称更改为更具描述性:

// keep octave below 256 (255 is 1111 1111)
int octave = kpt.octave & 255;
// if octave is >= 128, ...????
octave = octave < 128 ? octave : (-128 | octave);
// 1/2^absval(octave)
float scale = octave >= 0 ? 1.0f/(1 << octave) : (float)(1 << -octave);
// multiply the point's radius by the calculated scale
float scl = kpt.size * 0.5f * scale;
// the constant sclFactor is 3 and has the following comment:
// determines the size of a single descriptor orientation histogram
float histWidth = sclFactor * scl;
// descWidth is the number of histograms on one side of the descriptor
// the long float is sqrt(2)
int radius = (int)(histWidth * 1.4142135623730951f * (descWidth + 1) * 0.5f);

我知道这与转换为获取兴趣点的比例有关(我已阅读 Lowe 的论文),但我无法将这些点与代码联系起来。具体来说,我不明白前 3 行和最后一行。

我需要了解这一点才能为运动创建类似的局部点描述符。

4

1 回答 1

8

我不明白前 3 行

事实上,这个 SIFT 实现在属性中编码了几个值。KeyPoint octave如果您参考第439 行,您可以看到:

kpt.octave = octv + (layer << 8) + (cvRound((xi + 0.5)*255) << 16);

这意味着八度音阶存储在第一个字节块中,第二个字节块中的层等。

所以kpt.octave & 255(可以在unpackOctave方法中找到)只需掩盖关键点八度音以检索有效八度音程值。

另外:这个 SIFT 实现使用负的第一个八度音阶 ( int firstOctave = -1) 来处理更高分辨率的图像。由于倍频程索引从 0 开始,因此计算映射:

octave index = 0 => 255
octave index = 1 => 0
octave index = 2 => 1
...

此映射在第 790 行计算:

kpt.octave = (kpt.octave & ~255) | ((kpt.octave + firstOctave) & 255);

因此,上面的第二行只是映射回这些值的一种方式:

octave = 255 => -1
octave = 0   => 0
octave = 1   => 1
..

第三行只是一种计算比例的方法,考虑到负八度音阶给出的比例> 1,例如1 << -octave给出2,octave = -1这意味着它的大小加倍。

[我不明白] 最后一行。

基本上它对应于一个圆的半径,该圆包裹一个维度为 的平方块D,因此sqrt(2)和 除以 2。D通过乘以计算:

  • 关键点量表,
  • 放大系数 = 3,
  • 描述符直方图的宽度 = 4,四舍五入到下一个整数(因此为 +1)

事实上,您可以在vlfeat 的 SIFT 实现中找到详细描述:

每个空间 bin 的支持都有 SBP = 3sigma 像素的扩展,其中 sigma 是关键点的尺度。因此,所有的 bin 一起支持 SBP x NBP 像素宽。由于使用了像素的加权和插值,因此支持扩展了另一个半仓。因此,支持是 SBP x (NBP + 1) 像素的方形窗口。最后,由于补丁可以任意旋转,我们需要考虑一个 2W += sqrt(2) x SBP x (NBP + 1) 像素宽的窗口。

最后我强烈推荐你参考这个vlfeat SIFT 文档

于 2013-06-10T08:57:59.413 回答