Skip to content

Commit 9d8fcee

Browse files
authored
Add files via upload
1 parent 1af9243 commit 9d8fcee

31 files changed

+2789
-0
lines changed

dataset/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .dataset_360D import *

dataset/dataset_360D.py

Lines changed: 443 additions & 0 deletions
Large diffs are not rendered by default.

exporters/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .image import *

exporters/image.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import torch
2+
import cv2
3+
import numpy
4+
5+
def save_image(filename, tensor, scale=255.0):
6+
b, _, __, ___ = tensor.size()
7+
for n in range(b):
8+
array = tensor[n, :, :, :].detach().cpu().numpy()
9+
array = array.transpose(1, 2, 0) * scale
10+
cv2.imwrite(filename.replace("#", str(n)), array)
11+
12+
def save_depth(filename, tensor, scale=1000.0):
13+
b, _, __, ___ = tensor.size()
14+
for n in range(b):
15+
array = tensor[n, :, :, :].detach().cpu().numpy()
16+
array = array.transpose(1, 2, 0) * scale
17+
array = numpy.uint16(array)
18+
cv2.imwrite(filename.replace("#", str(n)), array)
19+
20+
def save_data(filename, tensor, scale=1000.0):
21+
b, _, __, ___ = tensor.size()
22+
for n in range(b):
23+
array = tensor[n, :, :, :].detach().cpu().numpy()
24+
array = array.transpose(1, 2, 0) * scale
25+
array = numpy.float32(array)
26+
cv2.imwrite(filename.replace("#", str(n)), array)

filesystem/file_utils.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import os
2+
3+
'''
4+
Filesystem class
5+
provides file control utilities like tensor saving etc.
6+
'''
7+
class Filesystem:
8+
def __init__(self):
9+
self.cwd = os.getcwd()
10+
if os.path.isfile(self.cwd):
11+
self.cwd = os.path.basename(self.cwd)
12+
'''
13+
Creates directory
14+
either by giving the absolute path to create
15+
or the relative path w.r.t. the current working directory
16+
17+
\param path the path to create
18+
'''
19+
def mkdir(self, path):
20+
if os.path.isabs(path):
21+
if not os.path.exists(path):
22+
os.mkdir(path)
23+
else:
24+
pathToCreate = os.path.join(self.cwd, path)
25+
if not os.path.exists(pathToCreate):
26+
os.mkdir(pathToCreate)

models/__init__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from .resnet360 import *
2+
3+
import sys
4+
5+
def get_model(name, model_params):
6+
if name == 'resnet_coord':
7+
return ResNet360(
8+
# conv_type='standard', activation='elu', norm_type='none', \
9+
conv_type='coord', activation='elu', norm_type='none', \
10+
width=512,
11+
)
12+
else:
13+
print("Could not find the requested model ({})".format(name), file=sys.stderr)

