解析pytorch3D中的坐标变换问题

Pytorch3D坐标系

借用pytorch3d官网对于坐标系的解释来讲,pytorch3d中使用了一个NDC坐标系,这个坐标系最终将所有3d点的坐标归一化到-1到1之间。熟悉pytorch的朋友应该知道这是为了方便梯度的反向传播。
与常规的图形和视觉系统一致,我们分别定义了
1、模型坐标系(可选,图中未显示)
2、世界坐标系
3、相机坐标系
4、NDC坐标系
5、屏幕坐标系
其坐标轴的朝向如图所示
在这里插入图片描述

坐标系转换

在研究中我们很自然的获得了了3D模型的K(内参信息),RT位姿信息,但是往往直接输入会出现问题
这里需要辨析一些概念

概念解析

1、这里的R一般是世界坐标系到相机坐标系的旋转矩阵,一般记为R_w2c ,其逆矩阵 R − 1 = R T R^{-1}=R^T R1=RT的三列代表着相机三个轴分别在世界坐标系的朝向,向量模长是1,基本性质可以通过games101查看

2、T一般是世界坐标系到相机坐标系的位移矩阵,一般记为t_w2c,它代表着
我们通过下面简单的分块矩阵乘法感受一下R和T具体的含义
设相机位姿为V,世界坐标系下相机位置(相机位置处成像)为t
[ V T 1 ] [ E − t 1 ] = ] [ V T − V T t 0 1 ] [\begin{matrix}V^T&\\&1\end{matrix}][\begin{matrix}E&-t\\&1\end{matrix}]=][\begin{matrix}V^T&-V^Tt\\0&1\end{matrix}] [VT1][Et1]=][VT0VTt1]
这个矩阵就是 [ R T 1 ] [\begin{matrix}R&T\\&1\end{matrix}] [RT1]

坐标转换

不同包之间的相机朝向一般是不同的
在这里插入图片描述
如上图所示,例如当已知在opencv坐标系下的R和T,想获得在pytorch3d坐标系下的转换矩阵 R p R_p Rp T p T_p Tp
按照我们对 V V V的理解,只需要将x轴和y轴取反就可以了,即对 V V V的第一列和第二列取反,其位置坐标t不变。

我们将相机再求一遍逆矩阵,获得新的 R p R_p Rp T p T_p Tp即为将原始 [ R T 1 ] [\begin{matrix}R&T\\&1\end{matrix}] [RT1]第一行和第二行取反的结果
注意: 新矩阵的对应T_p也要取反,这是因为我们是先定了相机位姿和位置然后求的逆矩阵获得的 R p R_p Rp T p T_p Tp

还有一个最重要的问题:pytorch3d中的3D点是按照Nx3的格式存储的,因此 在计算过程中 Point= point@R+t 而不是 Point=R @ point+t,所以在传入参数时应使用 R.t()
ps:如果使用R, T = pytorch3d.renderer.look_at_view_transform(dist=3, elev=0, azim=180)
生成的R和T已经是转置好了的,因此就不必再转置了。

实验

我们接下来通过几个实验,熟悉pytorch3d中的操作
下面提供了实验使用的模型和图片
实验所用到的数据和完整代码在以下链接中
实验链接

1、 利用MeshRasterizer生成深度图

正如前面所说,我们设in_ndc=False ,即为将相机定义在常规的坐标系中,我们按照opencv转pytorch3d的操作将R和T对应位置做改变

def getcamera(width,height,R,T,K):
    T=T.reshape(3)
    R[0,:]=-R[0,:]
    T[0]=-T[0]
    R[1,:]=-R[1,:]
    T[1]=-T[1]
    R=R.t()
    fx,_,cx,_,fy,cy,_,_,_=K.reshape(9)
    cameras = PerspectiveCameras(
    image_size=[[height, width]],
    R=R[None],
    T=T[None],
    focal_length=torch.tensor([[fx, fy]], dtype=torch.float32),
    principal_point=torch.tensor([[cx, cy]], dtype=torch.float32),
    in_ndc=False,
    device=device
    )
    return cameras
def getdepth(model_path,R,T,K,device=torch.device('cuda')):
    width,height=640,480
    verts,faces=pytorch3d.io.load_ply(model_path)
    mesh = pytorch3d.structures.Meshes( 
        verts=[verts],
        faces=[faces],
        textures=TexturesVertex(verts_features=torch.ones_like(verts)[None]),
    )
    mesh = mesh.to(device)
    
    cameras=getcamera(width,height,R,T,K)
    rasterizer = MeshRasterizer(
    cameras=cameras,
    raster_settings=RasterizationSettings(
        image_size=((height, width)),
    ),
    )
    fragments = rasterizer(meshes_world=mesh)

    return fragments.zbuf[0, :, :, 0],cameras

在这里插入图片描述
再将原图读入后,将深度值不为-1的点可视化一下,我们发现确实深度图渲染正确
在这里插入图片描述

2、手动变换验证3D-2D关系

更进一步,我们想手动控制3D-2D的变换,而不是通过渲染。

xy = camera1.transform_points_screen(xyz)[:, :, :2]

我们试图使用以上函数可视化为红色点,看看和渲染的深度位置的区别,通过下图可以看出是基本覆盖的,也就是说当使用上图函数时可以将3D转换成2D
在这里插入图片描述

那么在这个过程中具体坐标是如何转换的呢?
我们通过查阅文档的源码发现,要注意区分view和camera以及screen和camera的区别

在transform_points_screen过程中,实际上中转借助了ndc坐标系,但是图中没有画出
其中最重要的是view 到screen是先取反和缩放坐标轴,然后加上偏移,而到camera是直接缩放+偏移
在这里插入图片描述
我们用了两种方式,试图找到screen坐标和camera坐标的关系

xy2=camera1.transform_points(xyz)
xy3=camera1.get_ndc_camera_transform().transform_points(xy2)
pr_point_fix = torch.zeros(
            (1, 4, 4), device=device, dtype=torch.float32
        )
pr_point_fix[:, 0, 0] = 1.0
pr_point_fix[:, 1, 1] = 1.0
pr_point_fix[:, 2, 2] = 1.0
pr_point_fix[:, 3, 3] = 1.0
pr_point_fix[:, :2, 3] = -2.0 * camera1.get_principal_point()
pr_point_fix_transform = Transform3d(
    matrix=pr_point_fix.transpose(1, 2).contiguous(), device=device
)
xy44=pr_point_fix_transform.transform_points(xy2)
xy44[:,:,:2]=-xy44[:,:,:2]
xy4=get_ndc_to_screen_transform(camera1,with_xyflip=True,image_size=camera1.get_image_size()).transform_points(xy3)


print(torch.allclose(xy4,xy44)) #true
print(torch.allclose(xy,xy4[:,:,:2])) #true

参考资料

  • https://zhuanlan.zhihu.com/p/593204605
  • https://zhuanlan.zhihu.com/p/651937759
  • https://pytorch3d.org/docs/cameras
  • 8
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值