将针孔模型相机 应用到3DGS

Motivation

3DGS 的 投影采用的是 CG系的投影矩阵 P P P, 默认相机的 principal point (相机光心) 位于图像的中点处。但是 实际应用的 绝大多数的 相机 并不满足这样一个设定, 因此我们 需要根据 f , c x , c y {f,c_x, c_y} f,cx,cy 这几个参数重新构建3D GS 的 投影矩阵。

3DGS 的相机模型的构建

原理:

目的: 将一个 相机View 坐标系的一个3D 点 变换到 NDC 坐标系

维基百科:https://www.songho.ca/opengl/gl_projectionmatrix.html

一共有如下3个坐标系

Eye 坐标系(View 坐标系) : ( x e , y e , z e ) (x_e,y_e,z_e) (xe,ye,ze)

View 坐标系 通过转化 矩阵 M p r o j M_{proj} Mproj 转化到Clip 坐标系。**先进行 缩放变换,**缩放之后的坐标是 ( x p , y p , z p ) (x_p,y_p,z_p) (xp,yp,zp), 缩放之后继续做正交投影 【 就是把 (l,r)映射到 (-1,1)】,最后才可以变换到Clip坐标系下面的坐标 ( x c , y c , z c ) (x_c,y_c,z_c) (xc,yc,zc)

Clip坐标系 : ( x c , y c , z c ) (x_c,y_c,z_c) (xc,yc,zc)

Clip 坐标系通过除以 齐次坐标系的 最后一个分量转换到 NDC 坐标系

NDC坐标系 : ( x n , y n , z n ) (x_n,y_n,z_n) (xn,yn,zn)

在这里插入sd图片描述
n为视锥体近面z坐标,f为远面z坐标,
t为视锥体top面z坐标,b为 bottom面y坐标,
r为视锥体right x坐标,left为左面x坐标,

1. 从 View 坐标系转化到 Clip 坐标系

主要是通过相似三角形的 原理去列方程:
x p = − n ⋅ x e z e = n ⋅ x e − z e x_p=\frac{-n \cdot x_e}{z_e}=\frac{n \cdot x_e}{-z_e} xp=zenxe=zenxe
y p = − n ⋅ y e z e = n ⋅ y e − z e y_p=\frac{-n \cdot y_e}{z_e}=\frac{n \cdot y_e}{-z_e} yp=zenye=zenye
z p z_p zp 坐标的求解,可以观看 闫令琪 的计算机图像学:有两个基本假设:

  • Near 的平面的所有点 Z 缩放之后的 Z值不会发生变化;
  • Far 平面的所有点 Z 缩放之后的 Z值不会发生变化;

得到了 缩放之后的 ( x p , y p , z p ) (x_p,y_p, z_p) (xp,yp,zp), 然后我们再通过线性变换做正交投影将Cuboid 的长和宽分别缩放到 一个 单位立方体, 即将 [l, r] ⇒ [-1, 1] and [b, t] ⇒ [-1, 1]。
Eq2:
x c = α x x p + β x x_c=\alpha_x x_p+\beta_x xc=αxxp+βx
y c = α y y p + β y y_c=\alpha_y y_p+\beta_y yc=αyyp+βy

x p x_p xp x e x_e xe 的关系带入上面Eq2式子当中。以 x 坐标为例,由于l对应-1,r对应1,求解出 α \alpha α β \beta β我们有:

x c = 2 x p r − l − r + l r − l ( x p = n x e − z e ) = 2 ⋅ n ⋅ x e − z e r − l − r + l r − l = 2 n ⋅ x e ( r − l ) ( − z e ) − r + l r − l = 2 n r − l ⋅ x e − z e − r + l r − l = 2 n r − l ⋅ x e − z e + r + l r − l ⋅ z e − z e = ( 2 n r − l ⋅ x e + r + l r − l ⋅ z e ⏟ x c ) / − z e \begin{aligned} x_c& =\frac{2 x_p}{r-l}-\frac{r+l}{r-l} \quad\left(x_p=\frac{n x_e}{-z_e}\right) \\ & =\frac{2 \cdot \frac{n \cdot x_e}{-z_e}}{r-l}-\frac{r+l}{r-l} \\ & =\frac{2 n \cdot x_e}{(r-l)\left(-z_e\right)}-\frac{r+l}{r-l} \\ & =\frac{\frac{2 n}{r-l} \cdot x_e}{-z_e}-\frac{r+l}{r-l} \\ & =\frac{\frac{2 n}{r-l} \cdot x_e}{-z_e}+\frac{\frac{r+l}{r-l} \cdot z_e}{-z_e} \\ & =(\underbrace{\frac{2 n}{r-l} \cdot x_e+\frac{r+l}{r-l} \cdot z_e}_{x_c}) /-z_e\end{aligned} xc=rl2xprlr+l(xp=zenxe)=rl2zenxerlr+l=(rl)(ze)2nxerlr+l=zerl2nxerlr+l=zerl2nxe+zerlr+lze=(xc rl2nxe+rlr+lze)/ze

y c = 2 y p t − b − t + b t − b ( y p = n y e − z e ) = 2 ⋅ n ⋅ y e − z e t − b − t + b t − b = 2 n ⋅ y e ( t − b ) ( − z e ) − t + b t − b = 2 n t − b ⋅ y e − z e − t + b t − b = 2 n t − b − z e ⋅ y e + t + b t − b − z e = ( 2 n t − b ⋅ y e + t + b t − b ⋅ z e ⏟ y c ) / − z e \begin{aligned} y_c & =\frac{2 y_p}{t-b}-\frac{t+b}{t-b} \quad\left(y_p=\frac{n y_e}{-z_e}\right) \\ & =\frac{2 \cdot \frac{n \cdot y_e}{-z_e}}{t-b}-\frac{t+b}{t-b} \\ & =\frac{2 n \cdot y_e}{(t-b)\left(-z_e\right)}-\frac{t+b}{t-b} \\ & =\frac{\frac{2 n}{t-b} \cdot y_e}{-z_e}-\frac{t+b}{t-b} \\ & =\frac{2 n}{\frac{t-b}{-z_e} \cdot y_e}+\frac{t+b}{\frac{t-b}{-z_e}} \\ & =(\underbrace{\frac{2 n}{t-b} \cdot y_e+\frac{t+b}{t-b} \cdot z_e}_{y_c}) /-z_e\end{aligned} yc=tb2yptbt+b(yp=zenye)=tb2zenyetbt+b=(tb)(ze)2nyetbt+b=zetb2nyetbt+b=zetbye2n+zetbt+b=(yc tb2nye+tbt+bze)/ze

上面的恰好是 Clip 坐标系的 齐次坐标系。 发现 计算的 x c , y c x_c,y_c xc,yc 恰好是 除以了 − z e -z_e ze, 因此 我们可以预先指定 齐次坐标的 第四项是: w c = − z e w_c = -z_e wc=ze earlier。 下面是 Clip 坐标系的齐次坐标:
( x c y c z c w c ) = ( 2 n r − l 0 r + l r − l 0 0 2 n t − b t + b t − b 0 ⋅ ⋅ ⋅ ⋅ 0 0 − 1 0 ) ( x e y e z e w e ) \left(\begin{array}{c}x_c \\ y_c \\ z_c \\ w_c\end{array}\right)=\left(\begin{array}{cccc}\frac{2 n}{r-l} & 0 & \frac{r+l}{r-l} & 0 \\ 0 & \frac{2 n}{t-b} & \frac{t+b}{t-b} & 0 \\ \cdot & \cdot & \cdot & \cdot \\ 0 & 0 & -1 & 0\end{array}\right)\left(\begin{array}{l}x_e \\ y_e \\ z_e \\ w_e\end{array}\right) xcyczcwc = rl2n000tb2n0rlr+ltbt+b1000 xeyezewe

