shaofengzeng/SuperPoint-Pytorch

请问训练sp时候的单应变换矩阵为什么要求逆呢?

Closed this issue · 8 comments

dbdxzx commented
    homography = torch.inverse(homography)#inverse here to be consistent with tf version

您好,请问在SuperPoint-Pytorch/tree/master/dataset/utils/homographic_augmentation.py中生成射影变换矩阵的时候,为什么要进行一次求逆。这样的话,数据增强中使用的参数的含义好像不太直观,调节的话也不太容易。

这里是什么意思(第一行说要注释,下面又说必须应用求逆),要想与tf版本保持一致的话,是需要注释求逆的代码吗?

    #TODO: comment the follwing line if you want same result as tf version
    # since if we use homography directly ofr opencv function, for example warpPerspective
    # the result we get is different from tf version. In order to get a same result, we have to
    # apply inverse operation,like this
    #homography = np.linalg.inv(homography)
    homography = torch.inverse(homography)#inverse here to be consistent with tf version

此外,我发现在keypoint_op.py中的warp_points函数中提示 保持一致需要求逆,但这与前面图像变换所使用的H不一致,得到的gt也是不对应的吧。

    ##TODO: uncomment the following line to get same result as tf version
    # homographies = torch.linalg.inv(homographies)
    points = torch.cat((points, torch.ones((points.shape[0], 1),device=device)),dim=1)
    ##each row dot each column of points.transpose
    warped_points = torch.tensordot(homographies, points.transpose(1,0),dims=([2], [0]))#batch dot

关于这些对应关系希望作者可以为我解惑,万分感谢。

您好,
凡是有
··· #TODO: comment the follwing line if you want same result as tf version
标识的代码,您可以忽略。我在复现rpautrat/SuperPoint代码时,为保证一致,几乎查看了他们所有关键代码的输出结果,并尝试通过torch严格复现,但毕竟两个框架不同,在实现上有区别,例如,矩阵求逆操作,torch和tf求逆的结果是不同的,印象里,他们的结果差一个逆,您可以自己测试一下。如果没记错,
···
homography = np.linalg.inv(homography)
homography = torch.inverse(homography)
'''
通过这两行代码,才能保证与tf求逆结果完全一致,原因我至今也没明白哈~。
因此,显然,如果我不追求与tf一样的结果,仅仅关注求逆本身这个运算,那么都通过torch对矩阵求逆就可以了,也满足矩阵逆的逆就是原矩阵这一基本事实。
当然,部分函数,如卷积操作,tf和torch是无法保证完全一致,这也是本项目与tf项目差异所在。

dbdxzx commented

感谢您的回复,根据您的提示,我又去看了rpautrat/SuperPoint对应的代码,确认了关于单应变换数据增强部分的实现是一致的。

但是我不太理解为什么要这样做

  • 直接使用通过cv2.getPerspectiveTransform得到的矩阵H更有明确的含义吧(代表着从center crop顺序经过射影、放缩、平移、旋转等操作得到目标图像),也可以更加方便地通过调节数据增强的参数(使用的分布的参数)来调整想要的效果。
  • 如果使用H矩阵的逆去生成warp_img,虽然也可以使得gt对应起来,但是这会使参数调整变得困难吧。

希望能够得到您的回复,感谢!

cv2.getPerspectiveTransform这个函数是有了特征匹配结果,去推测变换矩阵。而我们的目的不是求变换矩阵。
我们要解决得是通过自监督方式训练模型,实现特征点检测与特征生成。为了达到这个目标,使用了孪生网络。网络一支输入图像A,另一分支输入图像A变换后的图像A',然后对两幅图像进行特征点位置预测和特征预测,希望预测的结果尽可能一致。
为达到上述目的,作者设计了自监督的方法。具体的,先利用模拟数据训练magicpoint网络,能够对特征点进行粗预测,用这个模型,检测真实图像的特征点,此时结果肯定不理想,但这不是magicpoint的目的。我们用magicpoint主要目的是生成标签数据,用标签数据训练superpoint。
具体的,通过magicpoint对图像Q0进行预测,得到特征点集P0,然后通过homographic_augmentation.py,随机获得n个变换矩阵H1,H2,... ,Hn;分别对Q0做变换,得到Q1,Q2, ..., Qn,并通过magicpoint分别对Q0~Qn做预测,得到点集P1~Pn;通过H1~Hn的逆,对P1~Pn进行变换,得到 P1’~Pn';若我们对图像Q1~Qn施加H1~Hn逆变换,得到图像Q1‘~Qn’,那么P1’~Pn'相当于Q1‘~Qn’上的特征点(这里请仔细理解); 最后把P1’~Pn'的点位置以0,1矩阵方式表示出来,把这些矩阵求和(或者其它方式),通过阈值过滤掉出现频率较低的特征点,剩余的才是为可以被用于训练的特征点。
为什么要搞这么复杂?因为magicpoint是通过模拟数据集训练得到的,在真实数据集上效果不好,把magicpoint直接预测的结果用于训练,获得到很多 不稳定 的特征点 ,而通过上述过程,可以确保用于训练的特征点的稳定性。
这就是求逆的原因了。不知道有没有解释清楚~

dbdxzx commented

是的,我理解您所说的关于生成gt所使用的这种思路。

但是在训练superpoint模型的时候也需要随机生成一个H,从而得到一个可以拿来训练的图像对。而原论文中提到,要对H做一些限制,尽可能去模拟现实场景中可能会出现的图像对,这就对H的生成提出了限制。

  • 因此,代码中生成H的时候,从src_img的中心裁剪区域的4个角点出发,经过事先设计好的分布随机生成一系列的变换参数,得到dst_img(我认为这就是最终应该使用的warp_img,其可以通过调节yaml文件中的超参数来控制想要的效果,可控性好),这时候使用cv2.getPerspectiveTransform函数就可以计算得到满足我们要求的H
  • 然后再应用得到的H去生成warp_img,以及对应的特征点位置坐标,这样得到的图像就可以满足我们对其的限制
  • 然而,在代码实现中使用了H的逆,去对src_img进行变换得到warp_img,同时得到warp_img的gt。虽然这也是一种变换,但是这样的变换不容易通过yaml中的分布参数来控制想要的生成效果吧

因此,我认为不应该求逆,而是直接使用生成的H。不知道您怎么样看待这个问题?

您是指dataset/utils
/homographic_augmentation.py

 homography = torch.inverse(homography)#inverse here to be consistent with tf version

这行代码吗?

dbdxzx commented

是的,感觉加上这一行没什么必要。

是的,感觉加上这一行没什么必要。

您理解的没有问题,这个就是为了和tf版本保持结果一致加上的。如果没有这个要求,确实不需要的

dbdxzx commented

感谢您的回答