光度立体法估计法线与反射率重建场景

1 从明暗恢复形状

从明暗恢复形状(Shape from Shading,SfS)是指从图像的明暗信息推断出物体表面几何形状的过程。这个问题假设光照条件已知,目标表面是光滑且均匀的,并且照明是单向的。其基本思想是根据目标表面对光照的反应,推断出表面法线,从而得到表面的三维形状。

1.1 渲染方程

渲染方程为:

L o ( x , ω o ) = L e ( x , ω o ) + ∫ Ω f ( x , ω i , ω o ) L i ( x , ω i ) ( n ⋅ ω i ) d ω i L_o(\mathbf{x}, \omega_o) = L_e(\mathbf{x}, \omega_o) + \int_{\Omega} f(\mathbf{x}, \omega_i, \omega_o) L_i(\mathbf{x}, \omega_i) (\mathbf{n} \cdot \omega_i) d\omega_i Lo(x,ωo)=Le(x,ωo)+Ωf(x,ωi,ωo)Li(x,ωi)(nωi)dωi

我们将其简化:
L out = ρ ⋅ L in ⋅ n ⊤ s = R ( n ) L_{\text{out}} = \rho \cdot L_{\text{in}} \cdot n^\top s = R(n) Lout=ρLinns=R(n)

  • L out L_{\text{out}} Lout :表示出射光线
  • ρ \rho ρ :表示该点的漫反射率
  • L in L_{\text{in}} Lin :表示入射光线
  • n n n :表示表面法线
1.2 梯度空间表示法

梯度空间表示法 Gradient Space Representation,使用梯度信息来表示物体表面的几何形状。

image-20240426204012566

在梯度空间表示法中,给定光线 s s s 和观察到的反射率 R R R,我们可以通过以下方式来计算法线 n n n

  1. 计算 n ⊤ s = cos ⁡ ( θ ) n^\top s = \cos(\theta) ns=cos(θ),得到 θ \theta θ —— 法线 n n n 与光线 s s s 之间的夹角
  2. 按照给定的角度 θ \theta θ,从光线 s s s 开始,投影这个集合到 z = 1 z = 1 z=1 平面上,我们可以得到一个锥面曲线即反射率曲线 Iso-Reflectance Contour,它表示出了所有与 s s s 的夹角为 θ \theta θ​ 的可能法线

通过不同的反射率 R R R ,我们可以得到反射率图(Reflectance Map):

image-20240426204817978

因此,在梯度空间表示法中,我们可以通过结合反射率图和其他几何信息来有效地参数化法线,并利用这些信息来推断物体表面的形状和材质。

2 光度立体法

光度立体法(Photometric Stereo)是通过在相同视点但采用不同(已知)点光源的多张图像来实现三维重建的。其需要对每个像素的法线和反射率进行估计

2.1 采集 K 张图像

在相同的相机视点下,采集 K 张图像,每张图像使用不同的已知点光源。这些光源的位置和方向应该是已知的,并且在不同的图像中有所变化。

通过如下的反射率图可以看到,对于每个像素,我们必须要有3个反射率图才能确定一个真正的法线,所以,我们至少要有从3个不同的方向来的光源

image-20240426210653283

但是要避免共线光源:当光度立体设置中使用的所有光源都是共线时(位于同一直线或平面上),所得的线性系统将变得秩亏。这使得不可能唯一地确定每个像素的表面法线。因此,光度立体无法提供准确的重建。

2.2 光度法线与反射率估计

对于每个像素,利用 K 张图像中的光照信息,通过解光度立体方程组来估计法线和反射率:

使用兰伯反射,并且入射光强度为 L in = 1 L_{\text{in}} = 1 Lin=1,那么图像的亮度 I I I 可以表示为:

I = L out = ρ ⋅ n ⊤ s = ρ ⋅ s ⊤ n I = L_{\text{out}} = \rho \cdot n^\top s = \rho \cdot s^\top n I=Lout=ρns=ρsn

对于给定的三个观测(相同的 v v v,不同的 s s s),我们可以将其表示为矩阵形式如下:

[ I 1 I 2 I 3 ] = [ s 1 ⊤ s 2 ⊤ s 3 ⊤ ] ⋅ ρ ⋅ n \begin{bmatrix} I_1 \\ I_2 \\ I_3 \end{bmatrix} = \begin{bmatrix} s_1^\top \\ s_2^\top \\ s_3^\top \end{bmatrix} \cdot \rho \cdot n I1I2I3 = s1s2s3 ρn