$Z的推导 和 x,y 没有关系。因此 上面的矩阵写成下面的形式:
( x c y c z c w c ) = ( 2 n r − l 0 r + l r − l 0 0 2 n t − b t + b t − b 0 0 0 A B 0 0 − 1 0 ) ( x e y e z e w e ) \left(\begin{array}{c}x_c \\ y_c \\ z_c \\ w_c\end{array}\right)=\left(\begin{array}{cccc}\frac{2 n}{r-l} & 0 & \frac{r+l}{r-l} & 0 \\ 0 & \frac{2 n}{t-b} & \frac{t+b}{t-b} & 0 \\ 0 & 0 & A & B \\ 0 & 0 & -1 & 0\end{array}\right)\left(\begin{array}{c}x_e \\ y_e \\ z_e \\ w_e\end{array}\right) xcyczcwc = rl2n0000tb2n00rlr+ltbt+bA100B0 xeyezewe ,
其中的 Z 项 单目提出来应该等于下面的式子:
z n = z c / w c = A z e + B w e − z c z_n=z_c / w_c=\frac{A z_e+B w_e}{-z_c} zn=zc/wc=zcAze+Bwe
最后根据: Z_near 平面 和 Z_far 平面不会移动的原因,得到最后的 投影矩阵:
M p r o j = ( 2 n r − l 0 r + l r − l 0 0 2 n t − b t + b t − b 0 0 0 − ( f + n ) f − n − 2 f n f − n 0 0 − 1 0 ) M_{proj}=\left(\begin{array}{cccc}\frac{2 n}{r-l} & 0 & \frac{r+l}{r-l} & 0 \\ 0 & \frac{2 n}{t-b} & \frac{t+b}{t-b} & 0 \\ 0 & 0 & \frac{-(f+n)}{f-n} & \frac{-2 f n}{f-n} \\ 0 & 0 & -1 & 0\end{array}\right) Mproj= rl2n0000tb2n00rlr+ltbt+bfn(f+n)100fn2fn0

Code:

3DGS 设定:

self.zfar = 100.0
self.znear = 0.01

下面这个 Projection_Matrix 的构建 和上面公式推导会有一点不一样的地方,尤其是对于 Z值的计算上,Github 上也有人提出过疑问。 矩阵的P[2,2] 有误, 但是作者又说 他在 Code 中没有 使用 Z的数值。

https://github.com/graphdeco-inria/gaussian-splatting/issues/388
https://github.com/graphdeco-inria/gaussian-splatting/issues/376

def getProjectionMatrix(znear, zfar, fovX, fovY):
    tanHalfFovY = math.tan((fovY / 2)) ## 视场角一半的正切数值
    tanHalfFovX = math.tan((fovX / 2))
	## 得到 l,b,top,right
    top = tanHalfFovY * znear
    bottom = -top
    right = tanHalfFovX * znear
    left = -right

    P = torch.zeros(4, 4)
    z_sign = 1.0

    P[0, 0] = 2.0 * znear / (right - left)
    P[1, 1] = 2.0 * znear / (top - bottom)
    P[0, 2] = (right + left) / (right - left)
    P[1, 2] = (top + bottom) / (top - bottom)
    P[3, 2] = z_sign
    P[2, 2] = z_sign * zfar / (zfar - znear)
    P[2, 3] = -(zfar * znear) / (zfar - znear)
    return P
带有Cx, Cy的相机模型:

https://github.com/graphdeco-inria/gaussian-splatting/issues/144

