0%

挖个坑,准备写一下6.S081做的几个lab的过程

环境信息

作用 主机名 IP OS CPU 内存 系统盘 数据盘
observer, obproxy ecs-ob-0001 192.168.0.128 CentOS 7.6 64bit 8C 16G 50G 200G
observer ecs-ob-0002 192.168.0.185 CentOS 7.6 64bit 8C 16G 50G 200G
observer ecs-ob-0003 192.168.0.12 CentOS 7.6 64bit 8C 16G 50G 200G

准备

本次部署采用三副本部署模式,基本参照官方文档的部署步骤,不同的是ODP没有使用单独的机器部署。

没有特别说明,准备部分的步骤对每台机器都执行。

资源准备

根据官方文档,对于CentOS系统,运行以下命令手动关闭透明大页:

1
echo never > /sys/kernel/mm/transparent_hugepage/enabled

配置免密登录

为每台机器配置ssh免密登录,中控机器上如果没有密钥,则创建并将公钥发送给每台目标机器:

1
2
3
ssh-keygen -t rsa
scp ~/.ssh/id_rsa.pub root@192.168.0.185:~/.ssh
scp ~/.ssh/id_rsa.pub root@192.168.0.12:~/.ssh

目标机器将公钥加入authorized_keys

1
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

完成后就可以免密登录目标机器了:

1
ssh root@192.168.0.185

如果嫌敲ip麻烦,可以在中控机器的/etc/hosts里面加上主机信息,然后ssh的时候用主机名。

1
2
3
192.168.0.128 ecs-ob-0001
192.168.0.185 ecs-ob-0002
192.168.0.12 ecs-ob-0003

配置时钟源

配置时钟同步,给每台机器安装ntp服务:

1
yum install ntp ntpd -y

使用ob-0001作为内网时钟源服务端,在/etc/ntp.conf中将外网时钟源注释掉,并加上

1
2
server 127.127.1.0
fudge 127.127.1.0 stratum 10

保存后重启ntp服务

1
systemctl restart ntpd

然后查看时钟源是否同步,带”*”表示已经同步。

1
ntpq -p

剩下两台目标机器的/etc/ntp.conf也进行类似修改,将时钟源换成ob-0001

1
server 192.168.0.128 iburst

然后重启ntp服务即可。查看时钟源是否同步:

1
2
3
4
[root@ecs-ob-0002 ~]# ntpq -p
remote refid st t when poll reach delay offset jitter
==============================================================================
*192.168.0.128 LOCAL(0) 11 u 40 64 377 0.196 7.476 8.616

使用ntpstattimedatectl命令验证目标机器上的时钟是否同步:

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@ecs-ob-0002 ~]# ntpstat
synchronised to NTP server (192.168.0.128) at stratum 12
time correct to within 26 ms
polling server every 64 s
[root@ecs-ob-0002 ~]# timedatectl
Local time: Sat 2022-06-04 14:38:31 CST
Universal time: Sat 2022-06-04 06:38:31 UTC
RTC time: Sat 2022-06-04 06:38:31
Time zone: Asia/Shanghai (CST, +0800)
NTP enabled: yes
NTP synchronized: yes
RTC in local TZ: no
DST active: n/a

规划磁盘

正常情况下,OceanBase数据库需要分别挂载三块盘:数据盘data_dir、事务日志盘redo_dir和OB数据库安装盘home_path

由于仅做测试+条件限制,这里我将所有数据都挂载在/ob-data盘上。数据盘、事务日志盘和数据库安装盘配置的具体要求参考官方文档

设置limits.conf

OceanBase 数据库的进程涉及的限制包括线程最大栈空间大小(Stack)、最大文件句柄数(Open Files)和 core 文件大小 (Core File Size)。

/etc/security/limits.conf中修改以下内容:

1
2
3
4
5
6
7
8
9
10
root soft nofile 655350
root hard nofile 655350
* soft nofile 655350
* hard nofile 655350
* soft stack 20480
* hard stack 20480
* soft nproc 655360
* hard nproc 655360
* soft core unlimited
* hard core unlimited

完成后重新登陆,通过ulimit -a查看是否生效。

如果用vscode重新登陆,有可能出现不会生效的情况,重启一下就好了。

设置sysctl.conf