其中, I 1 , I 2 , I 3 I_1, I_2, I_3 I1,I2,I3 分别是三个观测得到的图像亮度, s 1 , s 2 , s 3 s_1, s_2, s_3 s1,s2,s3 分别是对应的三个光源方向向量, ρ \rho ρ 是漫反射率, n n n​ 是法线向量。

通过使用更多的光源可以获得更好的结果(通过对测量进行平均)。通过最小二乘解法我们得到:

ρ n = ( S ⊤ S ) − 1 S ⊤ I \rho n = (S^\top S)^{-1}S^\top I ρn=(SS)1SI

其中, S S S 是包含所有光源方向 s i s_i si 的矩阵, I I I 是包含对应图像亮度 I i I_i Ii 的向量

得到 ρ n \rho n ρn 后,可以通过 ρ = ∣ ∣ ρ n ∣ ∣ 2 \rho = ||\rho n||_2 ρ=∣∣ρn2 n n n 是单位向量 )来计算漫反射率 ρ \rho ρ n = ρ n / ρ n = \rho n / \rho n=ρn/ρ

简单实现代码如下:

def compute_normals_albedo_map(imgs, mask, light_positions):
    """
    imgs np.array [k,h,w] np.float32 [0.0, 1.0]
    mask np.array [h,w] np.bool
    light_positions np.array [k,3] np.float32
    ---
    dims:
    k: number of images
    h: image height (num rows)
    w: image width (num cols)
    """

    S = light_positions
    I = imgs.reshape(imgs.shape[0], -1)

    # rho n = (S^T S)^-1 S^T I
    rho_n = np.linalg.inv(S.T @ S) @ S.T @ I
    # rho = ||rho_n||
    rho = np.linalg.norm(rho_n, axis=0)
    # n = rho_n / rho
    n = np.divide(rho_n, rho, out=np.zeros_like(rho_n), where=rho != 0)

    # mask out
    mask_flat = mask.flatten()
    n[:, ~mask_flat] = 0

    normals_unit = n.T.reshape(imgs.shape[1], imgs.shape[2], 3)
    rho = rho.reshape(imgs.shape[1], imgs.shape[2])

    assert normals_unit.shape == (imgs.shape[1], imgs.shape[2], 3)
    assert rho.shape == (imgs.shape[1], imgs.shape[2])

    rho = np.clip(rho, 0.0, 1.0)
    normals_unit = np.clip(normals_unit, 0.0, 1.0)

    return normals_unit, rho, mask

输出是:

  • normals_unit:三维数组,表示每个像素点的单位法线向量。它的形状是 (imgs.shape[1], imgs.shape[2], 3),其中 imgs.shape[1]imgs.shape[2] 分别是图像的高度和宽度,3 表示每个像素点的法线有三个分量(x、y、z)
  • rho:二维数组,表示每个像素点的漫反射率。它的形状是 (imgs.shape[1], imgs.shape[2]),与图像的大小相同,对应每一个像素的反射率
2.3 重新照亮场景