def getProjectionMatrixShift(znear, zfar, focal_x, focal_y, cx, cy, width, height, fovX, fovY):
    tanHalfFovY = math.tan((fovY / 2))
    tanHalfFovX = math.tan((fovX / 2))

    # the origin at center of image plane
    top = tanHalfFovY * znear
    bottom = -top
    right = tanHalfFovX * znear
    left = -right

    # shift the frame window due to the non-zero principle point offsets
    offset_x = cx - (width/2)
    offset_x = (offset_x/focal_x)*znear
    offset_y = cy - (height/2)
    offset_y = (offset_y/focal_y)*znear

    top = top + offset_y
    left = left + offset_x
    right = right + offset_x
    bottom = bottom + offset_y

    P = torch.zeros(4, 4)

    z_sign = 1.0

    P[0, 0] = 2.0 * znear / (right - left)
    P[1, 1] = 2.0 * znear / (top - bottom)
    P[0, 2] = (right + left) / (right - left)
    P[1, 2] = (top + bottom) / (top - bottom)
    P[3, 2] = z_sign
    P[2, 2] = z_sign * zfar / (zfar - znear)
    P[2, 3] = -(zfar * znear) / (zfar - znear)
    return P

或者其他人 也给了 Projection 基于 相机内参的写法:

https://github.com/graphdeco-inria/gaussian-splatting/issues/399

P[0, 0] = 2 * fx / W
P[1, 1] = 2 * fy / H
P[0, 2] = 2 * (cx / W) - 0.5
P[1, 2] = 2 * (cy / H) - 0.5
P[2, 2] = -(zfar + znear) / (zfar - znear)
P[3, 2] = 1.0
P[2, 3] = -(2 * zfar * znear) / (zfar - znear)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/581015.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux 安装 nvm,并使用 Jenkins 打包前端

文章目录 nvm是什么nvm下载nvm安装设置 nvm 环境变量设置 Jenkins 打包命令 nvm是什么 nvm全英文也叫node.js version management,是一个nodejs的版本管理工具。nvm和n都是node.js版本管理工具,为了解决node.js各种版本存在不兼容现象可以通过它可以安装…

电脑提示msvcp100.dll丢失的解决方法,多种有效的解决方法分享

在日常使用电脑进行工作的时候,我们常常依赖于各种高效软件来辅助完成任务,提升工作效率。然而,当你满怀期待地双击启动某个至关重要的办公软件时,屏幕上却弹出了一个令人措手不及的错误提示:“msvcp100.dll文件丢失”…

二. 搭建Nginx 直播流程服务器

目录 1. 前言 2. 安装 Nginx 依赖 3.下载源码 4. 编译安装 5.配置 rtmp 服务 6.验证配置 1. 前言 服务器由 NGINXRTMP 构成。 NGINX 是 HTTP 服务器, RTMP 是附加模块。 其中 NGINX 我选择的是用 源码编译方式 进行安装,因为这种方式可以自定义…

基于python语言气象水文数据处理及精美科研绘图实践技术

Python是功能强大、免费、开源,实现面向对象的编程语言,Python能够运行在Linux、Windows、Macintosh、AIX操作系统上及不同平台(x86和arm),Python简洁的语法和对动态输入的支持,再加上解释性语言的本质&…

C语言中的三大循环

C语言中为我们提供了三种循环语句,今天我就来与诸君细谈其中之奥妙。循环这一板块总结的内容较多,而且,很重要!(敲黑板!!!),所以诸君一定要对此上心,耐住性子…

修复所有 bug 并不能解决所有问题

原文:jeffpsherman - 2024.04.08 在软件领域,如同在制造业,有些问题是由于 bug 或“特殊原因”引发的,而有些则是“常见原因”,这是由于系统设计和实现的性质所导致的。修复 bug 就是移除特殊原因,消除 bu…

go语言实现简单认证样例