为了保证OB的正常运行,需要对sysctl.conf进行修改配置,提高Linux系统的性能。

/etc/sysctl.conf中修改以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# for oceanbase
## 修改内核异步 I/O 限制
fs.aio-max-nr=1048576

## 网络优化
net.core.somaxconn = 2048
net.core.netdev_max_backlog = 10000
net.core.rmem_default = 16777216
net.core.wmem_default = 16777216
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216

net.ipv4.ip_local_port_range = 3500 65535
net.ipv4.ip_forward = 0
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.tcp_syncookies = 0
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.ipv4.tcp_max_syn_backlog = 16384
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_max_syn_backlog = 16384
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_slow_start_after_idle=0

vm.swappiness = 0
vm.min_free_kbytes = 2097152

# 此处为 OceanBase 数据库的 data 目录
# kernel.core_pattern = /data/core-%e-%p-%t

基本按照官网部署教程修改,由于仅做测试,最后一行注释掉了。修改完成后执行sysctl -p使配置生效。

关闭防火墙和SELinux

使用如下命令关闭防火墙:

1
2
3
systemctl disable firewalld 
systemctl stop firewalld
systemctl status firewalld

/etc/selinux/config中修改如下内容:

1
SELINUX=disabled

然后使更改生效

1
setenforce 0

查看更改是否生效

1
sestatus

由于使用的ECS默认关闭了防火墙和SELinux,这一步直接跳过。

创建用户

如果担心使用root误操作,可以创建一个新的用户(例如admin),并用新用户来进行操作。

OceanBase部署

完成上面的准备工作后,就可以开始部署OceanBase了。

以下的安装均为中控机器上执行,其他的目标机器只需安装OBLibs即可。注意在目标机器上安装时也需要在yum-config-manager中添加Oceanbase的repo(如下安装OBD步骤中所示)。

安装OBD

1
2
3
yum install -y yum-utils
yum-config-manager --add-repo https://mirrors.aliyun.com/oceanbase/OceanBase.repo
yum install -y ob-deploy

安装OBLibs

1
yum install -y oceanbase-ce-libs

安装OBClient

1
2
yum install -y libobclient
yum install -y obclient

下载并修改配置文件

可以从OceanBase的GitHub上下载对应的yaml配置文件,然后根据自己机器的实际情况进行修改,下面是去除注释之后的部分,这里并没有把数据盘、事务日志盘和OB数据库安装盘的目录做区分,全部放在一起,实际生产环境的时候要分开设置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
user:
username: root # your own username
password: ******* # your own password
oceanbase-ce:
servers:
- name: ecs-ob-0001
ip: 192.168.0.128
- name: ecs-ob-0002
ip: 192.168.0.185
- name: ecs-ob-0003
ip: 192.168.0.12
global:
home_path: /ob-data/oceanbase
devname: eth0
memory_limit: 8G
system_memory: 3G
datafile_size: 50G
appname: ob_tpch
ecs-ob-0001:
mysql_port: 2881
rpc_port: 2882
zone: zone1
ecs-ob-0002:
mysql_port: 2881
rpc_port: 2882
zone: zone2
ecs-ob-0003:
mysql_port: 2881
rpc_port: 2882
zone: zone3
obproxy-ce:
depends:
- oceanbase-ce
servers:
- 192.168.0.128
global:
home_path: /root/obproxy
skip_proxy_sys_private_check: true
enable_strict_kernel_release: false

完成后保存为distributed-with-obproxy-example.yaml

部署OceanBase集群

1
2
obd cluster deploy ob_tpch -c distributed-with-obproxy-example.yaml
obd cluster start ob_tpch

部署完成后,可以如下查看当前的OceanBase集群列表

1
obd cluster list
1
2
3
4
5
6
7
8
[root@ecs-ob-0001 ~]# obd cluster list
+------------------------------------------------------------+
| Cluster List |
+-----------+------------------------------+-----------------+
| Name | Configuration Path | Status (Cached) |
+-----------+------------------------------+-----------------+
| ob_tpch | /root/.obd/cluster/ob_tpch | running |
+-----------+------------------------------+-----------------+

查看某一个cluster

