-
Notifications
You must be signed in to change notification settings - Fork 62
/
utils.py
312 lines (243 loc) · 8.71 KB
/
utils.py
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
import errno
import json
import logging
import logging.config
import math
import os
import sys
import time
import numpy as np
import tensorflow as tf
from scipy.linalg import logm, norm
#####################################
##### General utility functions #####
#####################################
class Bunch(object):
def __init__(self, adict):
self.__dict__.update(adict)
class Timer(object):
"""A simple timer."""
def __init__(self):
self.total_time = 0.
self.calls = 0
self.start_time = 0.
self.diff = 0.
self.average_time = 0.
def tic(self):
# using time.time instead of time.clock because time time.clock
# does not normalize for multithreading
self.start_time = time.time()
def toc(self, average=False):
self.diff = time.time() - self.start_time
self.total_time += self.diff
self.calls += 1
self.average_time = self.total_time / self.calls
if average:
return self.average_time
else:
return self.diff
def reset(self):
self.__init__()
def mkdir_p(path):
"""Utility function emulating mkdir -p."""
try:
os.makedirs(path)
except OSError as exc: # Python >2.5
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else:
raise
def pretty_line(s):
print '*' * 5 + ' ' + s + ' ' + '*' * 5
def write_args(args, jsonfile):
dump = dict()
for arg in vars(args):
dump[arg] = getattr(args, arg)
with open(jsonfile, 'w') as dumpfile:
json.dump(dump, dumpfile, indent=4, sort_keys=True)
def init_logging(level="INFO"):
logging.basicConfig(
format='%(asctime)s:%(module)s - %(name)s: %(levelname)s - %(message)s'
)
logger = logging.getLogger('mview3d')
numlevel = getattr(logging, level.upper(), None)
if not isinstance(numlevel, int):
raise ValueError('Invalid log level: %s' % level)
logger.setLevel(numlevel)
return logger
def get_session_config(memfrac=1.0):
config = tf.ConfigProto()
config.gpu_options.allow_growth = True
config.gpu_options.per_process_gpu_memory_fraction = memfrac
return config
###################################################################
##### Utility functions for processing command line arguments #####
###################################################################
def process_args(parser, js=None):
args = parser.parse_args()
argsdict = vars(args)
# First search for argsjs, then args.log else None
if js is None:
if 'argsjs' in argsdict:
js = args.argsjs
elif 'log' in argsdict:
if args.log is not None:
js = os.path.join(args.log, 'args.json')
else:
js = None
else:
js = None
def get_cmd_args():
cmd_arg = {}
for arg in sys.argv[1:]:
for a in parser._actions:
for o in a.option_strings:
if arg.startswith(o):
cmd_arg[a.dest] = argsdict[a.dest]
return cmd_arg
if js is not None:
if not os.path.exists(js):
print('Error: Specified args json file not found at {}'.format(js))
print('Returning default args')
return args
with open(js, 'r') as f:
js = json.load(f)
# Get values specified on cmd line
cmd_args = get_cmd_args()
# Update values from json file
argsdict.update(js)
# Keep values specified in command line args
argsdict.update(cmd_args)
# Return namespace object
args = Bunch(argsdict)
return args
##################################################
##### Utility function for rotation matrices #####
##################################################
def quat2rot(q):
'''q = [w, x, y, z]
https://en.wikipedia.org/wiki/Rotation_matrix#Quaternion'''
eps = 1e-5
w, x, y, z = q
n = np.linalg.norm(q)
s = (0 if n < eps else 2.0 / n)
wx = s * w * x
wy = s * w * y
wz = s * w * z
xx = s * x * x
xy = s * x * y
xz = s * x * z
yy = s * y * y
yz = s * y * z
zz = s * z * z
R = np.array([[1 - (yy + zz), xy - wz,
xz + wy], [xy + wz, 1 - (xx + zz), yz - wx],
[xz - wy, yz + wx, 1 - (xx + yy)]])
return R
def rot2quat(M):
if M.shape[0] < 4 or M.shape[1] < 4:
newM = np.zeros((4, 4))
newM[:3, :3] = M[:3, :3]
newM[3, 3] = 1
M = newM
q = np.empty((4, ))
t = np.trace(M)
if t > M[3, 3]:
q[0] = t
q[3] = M[1, 0] - M[0, 1]
q[2] = M[0, 2] - M[2, 0]
q[1] = M[2, 1] - M[1, 2]
else:
i, j, k = 0, 1, 2
if M[1, 1] > M[0, 0]:
i, j, k = 1, 2, 0
if M[2, 2] > M[i, i]:
i, j, k = 2, 0, 1
t = M[i, i] - (M[j, j] + M[k, k]) + M[3, 3]
q[i] = t
q[j] = M[i, j] + M[j, i]
q[k] = M[k, i] + M[i, k]
q[3] = M[k, j] - M[j, k]
q = q[[3, 0, 1, 2]]
q *= 0.5 / math.sqrt(t * M[3, 3])
return q
def euler_to_rot(theta):
R_x = np.array([[1, 0, 0], [0, math.cos(theta[0]), -math.sin(theta[0])],
[0, math.sin(theta[0]),
math.cos(theta[0])]])
R_y = np.array([[math.cos(theta[1]), 0,
math.sin(theta[1])], [0, 1, 0],
[-math.sin(theta[1]), 0,
math.cos(theta[1])]])
R_z = np.array([[math.cos(theta[2]), -math.sin(theta[2]), 0],
[math.sin(theta[2]),
math.cos(theta[2]), 0], [0, 0, 1]])
R = np.dot(R_z, np.dot(R_y, R_x))
return R
def az_el_to_rot(az, el):
corr_mat = np.array([[0, 0, -1], [1, 0, 0], [0, -1, 0]])
inv_corr_mat = np.linalg.inv(corr_mat)
def R_x(theta):
return np.array([[1, 0, 0], [0, math.cos(theta),
math.sin(theta)],
[0, -math.sin(theta),
math.cos(theta)]])
def R_y(theta):
return np.array([[math.cos(theta), 0, -math.sin(theta)], [0, 1, 0],
[math.sin(theta), 0,
math.cos(theta)]])
def R_z(theta):
return np.array([[math.cos(theta), -math.sin(theta), 0],
[math.sin(theta), math.cos(theta), 0], [0, 0, 1]])
Rmat = np.matmul(R_x(-el * math.pi / 180), R_y(-az * math.pi / 180))
return np.matmul(Rmat, inv_corr_mat)
def rand_rotation_matrix(deflection=1.0, randnums=None):
"""
Creates a random rotation matrix.
deflection: the magnitude of the rotation. For 0, no rotation; for 1,
competely random rotation. Small deflection => small perturbation.
randnums: 3 random numbers in the range [0, 1]. If `None`,
they will be auto-generated.
"""
# from
# http://www.realtimerendering.com/resources/GraphicsGems/gemsiii/rand_rotation.c
if randnums is None:
randnums = np.random.uniform(size=(3, ))
theta, phi, z = randnums
theta = theta * 2.0 * deflection * np.pi # Rotation about the pole (Z).
phi = phi * 2.0 * np.pi # For direction of pole deflection.
z = z * 2.0 * deflection # For magnitude of pole deflection.
# Compute a vector V used for distributing points over the sphere
# via the reflection I - V Transpose(V). This formulation of V
# will guarantee that if x[1] and x[2] are uniformly distributed,
# the reflected points will be uniform on the sphere. Note that V
# has length sqrt(2) to eliminate the 2 in the Householder matrix.
r = np.sqrt(z)
Vx, Vy, Vz = V = (np.sin(phi) * r, np.cos(phi) * r, np.sqrt(2.0 - z))
st = np.sin(theta)
ct = np.cos(theta)
R = np.array(((ct, st, 0), (-st, ct, 0), (0, 0, 1)))
# Construct the rotation matrix ( V Transpose(V) - I ) R.
M = (np.outer(V, V) - np.eye(3)).dot(R)
reflM = np.array([[-1, 0, 0], [0, -1, 0], [0, 0, 1]])
return M.dot(reflM.T)
def rand_euler_rotation_matrix(nmax=10):
euler = (np.random.uniform(size=(3, )) - 0.5) * nmax * 2 * math.pi / 360.0
Rmat = euler_to_rot(euler)
return Rmat, euler * 180 / math.pi
def rot_mag(R):
angle = (1.0 / math.sqrt(2)) * \
norm(logm(R), 'fro') * 180 / (math.pi)
return angle
def add_noise(cams, nmax=10):
noises = []
rot_noise = []
for bx in range(cams.shape[0]):
item_max_noise = float(nmax)
for ix in range(cams.shape[1]):
rand_rot, euler = rand_euler_rotation_matrix(item_max_noise)
noises.append(euler)
rot_noise.append(rand_rot)
R_noisy = np.matmul(cams[bx, ix, :, :3], rand_rot)
cams[bx, ix, :, :3] = R_noisy
return cams, rot_noise, noises