目录 1、代码实现样例 2、postman调用 1、代码实现样例 package mainimport ("net/http""strings""github.com/dgrijalva/jwt-go""github.com/gin-gonic/gin" )var (// 密钥,用于验证 JWT 令牌signingKey []byte("…

上班太闲了,一坐就是一天,有没有什么副业可以干的?

一、别做兼职,做副业 兼职,仅仅是用时间换取报酬,短暂且有限,实质上仍是雇佣劳动。副业则不同,它依托你的独特价值换取长久回报,犹如你的第二事业。 或许你还不太清楚兼职的局限性,以下是一些…

上位机开发PyQt5(一)【创建窗口、窗口标题、气泡、显示图片和图标、显示文字】

目录 一、 第一个Qt窗口 二、PyQt模块简介 三、窗口标题和气泡 setWindowTitle resize setToolTip 四、标签QLabel显示图片和图标 setPixmap setWindowIcon resize(label.pixmap().size()) 五、标签QLabel显示文字 setText QFont setPointSize setFont set…

ios 打印选择纸张

问题描述: 手机App开发中的打印功能,在android中可以选择打印的纸张是A4 A5 等,但是在ios系统中不能选择纸张,一般情况下会只有一个纸类型。 原因解释: 因为在打印机的配置页中可以设置打印机的当前纸张大小&#xff…

sql今天学习总结

排序order by(默认升序) order by id desc(降序排序) order by id,number(先按id排再按name排序) in,not in and or 通配符 where name like "Aa%";选取所有以Aa开头的名字 like "%r" 以r结…

从关键新闻和最新技术看AI行业发展(2024.2.12-2.25第十七期) |【WeThinkIn老实人报】

写在前面 【WeThinkIn老实人报】旨在整理&挖掘AI行业的关键新闻和最新技术,同时Rocky会对这些关键信息进行解读,力求让读者们能从容跟随AI科技潮流。也欢迎大家提出宝贵的优化建议,一起交流学习💪 欢迎大家关注Rocky的公众号&…

C语言-atoi和atof函数的使用

人生应该树立目标,否则你的精力会白白浪费。💓💓💓 目录 •🌙知识回顾 🍋知识点一:atoi函数的使用和实现 • 🌰1.函数介绍 • 🌰2.代码演示 • 🌰3.atoi函数的…

LeetCode78:子集

题目描述 给你一个整数数组 nums &#xff0c;数组中的元素 互不相同 。返回该数组所有可能的 子集 &#xff08;幂集&#xff09;。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 代码 class Solution { public:vector<vector<int>> res;vector<…

DAM与开源AI名片B2B2C商城系统深度融合,引领企业数字化升级

在数字化浪潮的推动下&#xff0c;内容科技日益成为企业营销战略中的核心力量。DAM&#xff08;数字资产管理&#xff09;以其卓越的内容管理能力&#xff0c;引领企业进入数字资产高效管理与利用的新时代。与此同时&#xff0c;开源AI名片B2B2C商城系统也以其强大的功能和创新…

【Linux】进程的控制①之进程创建与进程退出

一 、进程的创建 1、fork函数 fork函数功能&#xff1a;从已经存在的进程中创建一个新进程。新进程为子进程&#xff0c;原进程为父进程。 fork函数创建进程过后&#xff0c;父子进程代码和数据是共享的。在前面也讲过。 2.函数的返回值 如果进程创建成功&#xff0c;给父进…

PCB设计:差分线

1、差分线的定义 差分线是两条平行的、等长的走线,传输相位差180度的同一个信号,是一根传输“+信号”,一根传输“-信号”。两个信号相减得到2倍强度的有用信号。而两根信号线上的干扰信号相减之后也就没了。 2、差分线的优势 差分信号和普通的单端信号走线相比,最明显的优…

ftp: connect :连接被拒绝(win10)

目录预览 一、问题描述二、原因分析三、解决方案四、参考链接 一、问题描述 使用命令测试显示&#xff1a;“FTP拒绝连接” 二、原因分析 第一时间查看ftp服务启动了没哟&#xff0c;相关服务有2个&#xff0c;一个是【Microsoft FTP Service】一个是【IIS 管理服务】&#xf…

为什么堆排序的时间复杂度是O(N*logN)?

目录 前言&#xff1a; 堆排序&#xff08;以排升序为例&#xff09; 步骤&#xff08;用大根堆&#xff0c;倒这排&#xff0c;排升序&#xff09;&#xff1a; 1.先把要排列的数组建立成大根堆 2.堆顶元素&#xff08;82&#xff09;和最后一个元素交换&#xff08;2&…

【详细讲解CentOS常用的命令】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…