1
obd cluster display ob_tpch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@ecs-ob-0001 ~]# obd cluster display ob_tpch
Get local repositories and plugins ok
Open ssh connection ok
Cluster status check ok
Connect to observer ok
Wait for observer init ok
+-------------------------------------------------+
| observer |
+---------------+---------+------+-------+--------+
| ip | version | port | zone | status |
+---------------+---------+------+-------+--------+
| 192.168.0.12 | 3.1.3 | 2881 | zone1 | active |
| 192.168.0.128 | 3.1.3 | 2881 | zone1 | active |
| 192.168.0.185 | 3.1.3 | 2881 | zone1 | active |
+---------------+---------+------+-------+--------+

Connect to obproxy ok
+-------------------------------------------------+
| obproxy |
+---------------+------+-----------------+--------+
| ip | port | prometheus_port | status |
+---------------+------+-----------------+--------+
| 192.168.0.128 | 2883 | 2884 | active |
+---------------+------+-----------------+--------+

验证

完成部署后就可以通过如下命令进行部署,-h代表OceanBase所在实例的地址,-P代表mysql所使用的端口号,默认为2881

1
obclient -h192.168.0.128 -P2881 -uroot

此时应当能够看到OceanBase的shell,测试一下:

1
2
use oceanbase;
select * from __all_server;

TPCH的使用

创建租户

为了使用TPCH,首先需要创建一个新的租户用于测试:

1
obd cluster tenant create ${cluster_name} -n ${tenant_name}

这种方法其实容易报资源不足的错(一开始在这里卡了半天),因为你并没有划分新的资源单元和资源池,所以最好用root用户连接到sys租户,在mysql shell下创建新的租户。

1
obclient -h192.168.0.128 -P2881 -uroot

OceanBase下一开始只有一个sys租户,有已经分配的资源单元sys_unit_config和资源池sys_pool,创建的新租户无法直接使用,所以我们要创建新的资源单元和资源池。

首先查看当前的剩余资源

1
2
3
4
5
use oceanbase;
select a.zone,concat(a.svr_ip,':',a.svr_port) observer, cpu_total, cpu_assigned, (cpu_total-cpu_assigned) cpu_free, mem_total/1024/1024/1024 mem_total_gb, mem_assigned/1024/1024/1024 mem_assign_gb, (mem_total-mem_assigned)/1024/1024/1024 mem_free_gb
from __all_virtual_server_stat a join __all_server b on (a.svr_ip=b.svr_ip and a.svr_port=b.svr_port)
order by a.zone, a.svr_ip
;

能够得到如下结果,含有free的就是当前还能使用的资源数

1
2
3
4
5
6
7
+-------+--------------------+-----------+--------------+----------+----------------+----------------+----------------+
| zone | observer | cpu_total | cpu_assigned | cpu_free | mem_total_gb | mem_assign_gb | mem_free_gb |
+-------+--------------------+-----------+--------------+----------+----------------+----------------+----------------+
| zone1 | 192.168.0.128:2882 | 6 | 2.5 | 3.5 | 5.000000000000 | 1.250000000000 | 3.750000000000 |
| zone2 | 192.168.0.185:2882 | 6 | 2.5 | 3.5 | 5.000000000000 | 1.250000000000 | 3.750000000000 |
| zone3 | 192.168.0.12:2882 | 6 | 2.5 | 3.5 | 5.000000000000 | 1.250000000000 | 3.750000000000 |
+-------+--------------------+-----------+--------------+----------+----------------+----------------+----------------+

新建资源池时使用的资源单元不能超过当前剩余的资源,比如理论上,现在的cpu_free的值为3.5,那么创建资源池时的最大cpu核心数就不能超过这个值。

然而这种方法还是不准确,如果创建资源池时的最大cpu核心数设成3,仍然会报错,此时需要看一下所有资源单元的配置情况:

1
SELECT * FROM oceanbase.__all_unit_config;

将返回如下结果,这里去掉了前后的一些不相关的列:

1
2
3
4
5
6
----------------+-----------------+---------+---------+------------+------------+
unit_config_id | name | max_cpu | min_cpu | max_memory | min_memory
----------------+-----------------+---------+---------+------------+------------+
1 | sys_unit_config | 5 | 2.5 | 1610612736 | 1342177280 |
----------------+-----------------+---------+---------+------------+------------+
1 row in set (0.001 sec)