models/modules.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import torch
2+
from torch import nn
3+
import torch.nn.functional as F
4+
5+
'''
6+
Code adapted from https://github.com/uber-research/coordconv
7+
accompanying the paper "An Intriguing Failing of Convolutional Neural Networks and the CoordConv Solution" (NeurIPS 2018)
8+
'''
9+
10+
class AddCoords360(nn.Module):
11+
def __init__(self, x_dim=64, y_dim=64, with_r=False):
12+
super(AddCoords360, self).__init__()
13+
self.x_dim = int(x_dim)
14+
self.y_dim = int(y_dim)
15+
self.with_r = with_r
16+
17+
def forward(self, input_tensor):
18+
"""
19+
input_tensor: (batch, c, x_dim, y_dim)
20+
"""
21+
batch_size_tensor = input_tensor.shape[0]
22+
23+
xx_ones = torch.ones([1, self.y_dim], dtype=torch.float32, device=input_tensor.device)
24+
xx_ones = xx_ones.unsqueeze(-1)
25+
26+
xx_range = torch.arange(self.x_dim, dtype=torch.float32, device=input_tensor.device).unsqueeze(0)
27+
xx_range = xx_range.unsqueeze(1)
28+
29+
xx_channel = torch.matmul(xx_ones, xx_range)
30+
xx_channel = xx_channel.unsqueeze(-1)
31+
32+
yy_ones = torch.ones([1, self.x_dim], dtype=torch.float32, device=input_tensor.device)
33+
yy_ones = yy_ones.unsqueeze(1)
34+
35+
yy_range = torch.arange(self.y_dim, dtype=torch.float32, device=input_tensor.device).unsqueeze(0)
36+
yy_range = yy_range.unsqueeze(-1)
37+
38+
yy_channel = torch.matmul(yy_range, yy_ones)
39+
yy_channel = yy_channel.unsqueeze(-1)
40+
41+
xx_channel = xx_channel.permute(0, 3, 2, 1)
42+
yy_channel = yy_channel.permute(0, 3, 2, 1)
43+
44+
xx_channel = xx_channel.float() / (self.x_dim - 1)
45+
yy_channel = yy_channel.float() / (self.y_dim - 1)
46+
47+
xx_channel = xx_channel * 2 - 1
48+
yy_channel = yy_channel * 2 - 1
49+
50+
xx_channel = xx_channel.repeat(batch_size_tensor, 1, 1, 1)
51+
yy_channel = yy_channel.repeat(batch_size_tensor, 1, 1, 1)
52+
53+
ret = torch.cat([input_tensor, xx_channel, yy_channel], dim=1)
54+
55+
if self.with_r:
56+
rr = torch.sqrt(torch.pow(xx_channel - 0.5, 2) + torch.pow(yy_channel - 0.5, 2))
57+
ret = torch.cat([ret, rr], dim=1)
58+
59+
return ret
60+
61+
class CoordConv360(nn.Module):
62+
"""CoordConv layer as in the paper."""
63+
def __init__(self, x_dim, y_dim, with_r, in_channels, out_channels, kernel_size, *args, **kwargs):
64+
super(CoordConv360, self).__init__()
65+
self.addcoords = AddCoords360(x_dim=x_dim, y_dim=y_dim, with_r=with_r)
66+
in_size = in_channels+2
67+
if with_r:
68+
in_size += 1
69+
self.conv = nn.Conv2d(in_size, out_channels, kernel_size, **kwargs)
70+
71+
def forward(self, input_tensor):
72+
ret = self.addcoords(input_tensor)
73+
ret = self.conv(ret)
74+
return ret
75+
76+
77+
def create_conv(in_size, out_size, conv_type, padding=1, stride=1, kernel_size=3, width=512):
78+
if conv_type == 'standard':
79+
return nn.Conv2d(in_channels=in_size, out_channels=out_size, \
80+
kernel_size=kernel_size, padding=padding, stride=stride)
81+
elif conv_type == 'coord':
82+
return CoordConv360(x_dim=width / 2.0, y_dim=width,\
83+
with_r=False, kernel_size=kernel_size, stride=stride,\
84+
in_channels=in_size, out_channels=out_size, padding=padding)
85+
86+
def create_activation(activation):
87+
if activation == 'relu':
88+
return nn.ReLU(inplace=True)
89+
elif activation == 'elu':
90+
return nn.ELU(inplace=True)
91+
92+
class Identity(nn.Module):
93+
def forward(self, x):
94+
return x
95+
96+
def create_normalization(out_size, norm_type):
97+
if norm_type == 'batchnorm':
98+
return nn.BatchNorm2d(out_size)
99+
elif norm_type == 'groupnorm':
100+
return nn.GroupNorm(out_size // 4, out_size)
101+
elif norm_type == 'none':
102+
return Identity()
103+
104+
def create_downscale(out_size, down_mode):
105+
if down_mode == 'pool':
106+
return torch.nn.modules.MaxPool2d(2)
107+
elif down_mode == 'downconv':
108+
return nn.Conv2d(in_channels=out_size, out_channels=out_size, kernel_size=3,\
109+
stride=2, padding=1, bias=False)
110+
elif down_mode == 'gaussian':
111+
print("Not implemented")

models/resnet360.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import torch
2+
import torch.nn as nn
3+
4+
import functools
5+
6+
from .modules import *
7+
8+
# adapted from https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix/blob/master/models/networks.py
9+
10+
class ResNet360(nn.Module):
11+
"""Resnet-based generator that consists of Resnet blocks between a few downsampling/upsampling operations.
12+
13+
We adapt Torch code and idea from Justin Johnson's neural style transfer project(https://github.com/jcjohnson/fast-neural-style)
14+
"""
15+
def __init__(
16+
self,
17+
in_channels=3,
18+
out_channels=1,
19+
depth=5,
20+
wf=32,
21+
conv_type='coord',
22+
padding='kernel',
23+
norm_type='none',
24+
activation='elu',
25+
up_mode='upconv',
26+
down_mode='downconv',
27+
width=512,
28+
use_dropout=False,
29+
padding_type='reflect',
30+
):
31+
"""Construct a Resnet-based generator
32+
33+
Parameters:
34+
input_nc (int) -- the number of channels in input images
35+
output_nc (int) -- the number of channels in output images
36+
ngf (int) -- the number of filters in the last conv layer
37+
norm_layer -- normalization layer
38+
use_dropout (bool) -- if use dropout layers
39+
n_blocks (int) -- the number of ResNet blocks
40+
padding_type (str) -- the name of padding layer in conv layers: reflect | replicate | zero
41+
"""
42+
assert(depth >= 0)
43+
super(ResNet360, self).__init__()
44+
model = (
45+
[
46+
create_conv(in_channels, wf, conv_type, \
47+
kernel_size=7, padding=3, stride=1, width=width),
48+
create_normalization(wf, norm_type),
49+
create_activation(activation)
50+
]
51+
)
52+
53+
n_downsampling = 2
54+
for i in range(n_downsampling):
55+
mult = 2 ** i
56+
model += (
57+
[
58+
create_conv(wf * mult, wf * mult * 2, conv_type, \
59+
kernel_size=3, stride=2, padding=1, width=width // (i+1)),
60+
create_normalization(wf * mult * 2, norm_type),
61+
create_activation(activation)
62+
]
63+
)
64+
65+
mult = 2 ** n_downsampling
66+
for i in range(depth):
67+
model += [ResnetBlock(wf * mult, activation=activation, \
68+
norm_type=norm_type, conv_type=conv_type, \
69+
width=width // (2 ** n_downsampling))]
70+
71+
for i in range(n_downsampling):
72+
mult = 2 ** (n_downsampling - i)
73+
model += (
74+
[
75+
nn.ConvTranspose2d(wf * mult, int(wf * mult / 2),
76+
kernel_size=3, stride=2,
77+
padding=1, output_padding=1),
78+
create_normalization(int(wf * mult / 2), norm_type),
79+
create_activation(activation)
80+
]
81+
)
82+
83+
model += [create_conv(wf, out_channels, conv_type, \
84+
kernel_size=7, padding=3, width=width)]
85+
86+
self.model = nn.Sequential(*model)
87+
88+
def forward(self, input):
89+
"""Standard forward"""
90+
return self.model(input)
91+
92+
93+
class ResnetBlock(nn.Module):
94+
"""Define a Resnet block"""
95+
96+
def __init__(self, dim, norm_type, conv_type, activation, width):
97+
"""Initialize the Resnet block
98+
99+
A resnet block is a conv block with skip connections
100+
We construct a conv block with build_conv_block function,
101+
and implement skip connections in <forward> function.
102+
Original Resnet paper: https://arxiv.org/pdf/1512.03385.pdf
103+
"""
104+
super(ResnetBlock, self).__init__()
105+
conv_block = []
106+
conv_block +=(
107+
[
108+
create_conv(dim, dim, conv_type, width=width),
109+
create_normalization(dim, norm_type),
110+
create_activation(activation),
111+
]
112+
)
113+
conv_block +=(
114+
[
115+
create_conv(dim, dim, conv_type, width=width),
116+
create_normalization(dim, norm_type),
117+
]
118+
)
119+
120+
self.block = nn.Sequential(*conv_block)
121+
122+
def forward(self, x):
123+
"""Forward function (with skip connections)"""
124+
out = x + self.block(x) # add skip connections
125+
return out

spherical/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from .grid import *
2+
from .cartesian import *
3+
from .derivatives import *
4+
from .weights import *

spherical/cartesian.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import torch
2+
3+
from .grid import *
4+
5+
'''
6+
Cartesian coordinates extraction from Spherical coordinates
7+
z is forward axis
8+
y is the up axis
9+
x is the right axis
10+
r is the radius (i.e. spherical depth)
11+
phi is the longitude/azimuthial rotation angle (defined on the x-z plane)
12+
theta is the latitude/elevation rotation angle (defined on the y-z plane)
13+
'''
14+
def coord_x(sgrid, depth):
15+
return ( # r * sin(phi) * sin(theta) -> r * cos(phi) * -cos(theta) in our offsets
16+
depth # this is due to the offsets as explained below
17+
* torch.cos(phi(sgrid)) # long = x - 3 * pi / 2
18+
* -1 * torch.cos(theta(sgrid)) # lat = y - pi / 2
19+
)
20+
21+
def coord_y(sgrid, depth):
22+
return ( # r * cos(theta) -> r * sin(theta) in our offsets
23+
depth # this is due to the offsets as explained below
24+
* torch.sin(theta(sgrid)) # lat = y - pi / 2
25+
)
26+
27+
def coord_z(sgrid, depth):
28+
return ( # r * cos(phi) * sin(theta) -> r * -sin(phi) * -cos(theta) in our offsets
29+
depth # this is due to the offsets as explained above
30+
* torch.sin(phi(sgrid)) # * -1
31+
* torch.cos(theta(sgrid)) # * -1
32+
) # the -1s cancel out
33+
34+
def coords_3d(sgrid, depth):
35+
return torch.cat(
36+
(
37+
coord_x(sgrid, depth),
38+
coord_y(sgrid, depth),
39+
coord_z(sgrid, depth)
40+
), dim=1
41+
)
42+
43+
def xi(pcloud):
44+
return pcloud[:, 0, :, :].unsqueeze(1)
45+
46+
def yi(pcloud):
47+
return pcloud[:, 1, :, :].unsqueeze(1)
48+
49+
def zeta(pcloud):
50+
return pcloud[:, 2, :, :].unsqueeze(1)

0 commit comments

Comments
 (0)