MVSNet单应性变换推导

MVSNet系列中基本都用到了可微的单应性变换,但是很难找到详细的推导过程,在这里整理出来。

一、推导

单应性:相机从不同角度拍摄同一物体得到的图像可以用单应性矩阵进行变换
在这里插入图片描述

1.世界坐标系到像素坐标系的转换

pi,pci,PW,分别为像素坐标系,相机坐标系,世界坐标系坐标
参考图像I1相机:K1,R1,C1,Z1分别为相机内参矩阵、旋转矩阵、平移矩阵、深度
原图像Ii相机:Ki,Ri,Ci,Zi分别为相机内参矩阵、旋转矩阵、平移矩阵、深度
由参考图像和源图像的坐标变换关系得①②:
在这里插入图片描述

2.沿参考图像相机坐标系的Z轴建立代价体

即Z轴为深度方向,对于代价体给定深度的平面,可由表示。其中,nT=(0,0,1)(后边会用到),为平面法向量。
在这里插入图片描述

3.从公式②开始推导

p0和pi是三维空间同一点的成像,即Pw相同,先将p0换到世界坐标系Pw,再从世界坐标系变换到像素坐标系pi:

在这里插入图片描述
代入
在这里插入图片描述

得,
在这里插入图片描述

在这里插入图片描述

注:
1.旋转矩阵R为正交矩阵,转置等于逆
2.像素坐标pi,p1都为齐次坐标,与系数无关,省去Z1/Zi。例如[a,b,z]→[a/z,b/z,1]=[u,v,1],x.[a,b,z]=[xa,xb,xz]→[xa/xz,xb/xz,1]=[a/z,b/z,1]

二、分析tensorflow源码公式

def get_homographies_inv_depth(left_cam, right_cam, depth_num, depth_start, depth_end):

    with tf.name_scope('get_homographies'):
        # cameras (K, R, t)
        R_left = tf.slice(left_cam, [0, 0, 0, 0], [-1, 1, 3, 3])
        R_right = tf.slice(right_cam, [0, 0, 0, 0], [-1, 1, 3, 3])
        t_left = tf.slice(left_cam, [0, 0, 0, 3], [-1, 1, 3, 1])
        t_right = tf.slice(right_cam, [0, 0, 0, 3], [-1, 1, 3, 1])
        K_left = tf.slice(left_cam, [0, 1, 0, 0], [-1, 1, 3, 3])
        K_right = tf.slice(right_cam, [0, 1, 0, 0], [-1, 1, 3, 3])

        # depth 
        depth_num = tf.reshape(tf.cast(depth_num, 'int32'), [])

        inv_depth_start = tf.reshape(tf.div(1.0, depth_start), [])
        inv_depth_end = tf.reshape(tf.div(1.0, depth_end), [])
        inv_depth = tf.lin_space(inv_depth_start, inv_depth_end, depth_num)
        depth = tf.div(1.0, inv_depth)

        # preparation
        num_depth = tf.shape(depth)[0]
        K_left_inv = tf.matrix_inverse(tf.squeeze(K_left, axis=1))
        R_left_trans = tf.transpose(tf.squeeze(R_left, axis=1), perm=[0, 2, 1])
        R_right_trans = tf.transpose(tf.squeeze(R_right, axis=1), perm=[0, 2, 1])

        fronto_direction = tf.slice(tf.squeeze(R_left, axis=1), [0, 2, 0], [-1, 1, 3])          # (B, D, 1, 3)

        c_left = -tf.matmul(R_left_trans, tf.squeeze(t_left, axis=1))
        c_right = -tf.matmul(R_right_trans, tf.squeeze(t_right, axis=1))                        # (B, D, 3, 1)
        c_relative = tf.subtract(c_right, c_left)        

        # compute
        batch_size = tf.shape(R_left)[0]
        temp_vec = tf.matmul(c_relative, fronto_direction)
        depth_mat = tf.tile(tf.reshape(depth, [batch_size, num_depth, 1, 1]), [1, 1, 3, 3])

        temp_vec = tf.tile(tf.expand_dims(temp_vec, axis=1), [1, num_depth, 1, 1])

        middle_mat0 = tf.eye(3, batch_shape=[batch_size, num_depth]) - temp_vec / depth_mat
        middle_mat1 = tf.tile(tf.expand_dims(tf.matmul(R_left_trans, K_left_inv), axis=1), [1, num_depth, 1, 1])
        middle_mat2 = tf.matmul(middle_mat0, middle_mat1)

        homographies = tf.matmul(tf.tile(K_right, [1, num_depth, 1, 1])
                     , tf.matmul(tf.tile(R_right, [1, num_depth, 1, 1])
                     , middle_mat2))

    return homographies

源码公式:
在这里插入图片描述
其中,fronto_direction为参考图像旋转矩阵的第三行。

对比分析

在这里插入图片描述

推导的公式与源码不同之处:

1.对于红色和蓝色部分,即平移量之差与负号

原因:对于平移旋转有两种做法