这里的max_cpu=5,意味着新建资源池时的max_cpu是不能超过1的(因为cpu_total=6)!上述仍然报错的原因,就是在sys_unit_config中max和min并不一致,上面的assigned是按min计算的。

为了测试效果的最佳化,我们可以把sys_unit_config这边尽量调小,把测试要用的资源单元尽量调大就行。

1
alter resource unit sys_unit_config max_cpu 0.5,min_cpu 0.5,max_memory '0.5G', min_memory '0.5G';

接着依次执行如下操作,分别对应新建资源单元、新建资源池、创建租户。创建租户中的primary_zone在TPCH测试中通常设为RANDOM

1
2
3
create resource unit tpch_unit max_cpu 5.5, max_memory '4G', max_iops 128, max_disk_size '20G', max_session_num 64, MIN_CPU=5.5, MIN_MEMORY '4G', MIN_IOPS=128;
create resource pool tpch_pool unit = 'tpch_unit', unit_num = 1, zone_list=('zone1','zone2','zone3');
create tenant tpch_mysql resource_pool_list=('tpch_pool'), charset=utf8mb4, replica_num=3, zone_list('zone1', 'zone2', 'zone3'), primary_zone=RANDOM, locality='F@zone1,F@zone2,F@zone3' set variables ob_compatibility_mode='mysql', ob_tcp_invited_nodes='%';

这样,测试用的租户tpch_mysql就创建成功了。

OBD一键测试

OBDeployer可以进行一键测试,首先安装相应依赖:

1
2
3
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://mirrors.aliyun.com/oceanbase/OceanBase.repo
sudo yum install obtpch

接着创建如下软链接:

1
sudo ln -s /usr/tpc-h-tools/tpc-h-tools/ /usr/local/

然后就可以一键测试:

1
obd test tpch ob_tpch --tenant=tpch_mysql -s 10 --remote-tbl-dir=/tmp/tpch_test

-s后的参数表示生成数据库的大小,单位为GB,进行测试的过程不需要其他操作。

10G结果如下:

Query Time/s
1 9.1
2 0.4
3 7.9
4 28.1
5 8.4
6 5.6
7 8.1
8 28.8
9 11.0
10 27.7
11 1.1
12 11.7
13 3.1
14 18.3
15 47.7
16 1.6
17 6.4
18 5.5
19 6.4
20 6.8
21 22.3
22 2.1
total 268.1

参考

https://open.oceanbase.com/docs/observer-cn/V3.1.3

https://blog.51cto.com/hbxztc/4742364

https://blog.csdn.net/u010674953/article/details/117701938

https://blog.csdn.net/xiefp/article/details/41442523

https://ask.oceanbase.com/t/topic/35600048

https://www.modb.pro/db/397718

Scalable Image-based Indoor Scene Rendering with Reflections

最近准备计算机图形学这门课的pre,准备讲这篇文章,读的时候顺便做了笔记

2022.06.16 Update:报告写完了,原理部分按照报告基本重写了一遍

Intro

基于图片的渲染(IBR)所面临的困难:

  1. 室内场景下物体与相机的距离近,有严重的遮挡和视差问题
  2. 有很多依赖于观察视角的效应需要考虑,例如有很多反射表面

层级表示的方法可以用于解决这类问题,将每个场景做为一层,每层对应一个系数,合成图片可以通过混合这些图层。不过存在占用存储较高的问题。

深度学习的方法:隐式建模,但是计算量大,实时性差。

本文的motivation基于两个观察:

  1. 当前对于室内场景的建模能力有较大提升,因此可以通过使用室内场景的全局建模,得到反射表面的建模作为先验,在reflection decomposition中提高鲁棒性
  2. 高频反射的渲染相比散射的渲染,需要更高的采样率,这样合成高分辨率渲染图的时候,拼接的每张图需要更多的重叠,会大大提高所需存储空间

对应的创新点:

  1. geometry mesh分为两层:
    1. surface mesh:用于表示RGB和建模信息
    2. reflection mesh:用于表示surface mesh产生的反射
  2. 使用一个基于CNN的超分网络来输出高分辨率渲染图,这样可以有效节省存储占用

Overview

