论文阅读可以看:Learning Feature Descriptors using Camera Pose Supervision阅读笔记(论文版)-CSDN博客
训练数据加载
加载大路径,然后根据传入的是train还是test选择不同文件夹,定义图像预处理方法,每个文件夹下包括许多图片(images文件),和pair.txt包含哪些图像是成对的,和img_cam.txt里面包含,图像名字、图像高,图像宽,fx,fy,cx,cy,旋转向量(9维),平移向量(3维)。然后把图像加载到一个字典中,其中键是图像的路径,值是一个Image类型的数据
包含图像名字、图像高,图像宽,fx,fy,cx,cy,旋转向量(9维),平移向量(3维)。
把这些参数读入到Image文件,然后使用图像的路径和他对应
然后读取图像对,根据pair.txt文件里的图片名字,读取train文件夹中的每一个文件夹,创建两个列表,imf1s和imf2s,分别存储成对图像的路径,它们对应的索引是一样的,然后这个文件夹图片加载完成之后放入两个总列表,接着循环遍历下一个文件夹,直到遍历完所有的文件夹。接着把两个图像对列表按照同一个索引打乱顺序,然后设置批次大小,是否打乱等,然后传入训练数据加载器,接着使用
train_loader_iterator = iter(cycle(train_loader))
创建了一个迭代器 train_loader_iterator,用于循环迭代训练数据加载器 train_loader 中的样本批次
训练的时候,首先加载第一个批次的图像,从imf1s和imf2s提取路径名称,根据Image存储的信息,分别提取宽高,fx,fy,cx,cy计算出内参矩阵,以及R和t计算出外参矩阵,然后计算两张图片相对外参矩阵R和t(相对于世界坐标系下),接着计算相对矩阵里的R旋转是否大于80°,如果大于80°则则不用这对图像参与训练。
然后根据计算出的相对变换矩阵R和t,首先把t转换成反对称矩阵用于叉乘计算
其中反对称矩阵的计算公式如下:
计算本质矩阵,E=t^R,然后计算基础矩阵F=K2^-T*E*K1^-1
接着把这对图像中的img1传入由事先预定好的方法进行提取特征点可以是随机可以是SIFT。如果SIFT没有提取到关键点则要么使用随机生成要么这对图像不用于训练。
接着根据传入的参数来决定是否进行排除不可靠的查询特征点,首先计算每个点的极线通过Fx其中x是被转成齐次坐标,把极线归一化成单位向量与img2的四个角点进行比较,如果有一个值大于sqrt(H2^2+W2^2)则排除。然后将查询点坐标 coord1 扩展为齐次坐标,然后乘以最小和最大深度值,得到 coord1_h_min 和 coord1_h_max。
将查询点在相机1和相机2中的齐次坐标通过相机外参 pose 进行变换,并根据相机内参进行归一化,得到相机2中的最小和最大坐标 coord2_min 和 coord2_max。
检查相机2中的坐标是否超出图像范围,并将超出范围的坐标标记为 out_range排除。
最后把选中的特征点返回,再随机选择事先定义好的点的个数。
接着加载好数据了,把经过预处理的im1和im2,转成张量的图片im1_ori和im2_ori,相对位姿pose(3*4),基础矩阵两个内参矩阵以及生成的查询点坐标全部返回data。
然后model.set_input(data)将训练数据传入模型。
网络模型
首先骨干网络部分使用的是ResNet50网络的前三层。
输入图像x(480*640)batchsize=5,x1是经过网络第一个模块输出的,首先经过初始的卷积和池化,分辨率降到原来的1/4,然后经过第一层x1为(5,256,120,160),x2为第二个模块输出,x2为(5,512,60,80),然后经过第三个模块为x3,x3为(5,1024,30,40),共享网络到此结束,下面是输出粗级别图和细级别图。
x3经过一个1*1的卷积核的卷积层,输入通道为1024,输出通道为128,然后经过一个bn层和激活层得到粗级别图为xc(5,128,30,40)。
细级别图,x3经过一个上采样过程使用插值方法,变为(5,1024,60,80)在经过一个卷积核为3*3的卷积层添加Padding输入通道为1024,输出为512,变为(5,512,60,80),这个时候再与x2进行通道维度的拼接变为(5,1024,60,80),再通过一个3*3卷积核的卷积层,添加padding,输入通道1024,输出通道为512变为(5,512,60,80),然后再经过一个上采样过程使用插值方法,变为(5,512,120,160)在经过一个卷积核为3*3的卷积层添加Padding输入通道为512,输出为256,变为(5,256,120,160),这个时候再与x1进行通道维度的拼接变为(5,512,120,160),再通过一个3*3卷积核的卷积层,添加padding,输入通道512,输出通道为256变为(5,256,120,160),最后再经过一个1*1卷积核的卷积层,输入通道为256输出通道为128,变为xf(5,128,120,160)。
xc为粗级别图,分辨率为原图大小的1/16,xf是细级别图分辨率为原图大小的1/4。
接下来把从第一张图提取的坐标点进行归一化处理,首先设置一个中心坐标c=(H/2,W/2),然后所有坐标x进行(x-c)/c,归一化到[-1,1]中,好处是尺度不变性: 通过将坐标归一化到固定的范围,可以使特征点在不同尺度的图像中保持相对位置,从而增强特征点的尺度不变性。
输入粗级别图和坐标值进行采样描述子,使用的是双线性插值得到feat1(5,500,128)
传入feat1和featmap2(5,128,30,40)以计算获取期望对应位置,生成一个归一化坐标点位置网格图,大小为30*40*2,其中第一个30*40代表x值,第二个30*40代表y的值,从-1到1,中间固定间隔,接着展平为grid_n(30*40,2)。然后将featmap2展平,为(5,30*40,128),将feat1和展平后的featmap2计算相关性,就是计算内积prob,为(5,500,1200),然后在这个最后一个维度1200进行softmax。先将grid_n增加两个维度(1,1,1200,2)的格式用于计算prob也增加一个维度为(5,500,1200,1),相乘得到最终的点坐标,因为在第三个维度上求和,相当于加权求和得到最终的x和y与第一张图某个提取的点对应的坐标(也是归一化坐标)expected_coord_n(5,500,2)。
然后计算每个期望点的置信度,将grid_n每个点的x和y的平方乘上每个点的概率值prob,然后加和再减去每个点的期望值expected_coord_n的x和y的平方,得到var,然后把var的x和y开方加和得到std,如果概率分布图prob中,对应的点的概率和期望值的概率距离很近那么这个方差就小,如果在其它部分概率分布平均也有大值那么这个方差就大,得到的每个值就是这个得到的点和置信度,如果这个std高,那么就证明这个点的置信度低,如果这个值低,那就说明这个点的置信度高。std(5,500)
然后根据是否使用最近邻策略来计算细级别图中窗口的中心位置,如果不使用最近邻那么则直接使用expected_coord_n,如果使用最近邻则传入在粗级别提取的第一张图特征点的描述符和第二张图的粗级别图进行计算,计算方法如下:
首先把所有特征点的描述子全部与第二张图粗级别图进行内积,每个特征点都得到1200个值,对这些值取最大值的索引,然后根据这个索引把他转换成2维坐标,再进行归一化,然后返回(5,500)。
接下来再根据归一化的特征点坐标(第一张图的)在第一张图的细级别图上进行采样描述子,得到细级别图描述子feat_fine(5,500,128),然后把feat_fine,第二张图的细节别图xf2和在粗级别上匹配的第二张图期望点传入计算细级别图对应点。
首先创建一个二维网格,宽高都是细级别图的1/8,值的范围都是[-0.125,0.125],然后将传入的中心点位置与这个网格相加,这样就得到了整个窗口的坐标在细级别图中的位置。这样就得到了每一个窗口的网格图(5,500,300,2).然后把窗口的第二张图的细节别图传入进行采样提取特征描述子。得到feat2_win(5,500,300,128),然后将feat_fine和feat2_win进行计算相似度内积,feat_fine reshape成(2500,1,128)
feat2_win reshape成(2500,300,128),计算完内积之后进行softmax,(5,500,300),然后再进行坐标值和概率值相乘得到期望坐标(5,500,2).然后再和上述方法一样计算方差置信度。
现在已经得到了第一张图特征点在粗级别匹配的点和细级别匹配的点了,下面需要使用计算得到的第二张图的特征点再反过来计算对于第一张图的对应点。
使用得到的第二张图对应第一张图的期望点,在第二张图粗级别图中采样这些点的描述子,使用和上面一样的方法计算出第二张图的期望点在第一张图的位置和置信度方差coord1_lc_n, std_lc。
然后在细级别图上做相同的操作,也是在第一张细级别图上定义一个1/8细级别图大小的窗口进行匹配得到细级别图1上的匹配点和置信度方差coord1_lf_n, std_lf。
接着把第一张图和第二张图得到的期望点(粗级别和细级别)全部恢复成原图上的坐标点,之前是归一化坐标点,coord2_ec(图二粗级别)coord2_ef(图二细级别)coord1_lc(图一粗级别)coord1_lf(图一细节别)全部是(5,500,2).然后把这些加上四个置信度方差全部返回。std_c: 粗级别预测的坐标的标准差。std_f: 细级别预测的坐标的标准差。std_lc: 粗级别预测的第一张图像上的坐标的标准差。std_lf: 细级别预测的第一张图像上的坐标的标准差。至此正向传播结束
反向传播:
损失函数计算首先计算在粗级别图二中的点距离极线的距离,其中极线的计算方法是Fx1,x1代表第一张图的坐标,F代表两张图之间的基础矩阵,计算Fx1之前,因为F是3*3大小而x1是(x,y)所以需要把x1变换成齐次坐标系,在最后加一个维度1,(x,y,1),coord1_h和coord2_h分别是图一的特征点和图二粗级别的期望点,他们的数据形式都是(5,3,500),F则是(5,3,3),F*coord1_h就是图二粗级别期望点对应的极线,epipolar_line_=F*coord1_h是(5,3,500),接着对极线进行归一化到单位长度。然后计算点到极线的距离
最后返回epipolar_cost_c(5,500),表示每个点和极线的距离粗级别。
将极线与期望点距离与三个阈值进行比较分别是短边*0.125(mask_ctof).短边*0.5(mask_epip_c)和短边*0.025(mask_cycle_c),如果大于则分别设置,不添加细级别极线约束,不添加极线约束和不添加循环一致性约束,形式全部是(5,500)的bool。
然后再和上面的计算极限约束距离的方法一样计算细级别图2的距离epipolar_cost_f形式也是(5,500)
接着再根据细级别图的极线距离约束epipolar_cost_f计算出和上面相同的掩码约束mask_epip_f和mask_cycle_f,和上面一样一个是阈值短边*0.5一个是短边*0.025,根据mask_epip_f值判断这个点是否添加极线约束,根据mask_cycle_f判断是否添加循环一致性约束。
下面开始计算损失函数每一个项的权重
粗级别视图之间极线损失的权重:
传入std_c(5,500)和mask_epip_c(5,500)进行计算,方法是先对std_c进行求倒数,因为标准差大的表示预测不准确,标准差小的表示预测准确,标准差较小的样本可能对模型的训练贡献更大,因为它们代表着相对“容易”预测的情况。相反,标准差较大的样本可能对模型的训练贡献较小,因为它们代表着相对“困难”预测的情况。得到inverse_std,然后将inverse_std的值进行归一化处理,使他们的加和为1,再与mask_epip_c进行相乘因为mask_epip_c里面是布尔类型的转成float就是true=1,false=0,然后会去掉一些特征点不参加反向传播,最后再进行一次归一化,返回weight_c也是(5,500)表示粗级别视图之间极线损失的每一个点的权重。
细级别视图之间极线损失的权重:
传入std_f和mask_epip_f*mask_ctof(需要同时满足添加粗级别特征图细级别极线损失和细节别图满足极线损失的要求),和上面方法一样返回weight_f也是(5,500).
关于极线距离和极线损失的权重已经计算完成了,下面就要计算粗级别视图之间的极线损失和细级别视图之间的极线损失。
粗计算方法:epipolar_cost_c*weight_c,然后对这个值求平均再除以原图像的长边得到eloss_c
细级别计算方法也差不多:
eloss_f
粗级别循环一致性损失权重:
输入std_c * std_lc作为标准差mask_cycle_c作为掩码和上面的操作一样返回weight_cycle_c
细级别循环一致性损失权重:
输入std_f * std_lf作为标准差mask_cycle_f作为掩码和上面一样的操作然后返回weight_cycle_f
下面计算循环一致性损失:
粗级别循环一致性损失:
输入coord1(原始提取的特征点),coord1_lc(通过期望点反到第一张图粗级别的期望点),weight_cycle_c。计算方法是,设置一个阈值th,首先计算coord1和coord1_lc的欧氏距离,然后只选择距离小于th的计算损失函数参与反向传播,将weight_cycle_c和筛选出的距离相乘求平均再除以长边的长度就是粗级别循环一致性损失closs_c。
细级别循环一致性损失:
输入coord1(原始提取的特征点),coord1_lf(通过期望点反到第一张图细级别的期望点),权重weight_cycle_c,计算方式和上面一样,返回细级别循环一致性损失closs_f。
总损失:
如果要加上标准差的损失则
最后将loss(总损失), eloss_c(粗极线损失), eloss_f(细极线损失), closs_c(粗循环一致性损失), closs_f(细级别循环一致性损失), std_loss(标准差损失)全部返回。
把总损失传递给backward()进行反向传播更新模型参数,在更新学习率,至此训练一轮结束。