①:先平移,再旋转
在这里插入图片描述
②:先旋转,再平移
在这里插入图片描述

所以,
在这里插入图片描述

另一方面,可以这样考虑:
对于先旋转再平移
在这里插入图片描述
即 -R.T.t为相机坐标系原点在世界坐标系的坐标
对于先平移再旋转
在这里插入图片描述
即平移量C为相机坐标系原点在世界坐标系的坐标
所以,两平移量之差等于两相机坐标系原点对应世界坐标系坐标之差。

结论:由于数据集提供的平移量是t(先旋转),所以将其转换为了C(先平移)

2.对于绿色部分

由于nT=(0,0,1),所以nT.R1相当于取旋转矩阵R1的最后一行,即代码中的fronto_direction。

三、分析pytorch源码公式

def homo_warping(src_fea, src_proj, ref_proj, depth_values):
    # src_fea: [B, C, H, W]
    # src_proj: [B, 4, 4]
    # ref_proj: [B, 4, 4]
    # depth_values: [B, Ndepth]
    # out: [B, C, Ndepth, H, W]
    batch, channels = src_fea.shape[0], src_fea.shape[1]
    num_depth = depth_values.shape[1]
    height, width = src_fea.shape[2], src_fea.shape[3]

    with torch.no_grad():
        proj = torch.matmul(src_proj, torch.inverse(ref_proj))
        rot = proj[:, :3, :3]  # [B,3,3]
        trans = proj[:, :3, 3:4]  # [B,3,1]

        y, x = torch.meshgrid([torch.arange(0, height, dtype=torch.float32, device=src_fea.device),
                               torch.arange(0, width, dtype=torch.float32, device=src_fea.device)])
        y, x = y.contiguous(), x.contiguous()
        y, x = y.view(height * width), x.view(height * width)
        xyz = torch.stack((x, y, torch.ones_like(x)))  # [3, H*W]
        xyz = torch.unsqueeze(xyz, 0).repeat(batch, 1, 1)  # [B, 3, H*W]
        rot_xyz = torch.matmul(rot, xyz)  # [B, 3, H*W]
        rot_depth_xyz = rot_xyz.unsqueeze(2).repeat(1, 1, num_depth, 1) * depth_values.view(batch, 1, num_depth,
                                                                                            1)  # [B, 3, Ndepth, H*W]
        proj_xyz = rot_depth_xyz + trans.view(batch, 3, 1, 1)  # [B, 3, Ndepth, H*W]
        proj_xy = proj_xyz[:, :2, :, :] / proj_xyz[:, 2:3, :, :]  # [B, 2, Ndepth, H*W]
        proj_x_normalized = proj_xy[:, 0, :, :] / ((width - 1) / 2) - 1
        proj_y_normalized = proj_xy[:, 1, :, :] / ((height - 1) / 2) - 1
        proj_xy = torch.stack((proj_x_normalized, proj_y_normalized), dim=3)  # [B, Ndepth, H*W, 2]
        grid = proj_xy

    warped_src_fea = F.grid_sample(src_fea, grid.view(batch, num_depth * height, width, 2), mode='bilinear',
                                   padding_mode='zeros')
    warped_src_fea = warped_src_fea.view(batch, channels, num_depth, height, width)

    return warped_src_fea

在这里插入图片描述
推导:
在这里插入图片描述
注:
1.Z1=d,为参考相机下的深度
2.n.T.Pc1取Pc1Z坐标,即深度d,与分母约掉
3.之前已推导过
在这里插入图片描述

  • 14
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
傅里叶变换(Fourier transform)是一种将一个函数转换为另一个函数的数学变换方法。而sa函数是一个采样函数,它根据一些特定规则对信号进行采样。 首先,假设我们有一个连续信号y(t),其中t表示时间。为了将这个连续信号转换为离散信号,我们需要进行采样。假设我们以时间间隔为T进行采样,得到的采样序列为y(n),其中n表示离散时间。 我们定义采样频率为Fs = 1/T,即每秒钟进行采样的次数。然后,我们可以通过傅里叶变换来将采样序列转换为频域表示。傅里叶变换公式如下: Y(k) = Σ y(n) * exp(-j * 2π * k * n / N) 其中,Y(k)表示频域中的幅度,k为频域中的频率序号,N为采样序列的长度,exp为欧拉公式中的指数函数,j为虚数单位。 对于sa函数,它是一个周期函数,即在一定时间间隔内重复。因此,在进行傅里叶变换时,我们可以利用信号的周期性质来简化计算。 具体来说,我们可以将采样序列看作是一个周期为N的序列,其中N为采样序列长度。然后,根据傅里叶级数展开的思想,我们可以将采样序列表示为一系列频率成分的叠加。 最后,我们通过傅里叶变换公式计算每个频率成分的幅度。这样,我们就可以得到表示信号在频域中的幅度分布。 总结一下,sa函数的傅里叶变换推导过程中,首先将连续信号进行采样得到离散序列,然后利用傅里叶变换公式将采样序列转换为频域表示。最终得到信号在频域中的幅度分布。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值