本文提出的IBR pipeline分为两阶段:

  1. View Warping
    1. Per-view Two-layer Mesh Construction: 在得到室内场景的整体的网格之后,每个视角建立两层网格,图片作为网格上的纹理
    2. View Warping Using Two-layer Mesh Representation: 在一个需要进行渲染的视角,选择与其接近的若干视角,合成每个视角的图片,得到一张低分辨率的渲染图
  2. DSRNet: 使用超分网络得到高分辨率的渲染图

Preprocessing

预处理阶段将利用预先建立的全局网格$G$,在每个视角中重新建立两层网格表示,因此可以分为全局网格建立、表层网格建立和反射分解等三个步骤。

Global Mesh Construction

本文为了重建整个室内场景的全局网格,同时利用了数码相机的照片和使用Microsoft Kinect4拍摄的RGBD图像。整个采集流程可概括如下:首先使用RealityCapture软件建立初步的全局网格,利用拍摄采集的数百张图片,更加精确地计算相机位姿参数,从而加速三维重建;然后拍摄少量RGBD图片序列,每个序列可以通过KinectFusion算法得到一个融合的网格,将融合网格和RealityCapture建立的全局网格使用迭代最近点算法进行对齐,这样就得到了最终的全局网格。

Surface Layer Construction

完成全局网格的建立之后,对于每个视角所拍的图片,需要进行图片的表层网格的建立。由于每张图片都有对应的相机位姿参数,我们只需在全局网格中用上对应的相机位参,就能得到一个初步的深度图表示,然后用深度图完成表层网格的建立。

由于全局网格不一定很精细,这样得到的深度图的边缘与对应的RGB图像相比可能参差不齐。为了解决上述的深度图误差问题,本文对深度图首先进行边缘探测,将可能作为边缘的像素提取出来,然后对粗糙的边缘进行插值细化,使其相对平整的同时,贴近真实的RGB图像中的边缘。如下图所示,(a)和(b)分别是深度图和法向图的可视化效果,(c)和(d)将深度图的边缘部分用黄绿色描了出来,可以看出与RGB图像中平整的边缘不同,深度图的边缘比较粗糙、不平滑,(e)是对深度图边缘进行细化后的结果,与原来相比平滑一些,(f)是最终建立的表层网格效果。

完成边缘细化后,本文将每个像素初始化为两个三角形,通过一系列简化和合并的方法,迭代生成对应的表层网格。在简化过程中,为了保留深度图中的边缘,本文提高了靠近这些边缘部分的网格的误差权重。

Reflection Decomposition

对于某个视角下的一张室内场景图片$I_k$,本文假设其可进行如下分解,即分解为不含光反射的表面层和光反射层的线性组合:

其中$u$代表$I_k$中的任一像素,$I_k^0$和$I_k^1$分别代表不含反射效果的表面层和仅含光反射效果的反射层,$\beta_k$是一个用于表示像素是否为反射面的一个掩膜。用$M_k^0$和$M_k^1$分别代表表面层和反射层的网格,那么反射分解的目的就是根据$I_k$估计出$I_k^0$、$I_k^1$、$M_k^0$和$M_k^1$。为了简便,用$I_k^{0,1}$同时指代$I_k^0$和$I_k^1$,$M_k^{0,1}$同理。

反射分解的流程图如下所示,大致流程为通过给定的参考图片、相邻视角的若干图片和全局网格,通过反射探测和三维空间探测,首先得到$\beta_k$和$M_k^{0,1}$的一个初步估计作为初始值,接下来通过优化不同的目标函数并迭代数次,得到最终的反射分解输出。

本文提出的反射分解算法中,首先将$\beta_k$和$M_k^{0,1}$进行初始化。接着定义$\mathcal{N}_k$为与$I_k$相邻的图片集合,这一步通过计算其他图片的两层网格$M_k^{0,1}$,并比较与本图的两层网格的重叠程度,选取重叠程度超过30%且重叠程度最高的六张图片作为相邻集合。

同时,本文还利用了给定图片与相邻图片像素之间的光流$\omega_{k^\prime}(u, M_k^0)$,其计算可以通过在$M_k^0$上用$I_{k^\prime}$的相机位参渲染来实现。