我们现在知道整个图像的像素法线和反照率,这使我们能够重新照亮场景(即人为地改变灯光位置

def relight_scene(light_pos, normals_unit, albedo, mask):
    """
    light_pos np.array [k,3] np.float32
    mask np.array [h,w] np.bool
    ----
    dims:
    h: image height (num rows)
    w: image width (num cols)
    ----
    returns:
        imgs np.array [h,w] np.float32 [0.0, 1.0]
    """
    assert light_pos.shape == (3,)
    assert np.allclose(1.0, np.linalg.norm(light_pos))
    assert normals_unit.shape[-1] == 3
    assert len(normals_unit.shape) == 3

    img = albedo * (normals_unit @ light_pos)
    # mask out
    img[~mask] = 0
    img_norm = np.clip(img, 0.0, 1.0)

    assert np.all(
        np.logical_and(0.0 <= img_norm, img_norm <= 1.0)
    ), "please normalize your image to interval [0.0,1.0]"
    return img_norm

其接受灯光位置、像素法线、反射率和掩码作为输入,并返回重新照亮后的图像。

其实其中核心就是:根据之前的简化的渲染公式 L out = ρ ⋅ L in ⋅ n ⊤ s L_{\text{out}} = \rho \cdot L_{\text{in}} \cdot n^\top s Lout=ρLinns ,其中设置入射光线为1,即可得:

img = albedo * (normals_unit @ light_pos)

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

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

相关文章

计算机组成原理实验(一)--可控加减法电路设计实验

一、一位全加器的设计 视频学习链接&#xff1a;3-2-4 定点数的加法和减法运算 — 一位全加器的硬件逻辑实现_哔哩哔哩_bilibili 仿真电路图&#xff1a; 总结&#xff1a;奇数个1时Si输出为1&#xff0c;偶数个1输出为0&#xff1b;1的个数大于等于2时&#xff0c;Ci输出1 实…

Kafka 3.x.x 入门到精通(05)——对标尚硅谷Kafka教程

Kafka 3.x.x 入门到精通&#xff08;05&#xff09;——对标尚硅谷Kafka教程 2. Kafka基础2.1 集群部署2.2 集群启动2.3 创建主题2.4 生产消息2.5 存储消息2.6 消费消息2.6.1 消费消息的基本步骤2.6.2 消费消息的基本代码2.6.3 消费消息的基本原理2.6.3.1消费者组2.6.3.1.1 消费…

【优秀AI项目】每日跟踪 OpenVoice ,AI快站,OpenVoice

持续更新好玩的开源AI项目或AI商业应用体验 一起来玩转AI&#xff01;&#xff01; 1 huggingface 国内镜像站&#xff1a;AI 快站 HUggingface被墙了&#xff0c;emmmmm 所以我之前玩模型的一大感觉就是 下载什么模型之类的太难受了&#xff01;服了 看到一个镜像站——…

如何使用bof-launcher在CC++Zig应用程序中执行Beacon对象文件(BOF)

关于bof-launcher bof-launcher是一款针对Beacon对象文件&#xff08;BOF&#xff09;的安全测试工具&#xff0c;在该工具的帮助下&#xff0c;广大研究人员可以轻松在C/C/Zig应用程序中执行Beacon对象文件&#xff08;BOF&#xff09;。 Cobalt Strike 4.1于2020年6月25日发…

[Diffusion Model 笔记]DDIM 笔记 数学推导 Denoising Diffusion Implicit Models

目录 核心总结符号定义第一套&#xff0c;快速简单讲清采样方法继续分析&#xff0c;待定系数法求解图示理解关于参数sigma 本文是观看以下视频的笔记&#xff0c;强烈推荐观看最后的图示理解&#xff1a; https://www.bilibili.com/video/BV13P411J7dm/?spm_id_from333.788 论…

数据结构|树形结构|并查集

数据结构|并查集 并查集 心有猛虎&#xff0c;细嗅蔷薇。你好朋友&#xff0c;这里是锅巴的C\C学习笔记&#xff0c;常言道&#xff0c;不积跬步无以至千里&#xff0c;希望有朝一日我们积累的滴水可以击穿顽石。 有趣的并查集剧情演绎&#xff1a;【算法与数据结构】—— 并…

idea自定义配置文件的注释

打开 IntelliJ Idea 软件 依次找到 File—>Editor—>File and Code Templates 设置 Files 下的Class、Interface、Enum等 输入下面的内容 /** * description: ${NAME} * date: ${YEAR}-${MONTH}-${DAY} ${HOUR}:${MINUTE} * author: author **/

php动态高亮web源代码

php动态高亮web源代码 注&#xff1a;配置好不允许高亮的文件名&#xff0c;安全第一 #php实现动态展示目录树结构源代码 适用于开放源代码&#xff0c;结合html缓存使用效果更佳&#xff0c;因循环较多不适合放首页 能力有限没实现行号 演示&#xff1a;show source|开放…

吉布提国家概况

吉布提国家概况 &#xff08;最近更新时间&#xff1a;2022年10月&#xff09; 【国 名】 吉布提共和国&#xff08;The Republic of Djibouti&#xff0c; La Rpublique de Djibouti&#xff09;。 【面 积】 2.32万平方公里。 【人 口】约100万。主要有伊萨族和阿法尔族。…

认识HTTP

HTTP缺点 通信使用明文&#xff08;不加密&#xff09;&#xff0c;内容可能会被窃听 不验证通信方的身份&#xff0c;可能遭遇伪装 无法证明报文的完整性&#xff0c;所以有可能遭篡改 一、通信使用明文&#xff08;不加密&#xff09;&#xff0c;内容可能会被窃听 TCP/…

鸿蒙OpenHarmony【轻量系统 编译】 (基于Hi3861开发板)

编译 OpenHarmony支持hb和build.sh两种编译方式。此处介绍hb方式&#xff0c;build.sh脚本编译方式请参考[使用build.sh脚本编译源码]。 使用build.sh脚本编译源码 进入源码根目录&#xff0c;执行如下命令进行版本编译。 ./build.sh --product-name name --ccache 说明&…

【算法基础实验】图论-深度优先搜索和深度优先路径

深度优先(DFS) 理论基础 深度优先搜索&#xff08;DFS, Depth-First Search&#xff09;是图和树的遍历算法中的一种&#xff0c;它从一个节点开始&#xff0c;沿着树的边走到尽可能深的分支&#xff0c;直到节点没有子节点为止&#xff0c;然后回溯继续搜索下一个分支。DFS …

python基础之元组、集合和函数的定义与返回值

1.元祖 1.元祖的定义 元组的数据结构跟列表相似 特征&#xff1a;有序、 有序&#xff1a;有&#xff08;索引/下标/index&#xff09; 正序、反序标识符&#xff1a; ( ) 里面的元素是用英文格式的逗号分割开来关键字&#xff1a;tuple 列表和元组有什么区别&#xff1f; 元组…

JavaEE 初阶篇-深入了解 I/O 高级流(缓冲流、交换流、数据流和序列化流)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 缓冲流概述 1.1 缓冲流的工作原理 1.2 使用缓冲流的步骤 1.3 字节缓冲流于字符缓冲流的区别 1.4 字节缓冲流的实例 1.5 字符缓冲流的实例 2.0 转换流概述 2.1 字符…

局部多项式近似与 AMPM 算法

kappa3; %已在您的代码中定义% 定义窗口大小 windowSize (2*kappa1);% 初始化梯度估计值 [rows, cols] size(wrappedPhase); phi_y zeros(rows, cols); phi_x zeros(rows, cols);% 遍历每个窗口 for m 1kappa:rows-kappafor n 1kappa:cols-kappa% 提取局部窗口Z_mn wrap…

Git--基础学习--面向企业--持续更新

一、基础学习 1.1基本命令 //查询基础信息 git config --global --list //选取合适位置创建 mkdir 文件名 //创建文件夹 //全局配置 git config --global user.email "****e***i" git config --global user.name "*** K****"//--------------------进入…

SHViT:具有内存高效宏设计的单头视觉Transformer

文章目录 摘要1、引言2、分析与方法2.1、宏观设计中的冗余分析2.2、微观设计中的冗余分析2.3、单头自注意力2.4、单头视觉转换器 3、实验3.1、实现细节3.2、SHViT在ImageNet-1K分类任务上的表现3.3、SHViT在下游任务中的表现3.4、消融研究 4、相关工作5、结论SHViT&#xff1a;…

python-opencv实现最近邻插值和双线性插值对图片上采样

使用背景 当我们需要把图像进行放大或者缩小的时候&#xff0c;第一反应是使用resize()实现。很多情况下&#xff0c;我们会调用最近邻插值和双线性插值去放大图片&#xff0c;当然要说没有分辨率的损失那是不可能的&#xff0c;只能说在放大图片的过程中尽可能增加了图片的分…

免费的一键伪原创工具,用来高效写文章真妙!

写文章是一件耗时间、费精力的事&#xff0c;遇到没有写作灵感的时候&#xff0c;写文章就像嚼蜡一样难受&#xff0c;但好在有免费的一键伪原创工具&#xff0c;它能帮助我们高效率实现自动写作文章&#xff0c;并且整个写作的过程我们无需思考内容怎么去写&#xff0c;是可以…

自动驾驶传感器篇: GNSSIMU组合导航

自动驾驶传感器篇&#xff1a; GNSS&IMU组合导航 1.GNSS1.1 GNSS 系统概述1.2 GNSS系统基本组成1. 空间部分&#xff08;Space Segment&#xff09;&#xff1a;2. 地面控制部分&#xff08;Ground Control Segment&#xff09;&#xff1a;3. 用户设备部分&#xff08;Use…
最新文章