为了细化光流,本文用一个非严格的二维扭曲函数$F_{k^\prime}$来描述光流因视角变换而产生的扭曲情况,$F_{k^\prime}$被定义在一个二维的$40\times 30$大小的网格上,网格共有$41\times 31$个控制点,每个控制点对应一个位移向量$f_{k^\prime}^l$。在某个像素$u$上的扭曲函数$F_{k^\prime}$可以如下定义:

其中$ \theta_{k^\prime}^l$是使用控制点进行双线性插值中的权重,控制点所在网格需包含给定像素$u$。有了上述定义,我们可以使用完整的光流$F_{k^\prime}\left( \omega_{k^\prime}\left(u, M_k^0\right) \right)$来聚合相邻视角的信息。

位移向量$f_{k^\prime}^l$可通过最小化下述目标函数得到:

其中$w_r=1.2, w_a=0.5$。目标函数的三项分别保证选定图片与相邻图片的颜色一致性、变形过程的正则化,以及ARAP(As-rigid-as-possible)正则化条件,第三项的计算需要将每个网格拆分成两个三角形,$v_i^t$代表每个三角形的三个顶点。优化过程通过高斯-牛顿法迭代进行。

最终,给定$\mathcal{N}_k$和$\omega_{k^\prime}(u, M_k^0)$,我们可以如下初始化$I_k^0$:

然后通过$I_k^1=I_k-I_k^0$,即可初始化$I_k^1$。

本文通过优化下述目标函数完成对$I_k^{0,1}$和$M_k^{0,1}$的估计:

其中$(R, T)_k^1$代表$M_k^1$的旋转和平移变换。整个目标函数有4项,分别代表数据项、光滑项、先验项和多视角一致项。

数据项计算的是多视角合成的结果$\tilde{I}_{k^\prime}$和拍摄的结果$I_{k^\prime}$之间的误差,其中$\omega_{k^\prime}^{-1}$代表扭曲变换的反变换。

光滑项用于最小化$I_k^{0, 1}$的梯度和$M_k^{0, 1}$的平均曲率法向量,其中$v$是网格$M_k^{0, 1}$上的顶点,H是使用余切权重计算得到的拉普拉斯矩阵。

先验项用于保证$I_k^{0, 1}$的正则化,使优化过程平稳:

两层分解的目标函数优化过程中,多视角一致项的系数$\lambda_{mv}$一开始先设为0,首先优化前三项。优化过程分为交替优化$I_k^{0, 1}$和$\left\{ (R, T)_k^1, M_k^{0, 1} \right\}$两步,每步固定其中一方优化另一方,如此交替迭代直到收敛,然后再将$\lambda_{mv}$调为0.05,在多视角一致性方面进行优化。

多视角一致项直观上可以描述合成结果和实际拍摄结果之间的误差:

实际优化过程中,本文为了确保鲁棒性,按照如下方式计算$\tilde{I}_k^0(u)$,其中用到的结果都是上一步分解过程得到的:

除此以外,反射中的两种特殊情况(高光和镜面反射)需要单独考虑。高光情形下像素亮度趋于饱和,难以用分解假设进行建模,实际优化时会提取出这些高光像素,不参与到数据项的计算中;对于镜面部分的像素,本文设为$I_k^0 = 0$,$I_k ^1 = I_k$。

Online Rendering

实时渲染阶段需要在产生新视角(例如相机移动)的过程中,实时渲染出新画面,该过程需要同时兼顾实时性和相对高的渲染质量。本文的渲染相关步骤主要包括两个阶段:视角转换和超采样,视角转换的目标是利用已采集的视角合成新视角下的渲染画面,超采样将低分辨率的渲染画面细化为高分辨率的输出。本节主要概括视角转换阶段的一些具体步骤。

我理解的大致流程:给定新视角$V_n$->View Selection选出一系列视角$V_k$->生成$V_n$对应的FMDP,同时计算$I_k$在$V_n$下的view warping结果->进行加权view blending->使用$I_n = I_n^0 + \beta_nI_n^1$进行合成。

View Selection

定义某个已有视角$V_k$和新视角$V_n$之间的距离$d_k$如下:

其中$R_k^z, R_n^z$分别代表$V_k, V_n$视角z轴方向(垂直于画面),$t_k, t_n$分别代表两个视角的光学中心,$\angle(R_k^z, R_n^z)$代表两条视角轴的夹角。

如此定义有两个优点,第一个是从视角距离的定义可以看出,当两个视角光学轴之间的角度越大或光学中心距离越大,视角距离就越大,说明视角距离能够刻画两个不同视角之间的连贯程度;第二个是当不存在视角围绕光学轴的旋转时,仅用光学轴方向就能够很好地表示相机朝向,较为简便。

在view selection的过程中,会分别搜索$V_n$为原点的空间坐标系的八个卦限中分别寻找,每个卦限又按照角度和不同角度和不同光学中心距离分成9个区域,在每个区域中寻找距离最近的视角。理论上有72个视角,由于距离条件等限制,实际能选到的视角数在20个左右。

FDMP Generation for Fuzzy Depth Test

得到了新视角对应的若干参考视角后,本文通过渲染参考视角中的网格,生成前端深度图(Front-most Depth Map, FDMP),FMDP用于完成模糊深度测试(Fuzzy Depth Test),即探测某个空间中的点是否能在新视角中显示出来,类似于消隐过程。如果某个像素的深度与FMDP中对应位置的深度的绝对误差在一定范围内,那么认为这个点在$V_n$下是可见的,可见点将用于后续的视角混合步骤。

然而,建模误差(Floating Geometry)的存在会较为明显地影响FDMP的质量,导致模糊深度测试结果的不精确。本文利用像素的IBR-cost来减少建模误差带来的影响。对于$V_n$,$V_k$以及三维空间中任一点$x$,定义它们之间的IBR-cost如下:

在FDMP生成过程中,对于空间中能够投影到$V_n$同一像素上的所有点,本文计算出这些点对应的最小IBR-cost,然后仅保留与最小IBR-cost的差值在一定阈值范围内的点,这些点中深度最小的将被选入FDMP。

IBR-cost对建模误差的体现如下图所示,在新视角(红色三角形)处可以选择蓝色或橙色视角,蓝色视角和橙色视角的深度边缘不一致导致了蓝色视角的建模误差。可以看出,蓝色视角与新视角角度较大,对应的IBR-cost较高,而橙色视角的角度较小,对应的IBR-cost较低,采用IBR-cost较低的橙色视角,选择其对应的橙色点作为FDMP,更加符合真实深度边缘的情形,这说明了IBR-cost确实能够减小建模误差的影响。右边两图分别为不引入和引入IBR-cost的合成效果,明显后者更为平滑自然。

View Blending

视角转换的最后一步是将$V_n$的参考视角中的建模网格和表面层、反射层图片进行融合。在融合之前,通过模糊深度测试移除隐藏的三角形,然后采用如下定义的权重将每个参考视角$V_k$进行融合,其中$\delta=0.033$是实验中使用的数值。

为了避免融合结果产生不连续,本文在融合时对图片边缘进行羽化操作,使得图片拼接的效果更为自然连续。羽化操作即针对深度图边缘的像素进行权值衰减操作,在图片边缘将权重$w_k$光滑地降到0,因为参考视角中距离深度图边缘较远的像素会对最终渲染效果产生较大的贡献,而距离较近的像素可能会变成融合过程中的噪声,所以针对边缘部分的权值衰减能够抑制边缘的不连续。

实验过程中,权值作为alpha通道进行存储。下图(a)展示了边缘权值衰减的影响,能够看出经过权值衰减的视角融合有更自然的效果。

融合过程中难免产生一些细小的空洞部分,本文采用基于块的渲染方法,对这些空洞部分进行填充和渲染,填充过程如下图(b)所示。

Postprocessing

后处理部分将视角转换渲染得到的低分辨率图像进行超分辨率处理,得到最终的高质量渲染图像,超分步骤同样有助于消除渲染结果中粗糙、不自然的部分的影响。

SR Network Architecture

本文提出的DSRNet结构基本类似于RSSNet,其核心思想都是保证输出在时间上的连续性,网络结构图如下所示。DSRNet以前一帧的输入图像$I_{n - 1}$,前一帧的输入深度图$D_{n - 1}$,以及当前帧的输入图像$I_n$和深度图$D_n$作为输入,得到超分后的输出$I_n^f$。DSRNet的网络结构中同时有两个特征提取塔,分别对当前帧和前一帧进行特征提取,将二者的特征融合之后送入下一级的U-Net,从而产生最终输出$I_n^f$。特征提取塔和U-Net的结构均保持与RSSNet一致。

提升DSRNet输出质量还需要依靠DSRNet中的运动向量校正(Motion Vector Rectification, MVR)模块。运动场(Motion Field)$M_r$通过如下方式计算:

其中$M_d$代表利用$D_n$和$D_{n - 1}$计算出来的运动场,$\odot$代表运动场对图片进行变形操作。DSRNet会计算出前一帧和当前帧之间的运动场$M_r$,用$M_r$对前一帧$I_{n - 1}$进行变换,得到变换后的结果$M_r\odot I_{n - 1}$,特征提取塔提取的分别是$I_n$和$M_r\odot I_{n - 1}$的特征,在特征融合过程中会对后者的特征加权。

SR Network Training

训练DSRNet的损失函数定义如下所示:

其中$I^f, I^{gt}$分别为网络输出和真实值,SSIM为两张图片之间的结构相似度。

在MVR模块的训练过程中,定义如下的损失函数:

其中$L1(\cdot)$表示L1误差,$G(\cdot)$表示一个$5\times 5$大小的高斯卷积核,起到平滑局部梯度的作用。

Experiments

Performance

作者在i7-7700k+2080Ti台式机环境下搭建pipeline,渲染一张1280*960的图片的平均耗时49.1ms,包含30.7ms view warping和18.4ms DSRNet inference.

不同场景的信息、图片数和占用存储情况如下表所示。

Reflection Decomposition Algorithm

Reflection decomposition的迭代过程如下所示。

Reflection decomposition的一些结果比较如下所示。

前面原理部分针对高光、镜面的处理做了细致说明,这里也做了比较。

DSRNet

图13~15 表2~3

使用DSRNet进行超分的结果。原来的低分辨率渲染图中的一些模糊、锯齿问题能得到一定结局。

使用MVR与否的对比如下。

不同分辨率网格、图片对结果的影响。

定量的PSNR、SSIM结果比较。

Rendering Results

图16~17 表4

最终效果比较。

不同场景定量比较,提升比较可观。

视频演示

展示了整个pipeline的流程,有实时渲染的结果,可以看一下

https://ensemble.clemson.edu/hapi/v1/contents/permalinks/o9XWz8q3/view

Reference

Jiamin Xu, Xiuchao Wu, Zihan Zhu, Qixing Huang, Yin Yang, Hujun Bao, and Weiwei Xu. 2021. Scalable image-based indoor scene rendering with reflections. ACM Trans. Graph. 40, 4, Article 60 (August 2021), 14 pages. https://doi.org/10.1145/3450626.3459849

Hello world

This is a test post for markdown testing.

本文档用于Markdown测试。

Definition of Loss

Image recovering is a series of problems like this: given a degenerated image $B$, which is assumed to be degenerated from a clear latent image $I$ by the following process:

where $k$ denotes degenerate kernel, $\otimes$ denotes convolution operation, and $N$ denotes additive noise.

We define the following loss function for image recovering task:

Where $\lVert\cdot\rVert_2$ denotes L2 norm of all elements, $\Psi(x)$ denotes regularization term and $\mu$ is a coefficient of the term. Recovering $k$ and $I$ blindly is ill-posed , so some priors for a clear image, e.g. sparsity of gradients, are needed in terms of regularization terms in the loss function.

For example, if we use gradient sparsity prior for image recovering, the regularization term should be

Where $\nabla$ denotes gradient operator, and $\lVert\cdot \rVert_0$ denotes L0 norm, or number of non-zero elements of a vector/matrix. Then image recovering is to solve the following problem:

Variable splitting solver

Since optimizing L0 norm is NP-hard, we need some method to obtain approximate solution. One of the approximate methods is half-quadratic penalty method. An auxiliary variable $A$ is introduced:

where $\gamma$ is a large parameter to enforce $\lVert\nabla I-A\rVert\approx0$ for its solution, thus $A\approx \nabla I$ holds.

Updating method of half-quadratic variable splitting

See this1.

1. https://jiaya.me/papers/deconvmodelsolver_2014.pdf