Reformat $EVERYTHING
This commit is contained in:
+2
-1
@@ -7,8 +7,9 @@
|
||||
# set matplotlib backend depending on env
|
||||
import os
|
||||
import matplotlib
|
||||
|
||||
if os.name == 'posix' and "DISPLAY" not in os.environ:
|
||||
matplotlib.use('Agg')
|
||||
matplotlib.use('Agg')
|
||||
|
||||
from . import geometry
|
||||
from . import plt
|
||||
|
||||
+5
-8
@@ -12,14 +12,14 @@ def parse_args():
|
||||
parser.add_argument('--loss',
|
||||
help='Train with \'ph\' for the first stage without geometric loss, \
|
||||
train with \'phge\' for the second stage with geometric loss',
|
||||
default='ph', choices=['ph','phge'], type=str)
|
||||
default='ph', choices=['ph', 'phge'], type=str)
|
||||
parser.add_argument('--data_type',
|
||||
default='syn', choices=['syn'], type=str)
|
||||
#
|
||||
parser.add_argument('--cmd',
|
||||
help='Start training or test',
|
||||
parser.add_argument('--cmd',
|
||||
help='Start training or test',
|
||||
default='resume', choices=['retrain', 'resume', 'retest', 'test_init'], type=str)
|
||||
parser.add_argument('--epoch',
|
||||
parser.add_argument('--epoch',
|
||||
help='If larger than -1, retest on the specified epoch',
|
||||
default=-1, type=int)
|
||||
parser.add_argument('--epochs',
|
||||
@@ -55,7 +55,7 @@ def parse_args():
|
||||
parser.add_argument('--blend_im',
|
||||
help='Parameter for adding texture',
|
||||
default=0.6, type=float)
|
||||
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
args.exp_name = get_exp_name(args)
|
||||
@@ -66,6 +66,3 @@ def parse_args():
|
||||
def get_exp_name(args):
|
||||
name = f"exp_{args.data_type}"
|
||||
return name
|
||||
|
||||
|
||||
|
||||
|
||||
+33
-28
@@ -1,19 +1,20 @@
|
||||
import numpy as np
|
||||
|
||||
_color_map_errors = np.array([
|
||||
[149, 54, 49], #0: log2(x) = -infinity
|
||||
[180, 117, 69], #0.0625: log2(x) = -4
|
||||
[209, 173, 116], #0.125: log2(x) = -3
|
||||
[233, 217, 171], #0.25: log2(x) = -2
|
||||
[248, 243, 224], #0.5: log2(x) = -1
|
||||
[144, 224, 254], #1.0: log2(x) = 0
|
||||
[97, 174, 253], #2.0: log2(x) = 1
|
||||
[67, 109, 244], #4.0: log2(x) = 2
|
||||
[39, 48, 215], #8.0: log2(x) = 3
|
||||
[38, 0, 165], #16.0: log2(x) = 4
|
||||
[38, 0, 165] #inf: log2(x) = inf
|
||||
[149, 54, 49], # 0: log2(x) = -infinity
|
||||
[180, 117, 69], # 0.0625: log2(x) = -4
|
||||
[209, 173, 116], # 0.125: log2(x) = -3
|
||||
[233, 217, 171], # 0.25: log2(x) = -2
|
||||
[248, 243, 224], # 0.5: log2(x) = -1
|
||||
[144, 224, 254], # 1.0: log2(x) = 0
|
||||
[97, 174, 253], # 2.0: log2(x) = 1
|
||||
[67, 109, 244], # 4.0: log2(x) = 2
|
||||
[39, 48, 215], # 8.0: log2(x) = 3
|
||||
[38, 0, 165], # 16.0: log2(x) = 4
|
||||
[38, 0, 165] # inf: log2(x) = inf
|
||||
]).astype(float)
|
||||
|
||||
|
||||
def color_error_image(errors, scale=1, mask=None, BGR=True):
|
||||
"""
|
||||
Color an input error map.
|
||||
@@ -27,31 +28,33 @@ def color_error_image(errors, scale=1, mask=None, BGR=True):
|
||||
Returns:
|
||||
colored_errors -- HxWx3 numpy array visualizing the errors
|
||||
"""
|
||||
|
||||
|
||||
errors_flat = errors.flatten()
|
||||
errors_color_indices = np.clip(np.log2(errors_flat / scale + 1e-5) + 5, 0, 9)
|
||||
i0 = np.floor(errors_color_indices).astype(int)
|
||||
f1 = errors_color_indices - i0.astype(float)
|
||||
colored_errors_flat = _color_map_errors[i0, :] * (1-f1).reshape(-1,1) + _color_map_errors[i0+1, :] * f1.reshape(-1,1)
|
||||
colored_errors_flat = _color_map_errors[i0, :] * (1 - f1).reshape(-1, 1) + _color_map_errors[i0 + 1,
|
||||
:] * f1.reshape(-1, 1)
|
||||
|
||||
if mask is not None:
|
||||
colored_errors_flat[mask.flatten() == 0] = 255
|
||||
|
||||
if not BGR:
|
||||
colored_errors_flat = colored_errors_flat[:,[2,1,0]]
|
||||
colored_errors_flat = colored_errors_flat[:, [2, 1, 0]]
|
||||
|
||||
return colored_errors_flat.reshape(errors.shape[0], errors.shape[1], 3).astype(np.int)
|
||||
|
||||
|
||||
_color_map_depths = np.array([
|
||||
[0, 0, 0], # 0.000
|
||||
[0, 0, 255], # 0.114
|
||||
[255, 0, 0], # 0.299
|
||||
[255, 0, 255], # 0.413
|
||||
[0, 255, 0], # 0.587
|
||||
[0, 255, 255], # 0.701
|
||||
[255, 255, 0], # 0.886
|
||||
[255, 255, 255], # 1.000
|
||||
[255, 255, 255], # 1.000
|
||||
[0, 0, 0], # 0.000
|
||||
[0, 0, 255], # 0.114
|
||||
[255, 0, 0], # 0.299
|
||||
[255, 0, 255], # 0.413
|
||||
[0, 255, 0], # 0.587
|
||||
[0, 255, 255], # 0.701
|
||||
[255, 255, 0], # 0.886
|
||||
[255, 255, 255], # 1.000
|
||||
[255, 255, 255], # 1.000
|
||||
]).astype(float)
|
||||
_color_map_bincenters = np.array([
|
||||
0.0,
|
||||
@@ -62,9 +65,10 @@ _color_map_bincenters = np.array([
|
||||
0.701,
|
||||
0.886,
|
||||
1.000,
|
||||
2.000, # doesn't make a difference, just strictly higher than 1
|
||||
2.000, # doesn't make a difference, just strictly higher than 1
|
||||
])
|
||||
|
||||
|
||||
def color_depth_map(depths, scale=None):
|
||||
"""
|
||||
Color an input depth map.
|
||||
@@ -82,12 +86,13 @@ def color_depth_map(depths, scale=None):
|
||||
|
||||
values = np.clip(depths.flatten() / scale, 0, 1)
|
||||
# for each value, figure out where they fit in in the bincenters: what is the last bincenter smaller than this value?
|
||||
lower_bin = ((values.reshape(-1, 1) >= _color_map_bincenters.reshape(1,-1)) * np.arange(0,9)).max(axis=1)
|
||||
lower_bin = ((values.reshape(-1, 1) >= _color_map_bincenters.reshape(1, -1)) * np.arange(0, 9)).max(axis=1)
|
||||
lower_bin_value = _color_map_bincenters[lower_bin]
|
||||
higher_bin_value = _color_map_bincenters[lower_bin + 1]
|
||||
alphas = (values - lower_bin_value) / (higher_bin_value - lower_bin_value)
|
||||
colors = _color_map_depths[lower_bin] * (1-alphas).reshape(-1,1) + _color_map_depths[lower_bin + 1] * alphas.reshape(-1,1)
|
||||
colors = _color_map_depths[lower_bin] * (1 - alphas).reshape(-1, 1) + _color_map_depths[
|
||||
lower_bin + 1] * alphas.reshape(-1, 1)
|
||||
return colors.reshape(depths.shape[0], depths.shape[1], 3).astype(np.uint8)
|
||||
|
||||
#from utils.debug import save_color_numpy
|
||||
#save_color_numpy(color_depth_map(np.matmul(np.ones((100,1)), np.arange(0,1200).reshape(1,1200)), scale=1000))
|
||||
# from utils.debug import save_color_numpy
|
||||
# save_color_numpy(color_depth_map(np.matmul(np.ones((100,1)), np.arange(0,1200).reshape(1,1200)), scale=1000))
|
||||
|
||||
+676
-626
File diff suppressed because it is too large
Load Diff
+24
-18
@@ -2,31 +2,37 @@ import numpy as np
|
||||
|
||||
from . import utils
|
||||
|
||||
|
||||
class StopWatch(utils.StopWatch):
|
||||
def __del__(self):
|
||||
print('='*80)
|
||||
print('gtimer:')
|
||||
total = ', '.join(['%s: %f[s]' % (k,v) for k,v in self.get(reduce=np.sum).items()])
|
||||
print(f' [total] {total}')
|
||||
mean = ', '.join(['%s: %f[s]' % (k,v) for k,v in self.get(reduce=np.mean).items()])
|
||||
print(f' [mean] {mean}')
|
||||
median = ', '.join(['%s: %f[s]' % (k,v) for k,v in self.get(reduce=np.median).items()])
|
||||
print(f' [median] {median}')
|
||||
print('='*80)
|
||||
def __del__(self):
|
||||
print('=' * 80)
|
||||
print('gtimer:')
|
||||
total = ', '.join(['%s: %f[s]' % (k, v) for k, v in self.get(reduce=np.sum).items()])
|
||||
print(f' [total] {total}')
|
||||
mean = ', '.join(['%s: %f[s]' % (k, v) for k, v in self.get(reduce=np.mean).items()])
|
||||
print(f' [mean] {mean}')
|
||||
median = ', '.join(['%s: %f[s]' % (k, v) for k, v in self.get(reduce=np.median).items()])
|
||||
print(f' [median] {median}')
|
||||
print('=' * 80)
|
||||
|
||||
|
||||
GTIMER = StopWatch()
|
||||
|
||||
|
||||
def start(name):
|
||||
GTIMER.start(name)
|
||||
GTIMER.start(name)
|
||||
|
||||
|
||||
def stop(name):
|
||||
GTIMER.stop(name)
|
||||
GTIMER.stop(name)
|
||||
|
||||
|
||||
class Ctx(object):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __enter__(self):
|
||||
start(self.name)
|
||||
def __enter__(self):
|
||||
start(self.name)
|
||||
|
||||
def __exit__(self, *args):
|
||||
stop(self.name)
|
||||
def __exit__(self, *args):
|
||||
stop(self.name)
|
||||
|
||||
+242
-235
@@ -2,266 +2,273 @@ import struct
|
||||
import numpy as np
|
||||
import collections
|
||||
|
||||
def _write_ply_point(fp, x,y,z, color=None, normal=None, binary=False):
|
||||
args = [x,y,z]
|
||||
if color is not None:
|
||||
args += [int(color[0]), int(color[1]), int(color[2])]
|
||||
if normal is not None:
|
||||
args += [normal[0],normal[1],normal[2]]
|
||||
if binary:
|
||||
fmt = '<fff'
|
||||
if color is not None:
|
||||
fmt = fmt + 'BBB'
|
||||
if normal is not None:
|
||||
fmt = fmt + 'fff'
|
||||
fp.write(struct.pack(fmt, *args))
|
||||
else:
|
||||
fmt = '%f %f %f'
|
||||
if color is not None:
|
||||
fmt = fmt + ' %d %d %d'
|
||||
if normal is not None:
|
||||
fmt = fmt + ' %f %f %f'
|
||||
fmt += '\n'
|
||||
fp.write(fmt % tuple(args))
|
||||
|
||||
def _write_ply_triangle(fp, i0,i1,i2, binary):
|
||||
if binary:
|
||||
fp.write(struct.pack('<Biii', 3,i0,i1,i2))
|
||||
else:
|
||||
fp.write('3 %d %d %d\n' % (i0,i1,i2))
|
||||
def _write_ply_point(fp, x, y, z, color=None, normal=None, binary=False):
|
||||
args = [x, y, z]
|
||||
if color is not None:
|
||||
args += [int(color[0]), int(color[1]), int(color[2])]
|
||||
if normal is not None:
|
||||
args += [normal[0], normal[1], normal[2]]
|
||||
if binary:
|
||||
fmt = '<fff'
|
||||
if color is not None:
|
||||
fmt = fmt + 'BBB'
|
||||
if normal is not None:
|
||||
fmt = fmt + 'fff'
|
||||
fp.write(struct.pack(fmt, *args))
|
||||
else:
|
||||
fmt = '%f %f %f'
|
||||
if color is not None:
|
||||
fmt = fmt + ' %d %d %d'
|
||||
if normal is not None:
|
||||
fmt = fmt + ' %f %f %f'
|
||||
fmt += '\n'
|
||||
fp.write(fmt % tuple(args))
|
||||
|
||||
|
||||
def _write_ply_triangle(fp, i0, i1, i2, binary):
|
||||
if binary:
|
||||
fp.write(struct.pack('<Biii', 3, i0, i1, i2))
|
||||
else:
|
||||
fp.write('3 %d %d %d\n' % (i0, i1, i2))
|
||||
|
||||
|
||||
def _write_ply_header_line(fp, str, binary):
|
||||
if binary:
|
||||
fp.write(str.encode())
|
||||
else:
|
||||
fp.write(str)
|
||||
if binary:
|
||||
fp.write(str.encode())
|
||||
else:
|
||||
fp.write(str)
|
||||
|
||||
|
||||
def write_ply(path, verts, trias=None, color=None, normals=None, binary=False):
|
||||
if verts.shape[1] != 3:
|
||||
raise Exception('verts has to be of shape Nx3')
|
||||
if trias is not None and trias.shape[1] != 3:
|
||||
raise Exception('trias has to be of shape Nx3')
|
||||
if color is not None and not callable(color) and not isinstance(color, np.ndarray) and color.shape[1] != 3:
|
||||
raise Exception('color has to be of shape Nx3 or a callable')
|
||||
if verts.shape[1] != 3:
|
||||
raise Exception('verts has to be of shape Nx3')
|
||||
if trias is not None and trias.shape[1] != 3:
|
||||
raise Exception('trias has to be of shape Nx3')
|
||||
if color is not None and not callable(color) and not isinstance(color, np.ndarray) and color.shape[1] != 3:
|
||||
raise Exception('color has to be of shape Nx3 or a callable')
|
||||
|
||||
mode = 'wb' if binary else 'w'
|
||||
with open(path, mode) as fp:
|
||||
_write_ply_header_line(fp, "ply\n", binary)
|
||||
if binary:
|
||||
_write_ply_header_line(fp, "format binary_little_endian 1.0\n", binary)
|
||||
else:
|
||||
_write_ply_header_line(fp, "format ascii 1.0\n", binary)
|
||||
_write_ply_header_line(fp, "element vertex %d\n" % (verts.shape[0]), binary)
|
||||
_write_ply_header_line(fp, "property float32 x\n", binary)
|
||||
_write_ply_header_line(fp, "property float32 y\n", binary)
|
||||
_write_ply_header_line(fp, "property float32 z\n", binary)
|
||||
if color is not None:
|
||||
_write_ply_header_line(fp, "property uchar red\n", binary)
|
||||
_write_ply_header_line(fp, "property uchar green\n", binary)
|
||||
_write_ply_header_line(fp, "property uchar blue\n", binary)
|
||||
if normals is not None:
|
||||
_write_ply_header_line(fp, "property float32 nx\n", binary)
|
||||
_write_ply_header_line(fp, "property float32 ny\n", binary)
|
||||
_write_ply_header_line(fp, "property float32 nz\n", binary)
|
||||
if trias is not None:
|
||||
_write_ply_header_line(fp, "element face %d\n" % (trias.shape[0]), binary)
|
||||
_write_ply_header_line(fp, "property list uchar int32 vertex_indices\n", binary)
|
||||
_write_ply_header_line(fp, "end_header\n", binary)
|
||||
|
||||
for vidx, v in enumerate(verts):
|
||||
if color is not None:
|
||||
if callable(color):
|
||||
c = color(vidx)
|
||||
elif color.shape[0] > 1:
|
||||
c = color[vidx]
|
||||
mode = 'wb' if binary else 'w'
|
||||
with open(path, mode) as fp:
|
||||
_write_ply_header_line(fp, "ply\n", binary)
|
||||
if binary:
|
||||
_write_ply_header_line(fp, "format binary_little_endian 1.0\n", binary)
|
||||
else:
|
||||
c = color[0]
|
||||
else:
|
||||
c = None
|
||||
if normals is None:
|
||||
n = None
|
||||
else:
|
||||
n = normals[vidx]
|
||||
_write_ply_point(fp, v[0],v[1],v[2], c, n, binary)
|
||||
_write_ply_header_line(fp, "format ascii 1.0\n", binary)
|
||||
_write_ply_header_line(fp, "element vertex %d\n" % (verts.shape[0]), binary)
|
||||
_write_ply_header_line(fp, "property float32 x\n", binary)
|
||||
_write_ply_header_line(fp, "property float32 y\n", binary)
|
||||
_write_ply_header_line(fp, "property float32 z\n", binary)
|
||||
if color is not None:
|
||||
_write_ply_header_line(fp, "property uchar red\n", binary)
|
||||
_write_ply_header_line(fp, "property uchar green\n", binary)
|
||||
_write_ply_header_line(fp, "property uchar blue\n", binary)
|
||||
if normals is not None:
|
||||
_write_ply_header_line(fp, "property float32 nx\n", binary)
|
||||
_write_ply_header_line(fp, "property float32 ny\n", binary)
|
||||
_write_ply_header_line(fp, "property float32 nz\n", binary)
|
||||
if trias is not None:
|
||||
_write_ply_header_line(fp, "element face %d\n" % (trias.shape[0]), binary)
|
||||
_write_ply_header_line(fp, "property list uchar int32 vertex_indices\n", binary)
|
||||
_write_ply_header_line(fp, "end_header\n", binary)
|
||||
|
||||
for vidx, v in enumerate(verts):
|
||||
if color is not None:
|
||||
if callable(color):
|
||||
c = color(vidx)
|
||||
elif color.shape[0] > 1:
|
||||
c = color[vidx]
|
||||
else:
|
||||
c = color[0]
|
||||
else:
|
||||
c = None
|
||||
if normals is None:
|
||||
n = None
|
||||
else:
|
||||
n = normals[vidx]
|
||||
_write_ply_point(fp, v[0], v[1], v[2], c, n, binary)
|
||||
|
||||
if trias is not None:
|
||||
for t in trias:
|
||||
_write_ply_triangle(fp, t[0], t[1], t[2], binary)
|
||||
|
||||
if trias is not None:
|
||||
for t in trias:
|
||||
_write_ply_triangle(fp, t[0],t[1],t[2], binary)
|
||||
|
||||
def faces_to_triangles(faces):
|
||||
new_faces = []
|
||||
for f in faces:
|
||||
if f[0] == 3:
|
||||
new_faces.append([f[1], f[2], f[3]])
|
||||
elif f[0] == 4:
|
||||
new_faces.append([f[1], f[2], f[3]])
|
||||
new_faces.append([f[3], f[4], f[1]])
|
||||
else:
|
||||
raise Exception('unknown face count %d', f[0])
|
||||
return new_faces
|
||||
new_faces = []
|
||||
for f in faces:
|
||||
if f[0] == 3:
|
||||
new_faces.append([f[1], f[2], f[3]])
|
||||
elif f[0] == 4:
|
||||
new_faces.append([f[1], f[2], f[3]])
|
||||
new_faces.append([f[3], f[4], f[1]])
|
||||
else:
|
||||
raise Exception('unknown face count %d', f[0])
|
||||
return new_faces
|
||||
|
||||
|
||||
def read_ply(path):
|
||||
with open(path, 'rb') as f:
|
||||
# parse header
|
||||
line = f.readline().decode().strip()
|
||||
if line != 'ply':
|
||||
raise Exception('Header error')
|
||||
n_verts = 0
|
||||
n_faces = 0
|
||||
vert_types = {}
|
||||
vert_bin_format = []
|
||||
vert_bin_len = 0
|
||||
vert_bin_cols = 0
|
||||
line = f.readline().decode()
|
||||
parse_vertex_prop = False
|
||||
while line.strip() != 'end_header':
|
||||
if 'format' in line:
|
||||
if 'ascii' in line:
|
||||
binary = False
|
||||
elif 'binary_little_endian' in line:
|
||||
binary = True
|
||||
else:
|
||||
raise Exception('invalid ply format')
|
||||
if 'element face' in line:
|
||||
splits = line.strip().split(' ')
|
||||
n_faces = int(splits[-1])
|
||||
with open(path, 'rb') as f:
|
||||
# parse header
|
||||
line = f.readline().decode().strip()
|
||||
if line != 'ply':
|
||||
raise Exception('Header error')
|
||||
n_verts = 0
|
||||
n_faces = 0
|
||||
vert_types = {}
|
||||
vert_bin_format = []
|
||||
vert_bin_len = 0
|
||||
vert_bin_cols = 0
|
||||
line = f.readline().decode()
|
||||
parse_vertex_prop = False
|
||||
if 'element camera' in line:
|
||||
parse_vertex_prop = False
|
||||
if 'element vertex' in line:
|
||||
splits = line.strip().split(' ')
|
||||
n_verts = int(splits[-1])
|
||||
parse_vertex_prop = True
|
||||
if parse_vertex_prop and 'property' in line:
|
||||
prop = line.strip().split()
|
||||
if prop[1] == 'float':
|
||||
vert_bin_format.append('f4')
|
||||
vert_bin_len += 4
|
||||
vert_bin_cols += 1
|
||||
elif prop[1] == 'uchar':
|
||||
vert_bin_format.append('B')
|
||||
vert_bin_len += 1
|
||||
vert_bin_cols += 1
|
||||
while line.strip() != 'end_header':
|
||||
if 'format' in line:
|
||||
if 'ascii' in line:
|
||||
binary = False
|
||||
elif 'binary_little_endian' in line:
|
||||
binary = True
|
||||
else:
|
||||
raise Exception('invalid ply format')
|
||||
if 'element face' in line:
|
||||
splits = line.strip().split(' ')
|
||||
n_faces = int(splits[-1])
|
||||
parse_vertex_prop = False
|
||||
if 'element camera' in line:
|
||||
parse_vertex_prop = False
|
||||
if 'element vertex' in line:
|
||||
splits = line.strip().split(' ')
|
||||
n_verts = int(splits[-1])
|
||||
parse_vertex_prop = True
|
||||
if parse_vertex_prop and 'property' in line:
|
||||
prop = line.strip().split()
|
||||
if prop[1] == 'float':
|
||||
vert_bin_format.append('f4')
|
||||
vert_bin_len += 4
|
||||
vert_bin_cols += 1
|
||||
elif prop[1] == 'uchar':
|
||||
vert_bin_format.append('B')
|
||||
vert_bin_len += 1
|
||||
vert_bin_cols += 1
|
||||
else:
|
||||
raise Exception('invalid property')
|
||||
vert_types[prop[2]] = len(vert_types)
|
||||
line = f.readline().decode()
|
||||
|
||||
# parse content
|
||||
if binary:
|
||||
sz = n_verts * vert_bin_len
|
||||
fmt = ','.join(vert_bin_format)
|
||||
verts = np.ndarray(shape=(1, n_verts), dtype=np.dtype(fmt), buffer=f.read(sz))
|
||||
verts = verts[0].astype(vert_bin_cols * 'f4,').view(dtype='f4').reshape((n_verts, -1))
|
||||
faces = []
|
||||
for idx in range(n_faces):
|
||||
fmt = '<Biii'
|
||||
length = struct.calcsize(fmt)
|
||||
dat = f.read(length)
|
||||
vals = struct.unpack(fmt, dat)
|
||||
faces.append(vals)
|
||||
faces = faces_to_triangles(faces)
|
||||
faces = np.array(faces, dtype=np.int32)
|
||||
else:
|
||||
raise Exception('invalid property')
|
||||
vert_types[prop[2]] = len(vert_types)
|
||||
line = f.readline().decode()
|
||||
verts = []
|
||||
for idx in range(n_verts):
|
||||
vals = [float(v) for v in f.readline().decode().strip().split(' ')]
|
||||
verts.append(vals)
|
||||
verts = np.array(verts, dtype=np.float32)
|
||||
faces = []
|
||||
for idx in range(n_faces):
|
||||
splits = f.readline().decode().strip().split(' ')
|
||||
n_face_verts = int(splits[0])
|
||||
vals = [int(v) for v in splits[0:n_face_verts + 1]]
|
||||
faces.append(vals)
|
||||
faces = faces_to_triangles(faces)
|
||||
faces = np.array(faces, dtype=np.int32)
|
||||
|
||||
# parse content
|
||||
if binary:
|
||||
sz = n_verts * vert_bin_len
|
||||
fmt = ','.join(vert_bin_format)
|
||||
verts = np.ndarray(shape=(1, n_verts), dtype=np.dtype(fmt), buffer=f.read(sz))
|
||||
verts = verts[0].astype(vert_bin_cols*'f4,').view(dtype='f4').reshape((n_verts,-1))
|
||||
faces = []
|
||||
for idx in range(n_faces):
|
||||
fmt = '<Biii'
|
||||
length = struct.calcsize(fmt)
|
||||
dat = f.read(length)
|
||||
vals = struct.unpack(fmt, dat)
|
||||
faces.append(vals)
|
||||
faces = faces_to_triangles(faces)
|
||||
faces = np.array(faces, dtype=np.int32)
|
||||
else:
|
||||
verts = []
|
||||
for idx in range(n_verts):
|
||||
vals = [float(v) for v in f.readline().decode().strip().split(' ')]
|
||||
verts.append(vals)
|
||||
verts = np.array(verts, dtype=np.float32)
|
||||
faces = []
|
||||
for idx in range(n_faces):
|
||||
splits = f.readline().decode().strip().split(' ')
|
||||
n_face_verts = int(splits[0])
|
||||
vals = [int(v) for v in splits[0:n_face_verts+1]]
|
||||
faces.append(vals)
|
||||
faces = faces_to_triangles(faces)
|
||||
faces = np.array(faces, dtype=np.int32)
|
||||
xyz = None
|
||||
if 'x' in vert_types and 'y' in vert_types and 'z' in vert_types:
|
||||
xyz = verts[:, [vert_types['x'], vert_types['y'], vert_types['z']]]
|
||||
colors = None
|
||||
if 'red' in vert_types and 'green' in vert_types and 'blue' in vert_types:
|
||||
colors = verts[:, [vert_types['red'], vert_types['green'], vert_types['blue']]]
|
||||
colors /= 255
|
||||
normals = None
|
||||
if 'nx' in vert_types and 'ny' in vert_types and 'nz' in vert_types:
|
||||
normals = verts[:, [vert_types['nx'], vert_types['ny'], vert_types['nz']]]
|
||||
|
||||
xyz = None
|
||||
if 'x' in vert_types and 'y' in vert_types and 'z' in vert_types:
|
||||
xyz = verts[:,[vert_types['x'], vert_types['y'], vert_types['z']]]
|
||||
colors = None
|
||||
if 'red' in vert_types and 'green' in vert_types and 'blue' in vert_types:
|
||||
colors = verts[:,[vert_types['red'], vert_types['green'], vert_types['blue']]]
|
||||
colors /= 255
|
||||
normals = None
|
||||
if 'nx' in vert_types and 'ny' in vert_types and 'nz' in vert_types:
|
||||
normals = verts[:,[vert_types['nx'], vert_types['ny'], vert_types['nz']]]
|
||||
|
||||
return xyz, faces, colors, normals
|
||||
return xyz, faces, colors, normals
|
||||
|
||||
|
||||
def _read_obj_split_f(s):
|
||||
parts = s.split('/')
|
||||
vidx = int(parts[0]) - 1
|
||||
if len(parts) >= 2 and len(parts[1]) > 0:
|
||||
tidx = int(parts[1]) - 1
|
||||
else:
|
||||
tidx = -1
|
||||
if len(parts) >= 3 and len(parts[2]) > 0:
|
||||
nidx = int(parts[2]) - 1
|
||||
else:
|
||||
nidx = -1
|
||||
return vidx, tidx, nidx
|
||||
parts = s.split('/')
|
||||
vidx = int(parts[0]) - 1
|
||||
if len(parts) >= 2 and len(parts[1]) > 0:
|
||||
tidx = int(parts[1]) - 1
|
||||
else:
|
||||
tidx = -1
|
||||
if len(parts) >= 3 and len(parts[2]) > 0:
|
||||
nidx = int(parts[2]) - 1
|
||||
else:
|
||||
nidx = -1
|
||||
return vidx, tidx, nidx
|
||||
|
||||
|
||||
def read_obj(path):
|
||||
with open(path, 'r') as fp:
|
||||
lines = fp.readlines()
|
||||
with open(path, 'r') as fp:
|
||||
lines = fp.readlines()
|
||||
|
||||
verts = []
|
||||
colors = []
|
||||
fnorms = []
|
||||
fnorm_map = collections.defaultdict(list)
|
||||
faces = []
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if line.startswith('#') or len(line) == 0:
|
||||
continue
|
||||
verts = []
|
||||
colors = []
|
||||
fnorms = []
|
||||
fnorm_map = collections.defaultdict(list)
|
||||
faces = []
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if line.startswith('#') or len(line) == 0:
|
||||
continue
|
||||
|
||||
parts = line.split()
|
||||
if line.startswith('v '):
|
||||
parts = parts[1:]
|
||||
x,y,z = float(parts[0]), float(parts[1]), float(parts[2])
|
||||
if len(parts) == 4 or len(parts) == 7:
|
||||
w = float(parts[3])
|
||||
x,y,z = x/w, y/w, z/w
|
||||
verts.append((x,y,z))
|
||||
if len(parts) >= 6:
|
||||
r,g,b = float(parts[-3]), float(parts[-2]), float(parts[-1])
|
||||
rgb.append((r,g,b))
|
||||
parts = line.split()
|
||||
if line.startswith('v '):
|
||||
parts = parts[1:]
|
||||
x, y, z = float(parts[0]), float(parts[1]), float(parts[2])
|
||||
if len(parts) == 4 or len(parts) == 7:
|
||||
w = float(parts[3])
|
||||
x, y, z = x / w, y / w, z / w
|
||||
verts.append((x, y, z))
|
||||
if len(parts) >= 6:
|
||||
r, g, b = float(parts[-3]), float(parts[-2]), float(parts[-1])
|
||||
rgb.append((r, g, b))
|
||||
|
||||
elif line.startswith('vn '):
|
||||
parts = parts[1:]
|
||||
x,y,z = float(parts[0]), float(parts[1]), float(parts[2])
|
||||
fnorms.append((x,y,z))
|
||||
elif line.startswith('vn '):
|
||||
parts = parts[1:]
|
||||
x, y, z = float(parts[0]), float(parts[1]), float(parts[2])
|
||||
fnorms.append((x, y, z))
|
||||
|
||||
elif line.startswith('f '):
|
||||
parts = parts[1:]
|
||||
if len(parts) != 3:
|
||||
raise Exception('only triangle meshes supported atm')
|
||||
vidx0, tidx0, nidx0 = _read_obj_split_f(parts[0])
|
||||
vidx1, tidx1, nidx1 = _read_obj_split_f(parts[1])
|
||||
vidx2, tidx2, nidx2 = _read_obj_split_f(parts[2])
|
||||
elif line.startswith('f '):
|
||||
parts = parts[1:]
|
||||
if len(parts) != 3:
|
||||
raise Exception('only triangle meshes supported atm')
|
||||
vidx0, tidx0, nidx0 = _read_obj_split_f(parts[0])
|
||||
vidx1, tidx1, nidx1 = _read_obj_split_f(parts[1])
|
||||
vidx2, tidx2, nidx2 = _read_obj_split_f(parts[2])
|
||||
|
||||
faces.append((vidx0, vidx1, vidx2))
|
||||
if nidx0 >= 0:
|
||||
fnorm_map[vidx0].append( nidx0 )
|
||||
if nidx1 >= 0:
|
||||
fnorm_map[vidx1].append( nidx1 )
|
||||
if nidx2 >= 0:
|
||||
fnorm_map[vidx2].append( nidx2 )
|
||||
faces.append((vidx0, vidx1, vidx2))
|
||||
if nidx0 >= 0:
|
||||
fnorm_map[vidx0].append(nidx0)
|
||||
if nidx1 >= 0:
|
||||
fnorm_map[vidx1].append(nidx1)
|
||||
if nidx2 >= 0:
|
||||
fnorm_map[vidx2].append(nidx2)
|
||||
|
||||
verts = np.array(verts)
|
||||
colors = np.array(colors)
|
||||
fnorms = np.array(fnorms)
|
||||
faces = np.array(faces)
|
||||
|
||||
# face normals to vertex normals
|
||||
norms = np.zeros_like(verts)
|
||||
for vidx in fnorm_map.keys():
|
||||
ind = fnorm_map[vidx]
|
||||
norms[vidx] = fnorms[ind].sum(axis=0)
|
||||
N = np.linalg.norm(norms, axis=1, keepdims=True)
|
||||
np.divide(norms, N, out=norms, where=N != 0)
|
||||
verts = np.array(verts)
|
||||
colors = np.array(colors)
|
||||
fnorms = np.array(fnorms)
|
||||
faces = np.array(faces)
|
||||
|
||||
return verts, faces, colors, norms
|
||||
# face normals to vertex normals
|
||||
norms = np.zeros_like(verts)
|
||||
for vidx in fnorm_map.keys():
|
||||
ind = fnorm_map[vidx]
|
||||
norms[vidx] = fnorms[ind].sum(axis=0)
|
||||
N = np.linalg.norm(norms, axis=1, keepdims=True)
|
||||
np.divide(norms, N, out=norms, where=N != 0)
|
||||
|
||||
return verts, faces, colors, norms
|
||||
|
||||
+202
-190
@@ -1,248 +1,260 @@
|
||||
import numpy as np
|
||||
from . import geometry
|
||||
|
||||
|
||||
def _process_inputs(estimate, target, mask):
|
||||
if estimate.shape != target.shape:
|
||||
raise Exception('estimate and target have to be same shape')
|
||||
if mask is None:
|
||||
mask = np.ones(estimate.shape, dtype=np.bool)
|
||||
else:
|
||||
mask = mask != 0
|
||||
if estimate.shape != mask.shape:
|
||||
raise Exception('estimate and mask have to be same shape')
|
||||
return estimate, target, mask
|
||||
if estimate.shape != target.shape:
|
||||
raise Exception('estimate and target have to be same shape')
|
||||
if mask is None:
|
||||
mask = np.ones(estimate.shape, dtype=np.bool)
|
||||
else:
|
||||
mask = mask != 0
|
||||
if estimate.shape != mask.shape:
|
||||
raise Exception('estimate and mask have to be same shape')
|
||||
return estimate, target, mask
|
||||
|
||||
|
||||
def mse(estimate, target, mask=None):
|
||||
estimate, target, mask = _process_inputs(estimate, target, mask)
|
||||
m = np.sum((estimate[mask] - target[mask])**2) / mask.sum()
|
||||
return m
|
||||
estimate, target, mask = _process_inputs(estimate, target, mask)
|
||||
m = np.sum((estimate[mask] - target[mask]) ** 2) / mask.sum()
|
||||
return m
|
||||
|
||||
|
||||
def rmse(estimate, target, mask=None):
|
||||
return np.sqrt(mse(estimate, target, mask))
|
||||
return np.sqrt(mse(estimate, target, mask))
|
||||
|
||||
|
||||
def mae(estimate, target, mask=None):
|
||||
estimate, target, mask = _process_inputs(estimate, target, mask)
|
||||
m = np.abs(estimate[mask] - target[mask]).sum() / mask.sum()
|
||||
return m
|
||||
estimate, target, mask = _process_inputs(estimate, target, mask)
|
||||
m = np.abs(estimate[mask] - target[mask]).sum() / mask.sum()
|
||||
return m
|
||||
|
||||
|
||||
def outlier_fraction(estimate, target, mask=None, threshold=0):
|
||||
estimate, target, mask = _process_inputs(estimate, target, mask)
|
||||
diff = np.abs(estimate[mask] - target[mask])
|
||||
m = (diff > threshold).sum() / mask.sum()
|
||||
return m
|
||||
estimate, target, mask = _process_inputs(estimate, target, mask)
|
||||
diff = np.abs(estimate[mask] - target[mask])
|
||||
m = (diff > threshold).sum() / mask.sum()
|
||||
return m
|
||||
|
||||
|
||||
class Metric(object):
|
||||
def __init__(self, str_prefix=''):
|
||||
self.str_prefix = str_prefix
|
||||
self.reset()
|
||||
def __init__(self, str_prefix=''):
|
||||
self.str_prefix = str_prefix
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
pass
|
||||
def reset(self):
|
||||
pass
|
||||
|
||||
def add(self, es, ta, ma=None):
|
||||
pass
|
||||
def add(self, es, ta, ma=None):
|
||||
pass
|
||||
|
||||
def get(self):
|
||||
return {}
|
||||
def get(self):
|
||||
return {}
|
||||
|
||||
def items(self):
|
||||
return self.get().items()
|
||||
def items(self):
|
||||
return self.get().items()
|
||||
|
||||
def __str__(self):
|
||||
return ', '.join([f'{self.str_prefix}{key}={value:.5f}' for key, value in self.get().items()])
|
||||
|
||||
def __str__(self):
|
||||
return ', '.join([f'{self.str_prefix}{key}={value:.5f}' for key, value in self.get().items()])
|
||||
|
||||
class MultipleMetric(Metric):
|
||||
def __init__(self, *metrics, **kwargs):
|
||||
self.metrics = [*metrics]
|
||||
super().__init__(**kwargs)
|
||||
def __init__(self, *metrics, **kwargs):
|
||||
self.metrics = [*metrics]
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def reset(self):
|
||||
for m in self.metrics:
|
||||
m.reset()
|
||||
def reset(self):
|
||||
for m in self.metrics:
|
||||
m.reset()
|
||||
|
||||
def add(self, es, ta, ma=None):
|
||||
for m in self.metrics:
|
||||
m.add(es, ta, ma)
|
||||
def add(self, es, ta, ma=None):
|
||||
for m in self.metrics:
|
||||
m.add(es, ta, ma)
|
||||
|
||||
def get(self):
|
||||
ret = {}
|
||||
for m in self.metrics:
|
||||
vals = m.get()
|
||||
for k in vals:
|
||||
ret[k] = vals[k]
|
||||
return ret
|
||||
def get(self):
|
||||
ret = {}
|
||||
for m in self.metrics:
|
||||
vals = m.get()
|
||||
for k in vals:
|
||||
ret[k] = vals[k]
|
||||
return ret
|
||||
|
||||
def __str__(self):
|
||||
return '\n'.join([str(m) for m in self.metrics])
|
||||
|
||||
def __str__(self):
|
||||
return '\n'.join([str(m) for m in self.metrics])
|
||||
|
||||
class BaseDistanceMetric(Metric):
|
||||
def __init__(self, name='', **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.name = name
|
||||
def __init__(self, name='', **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.name = name
|
||||
|
||||
def reset(self):
|
||||
self.dists = []
|
||||
def reset(self):
|
||||
self.dists = []
|
||||
|
||||
def add(self, es, ta, ma=None):
|
||||
pass
|
||||
def add(self, es, ta, ma=None):
|
||||
pass
|
||||
|
||||
def get(self):
|
||||
dists = np.hstack(self.dists)
|
||||
return {
|
||||
f'dist{self.name}_mean': float(np.mean(dists)),
|
||||
f'dist{self.name}_std': float(np.std(dists)),
|
||||
f'dist{self.name}_median': float(np.median(dists)),
|
||||
f'dist{self.name}_q10': float(np.percentile(dists, 10)),
|
||||
f'dist{self.name}_q90': float(np.percentile(dists, 90)),
|
||||
f'dist{self.name}_min': float(np.min(dists)),
|
||||
f'dist{self.name}_max': float(np.max(dists)),
|
||||
}
|
||||
|
||||
def get(self):
|
||||
dists = np.hstack(self.dists)
|
||||
return {
|
||||
f'dist{self.name}_mean': float(np.mean(dists)),
|
||||
f'dist{self.name}_std': float(np.std(dists)),
|
||||
f'dist{self.name}_median': float(np.median(dists)),
|
||||
f'dist{self.name}_q10': float(np.percentile(dists, 10)),
|
||||
f'dist{self.name}_q90': float(np.percentile(dists, 90)),
|
||||
f'dist{self.name}_min': float(np.min(dists)),
|
||||
f'dist{self.name}_max': float(np.max(dists)),
|
||||
}
|
||||
|
||||
class DistanceMetric(BaseDistanceMetric):
|
||||
def __init__(self, vec_length, p=2, **kwargs):
|
||||
super().__init__(name=f'{p}', **kwargs)
|
||||
self.vec_length = vec_length
|
||||
self.p = p
|
||||
def __init__(self, vec_length, p=2, **kwargs):
|
||||
super().__init__(name=f'{p}', **kwargs)
|
||||
self.vec_length = vec_length
|
||||
self.p = p
|
||||
|
||||
def add(self, es, ta, ma=None):
|
||||
if es.shape != ta.shape or es.shape[1] != self.vec_length or es.ndim != 2:
|
||||
print(es.shape, ta.shape)
|
||||
raise Exception('es and ta have to be of shape Nxdim')
|
||||
if ma is not None:
|
||||
es = es[ma != 0]
|
||||
ta = ta[ma != 0]
|
||||
dist = np.linalg.norm(es - ta, ord=self.p, axis=1)
|
||||
self.dists.append(dist)
|
||||
|
||||
def add(self, es, ta, ma=None):
|
||||
if es.shape != ta.shape or es.shape[1] != self.vec_length or es.ndim != 2:
|
||||
print(es.shape, ta.shape)
|
||||
raise Exception('es and ta have to be of shape Nxdim')
|
||||
if ma is not None:
|
||||
es = es[ma != 0]
|
||||
ta = ta[ma != 0]
|
||||
dist = np.linalg.norm(es - ta, ord=self.p, axis=1)
|
||||
self.dists.append( dist )
|
||||
|
||||
class OutlierFractionMetric(DistanceMetric):
|
||||
def __init__(self, thresholds, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.thresholds = thresholds
|
||||
def __init__(self, thresholds, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.thresholds = thresholds
|
||||
|
||||
def get(self):
|
||||
dists = np.hstack(self.dists)
|
||||
ret = {}
|
||||
for t in self.thresholds:
|
||||
ma = dists > t
|
||||
ret[f'of{t}'] = float(ma.sum() / ma.size)
|
||||
return ret
|
||||
|
||||
def get(self):
|
||||
dists = np.hstack(self.dists)
|
||||
ret = {}
|
||||
for t in self.thresholds:
|
||||
ma = dists > t
|
||||
ret[f'of{t}'] = float(ma.sum() / ma.size)
|
||||
return ret
|
||||
|
||||
class RelativeDistanceMetric(BaseDistanceMetric):
|
||||
def __init__(self, vec_length, p=2, **kwargs):
|
||||
super().__init__(name=f'rel{p}', **kwargs)
|
||||
self.vec_length = vec_length
|
||||
self.p = p
|
||||
def __init__(self, vec_length, p=2, **kwargs):
|
||||
super().__init__(name=f'rel{p}', **kwargs)
|
||||
self.vec_length = vec_length
|
||||
self.p = p
|
||||
|
||||
def add(self, es, ta, ma=None):
|
||||
if es.shape != ta.shape or es.shape[1] != self.vec_length or es.ndim != 2:
|
||||
raise Exception('es and ta have to be of shape Nxdim')
|
||||
dist = np.linalg.norm(es - ta, ord=self.p, axis=1)
|
||||
denom = np.linalg.norm(ta, ord=self.p, axis=1)
|
||||
dist /= denom
|
||||
if ma is not None:
|
||||
dist = dist[ma != 0]
|
||||
self.dists.append(dist)
|
||||
|
||||
def add(self, es, ta, ma=None):
|
||||
if es.shape != ta.shape or es.shape[1] != self.vec_length or es.ndim != 2:
|
||||
raise Exception('es and ta have to be of shape Nxdim')
|
||||
dist = np.linalg.norm(es - ta, ord=self.p, axis=1)
|
||||
denom = np.linalg.norm(ta, ord=self.p, axis=1)
|
||||
dist /= denom
|
||||
if ma is not None:
|
||||
dist = dist[ma != 0]
|
||||
self.dists.append( dist )
|
||||
|
||||
class RotmDistanceMetric(BaseDistanceMetric):
|
||||
def __init__(self, type='identity', **kwargs):
|
||||
super().__init__(name=type, **kwargs)
|
||||
self.type = type
|
||||
def __init__(self, type='identity', **kwargs):
|
||||
super().__init__(name=type, **kwargs)
|
||||
self.type = type
|
||||
|
||||
def add(self, es, ta, ma=None):
|
||||
if es.shape != ta.shape or es.shape[1] != 3 or es.shape[2] != 3 or es.ndim != 3:
|
||||
print(es.shape, ta.shape)
|
||||
raise Exception('es and ta have to be of shape Nx3x3')
|
||||
if ma is not None:
|
||||
raise Exception('mask is not implemented')
|
||||
if self.type == 'identity':
|
||||
self.dists.append(geometry.rotm_distance_identity(es, ta))
|
||||
elif self.type == 'geodesic':
|
||||
self.dists.append(geometry.rotm_distance_geodesic_unit_sphere(es, ta))
|
||||
else:
|
||||
raise Exception('invalid distance type')
|
||||
|
||||
def add(self, es, ta, ma=None):
|
||||
if es.shape != ta.shape or es.shape[1] != 3 or es.shape[2] != 3 or es.ndim != 3:
|
||||
print(es.shape, ta.shape)
|
||||
raise Exception('es and ta have to be of shape Nx3x3')
|
||||
if ma is not None:
|
||||
raise Exception('mask is not implemented')
|
||||
if self.type == 'identity':
|
||||
self.dists.append( geometry.rotm_distance_identity(es, ta) )
|
||||
elif self.type == 'geodesic':
|
||||
self.dists.append( geometry.rotm_distance_geodesic_unit_sphere(es, ta) )
|
||||
else:
|
||||
raise Exception('invalid distance type')
|
||||
|
||||
class QuaternionDistanceMetric(BaseDistanceMetric):
|
||||
def __init__(self, type='angle', **kwargs):
|
||||
super().__init__(name=type, **kwargs)
|
||||
self.type = type
|
||||
def __init__(self, type='angle', **kwargs):
|
||||
super().__init__(name=type, **kwargs)
|
||||
self.type = type
|
||||
|
||||
def add(self, es, ta, ma=None):
|
||||
if es.shape != ta.shape or es.shape[1] != 4 or es.ndim != 2:
|
||||
print(es.shape, ta.shape)
|
||||
raise Exception('es and ta have to be of shape Nx4')
|
||||
if ma is not None:
|
||||
raise Exception('mask is not implemented')
|
||||
if self.type == 'angle':
|
||||
self.dists.append( geometry.quat_distance_angle(es, ta) )
|
||||
elif self.type == 'mineucl':
|
||||
self.dists.append( geometry.quat_distance_mineucl(es, ta) )
|
||||
elif self.type == 'normdiff':
|
||||
self.dists.append( geometry.quat_distance_normdiff(es, ta) )
|
||||
else:
|
||||
raise Exception('invalid distance type')
|
||||
def add(self, es, ta, ma=None):
|
||||
if es.shape != ta.shape or es.shape[1] != 4 or es.ndim != 2:
|
||||
print(es.shape, ta.shape)
|
||||
raise Exception('es and ta have to be of shape Nx4')
|
||||
if ma is not None:
|
||||
raise Exception('mask is not implemented')
|
||||
if self.type == 'angle':
|
||||
self.dists.append(geometry.quat_distance_angle(es, ta))
|
||||
elif self.type == 'mineucl':
|
||||
self.dists.append(geometry.quat_distance_mineucl(es, ta))
|
||||
elif self.type == 'normdiff':
|
||||
self.dists.append(geometry.quat_distance_normdiff(es, ta))
|
||||
else:
|
||||
raise Exception('invalid distance type')
|
||||
|
||||
|
||||
class BinaryAccuracyMetric(Metric):
|
||||
def __init__(self, thresholds=np.linspace(0.0, 1.0, num=101, dtype=np.float64)[:-1], **kwargs):
|
||||
self.thresholds = thresholds
|
||||
super().__init__(**kwargs)
|
||||
def __init__(self, thresholds=np.linspace(0.0, 1.0, num=101, dtype=np.float64)[:-1], **kwargs):
|
||||
self.thresholds = thresholds
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def reset(self):
|
||||
self.tps = [0 for wp in self.thresholds]
|
||||
self.fps = [0 for wp in self.thresholds]
|
||||
self.fns = [0 for wp in self.thresholds]
|
||||
self.tns = [0 for wp in self.thresholds]
|
||||
self.n_pos = 0
|
||||
self.n_neg = 0
|
||||
def reset(self):
|
||||
self.tps = [0 for wp in self.thresholds]
|
||||
self.fps = [0 for wp in self.thresholds]
|
||||
self.fns = [0 for wp in self.thresholds]
|
||||
self.tns = [0 for wp in self.thresholds]
|
||||
self.n_pos = 0
|
||||
self.n_neg = 0
|
||||
|
||||
def add(self, es, ta, ma=None):
|
||||
if ma is not None:
|
||||
raise Exception('mask is not implemented')
|
||||
es = es.ravel()
|
||||
ta = ta.ravel()
|
||||
if es.shape[0] != ta.shape[0]:
|
||||
raise Exception('invalid shape of es, or ta')
|
||||
if es.min() < 0 or es.max() > 1:
|
||||
raise Exception('estimate has wrong value range')
|
||||
ta_p = (ta == 1)
|
||||
ta_n = (ta == 0)
|
||||
es_p = es[ta_p]
|
||||
es_n = es[ta_n]
|
||||
for idx, wp in enumerate(self.thresholds):
|
||||
wp = np.asscalar(wp)
|
||||
self.tps[idx] += (es_p > wp).sum()
|
||||
self.fps[idx] += (es_n > wp).sum()
|
||||
self.fns[idx] += (es_p <= wp).sum()
|
||||
self.tns[idx] += (es_n <= wp).sum()
|
||||
self.n_pos += ta_p.sum()
|
||||
self.n_neg += ta_n.sum()
|
||||
def add(self, es, ta, ma=None):
|
||||
if ma is not None:
|
||||
raise Exception('mask is not implemented')
|
||||
es = es.ravel()
|
||||
ta = ta.ravel()
|
||||
if es.shape[0] != ta.shape[0]:
|
||||
raise Exception('invalid shape of es, or ta')
|
||||
if es.min() < 0 or es.max() > 1:
|
||||
raise Exception('estimate has wrong value range')
|
||||
ta_p = (ta == 1)
|
||||
ta_n = (ta == 0)
|
||||
es_p = es[ta_p]
|
||||
es_n = es[ta_n]
|
||||
for idx, wp in enumerate(self.thresholds):
|
||||
wp = np.asscalar(wp)
|
||||
self.tps[idx] += (es_p > wp).sum()
|
||||
self.fps[idx] += (es_n > wp).sum()
|
||||
self.fns[idx] += (es_p <= wp).sum()
|
||||
self.tns[idx] += (es_n <= wp).sum()
|
||||
self.n_pos += ta_p.sum()
|
||||
self.n_neg += ta_n.sum()
|
||||
|
||||
def get(self):
|
||||
tps = np.array(self.tps).astype(np.float32)
|
||||
fps = np.array(self.fps).astype(np.float32)
|
||||
fns = np.array(self.fns).astype(np.float32)
|
||||
tns = np.array(self.tns).astype(np.float32)
|
||||
wp = self.thresholds
|
||||
def get(self):
|
||||
tps = np.array(self.tps).astype(np.float32)
|
||||
fps = np.array(self.fps).astype(np.float32)
|
||||
fns = np.array(self.fns).astype(np.float32)
|
||||
tns = np.array(self.tns).astype(np.float32)
|
||||
wp = self.thresholds
|
||||
|
||||
ret = {}
|
||||
ret = {}
|
||||
|
||||
precisions = np.divide(tps, tps + fps, out=np.zeros_like(tps), where=tps + fps != 0)
|
||||
recalls = np.divide(tps, tps + fns, out=np.zeros_like(tps), where=tps + fns != 0) # tprs
|
||||
fprs = np.divide(fps, fps + tns, out=np.zeros_like(tps), where=fps + tns != 0)
|
||||
precisions = np.divide(tps, tps + fps, out=np.zeros_like(tps), where=tps + fps != 0)
|
||||
recalls = np.divide(tps, tps + fns, out=np.zeros_like(tps), where=tps + fns != 0) # tprs
|
||||
fprs = np.divide(fps, fps + tns, out=np.zeros_like(tps), where=fps + tns != 0)
|
||||
|
||||
precisions = np.r_[0, precisions, 1]
|
||||
recalls = np.r_[1, recalls, 0]
|
||||
fprs = np.r_[1, fprs, 0]
|
||||
precisions = np.r_[0, precisions, 1]
|
||||
recalls = np.r_[1, recalls, 0]
|
||||
fprs = np.r_[1, fprs, 0]
|
||||
|
||||
ret['auc'] = float(-np.trapz(recalls, fprs))
|
||||
ret['prauc'] = float(-np.trapz(precisions, recalls))
|
||||
ret['ap'] = float(-(np.diff(recalls) * precisions[:-1]).sum())
|
||||
ret['auc'] = float(-np.trapz(recalls, fprs))
|
||||
ret['prauc'] = float(-np.trapz(precisions, recalls))
|
||||
ret['ap'] = float(-(np.diff(recalls) * precisions[:-1]).sum())
|
||||
|
||||
accuracies = np.divide(tps + tns, tps + tns + fps + fns)
|
||||
aacc = np.mean(accuracies)
|
||||
for t in np.linspace(0,1,num=11)[1:-1]:
|
||||
idx = np.argmin(np.abs(t - wp))
|
||||
ret[f'acc{wp[idx]:.2f}'] = float(accuracies[idx])
|
||||
accuracies = np.divide(tps + tns, tps + tns + fps + fns)
|
||||
aacc = np.mean(accuracies)
|
||||
for t in np.linspace(0, 1, num=11)[1:-1]:
|
||||
idx = np.argmin(np.abs(t - wp))
|
||||
ret[f'acc{wp[idx]:.2f}'] = float(accuracies[idx])
|
||||
|
||||
return ret
|
||||
return ret
|
||||
|
||||
@@ -6,94 +6,99 @@ import matplotlib.pyplot as plt
|
||||
import os
|
||||
import time
|
||||
|
||||
|
||||
def save(path, remove_axis=False, dpi=300, fig=None):
|
||||
if fig is None:
|
||||
fig = plt.gcf()
|
||||
dirname = os.path.dirname(path)
|
||||
if dirname != '' and not os.path.exists(dirname):
|
||||
os.makedirs(dirname)
|
||||
if remove_axis:
|
||||
for ax in fig.axes:
|
||||
ax.axis('off')
|
||||
ax.margins(0,0)
|
||||
fig.subplots_adjust(top=1, bottom=0, right=1, left=0, hspace=0, wspace=0)
|
||||
for ax in fig.axes:
|
||||
ax.xaxis.set_major_locator(plt.NullLocator())
|
||||
ax.yaxis.set_major_locator(plt.NullLocator())
|
||||
fig.savefig(path, dpi=dpi, bbox_inches='tight', pad_inches=0)
|
||||
if fig is None:
|
||||
fig = plt.gcf()
|
||||
dirname = os.path.dirname(path)
|
||||
if dirname != '' and not os.path.exists(dirname):
|
||||
os.makedirs(dirname)
|
||||
if remove_axis:
|
||||
for ax in fig.axes:
|
||||
ax.axis('off')
|
||||
ax.margins(0, 0)
|
||||
fig.subplots_adjust(top=1, bottom=0, right=1, left=0, hspace=0, wspace=0)
|
||||
for ax in fig.axes:
|
||||
ax.xaxis.set_major_locator(plt.NullLocator())
|
||||
ax.yaxis.set_major_locator(plt.NullLocator())
|
||||
fig.savefig(path, dpi=dpi, bbox_inches='tight', pad_inches=0)
|
||||
|
||||
|
||||
def color_map(im_, cmap='viridis', vmin=None, vmax=None):
|
||||
cm = plt.get_cmap(cmap)
|
||||
im = im_.copy()
|
||||
if vmin is None:
|
||||
vmin = np.nanmin(im)
|
||||
if vmax is None:
|
||||
vmax = np.nanmax(im)
|
||||
mask = np.logical_not(np.isfinite(im))
|
||||
im[mask] = vmin
|
||||
im = (im.clip(vmin, vmax) - vmin) / (vmax - vmin)
|
||||
im = cm(im)
|
||||
im = im[...,:3]
|
||||
for c in range(3):
|
||||
im[mask, c] = 1
|
||||
return im
|
||||
cm = plt.get_cmap(cmap)
|
||||
im = im_.copy()
|
||||
if vmin is None:
|
||||
vmin = np.nanmin(im)
|
||||
if vmax is None:
|
||||
vmax = np.nanmax(im)
|
||||
mask = np.logical_not(np.isfinite(im))
|
||||
im[mask] = vmin
|
||||
im = (im.clip(vmin, vmax) - vmin) / (vmax - vmin)
|
||||
im = cm(im)
|
||||
im = im[..., :3]
|
||||
for c in range(3):
|
||||
im[mask, c] = 1
|
||||
return im
|
||||
|
||||
|
||||
def interactive_legend(leg=None, fig=None, all_axes=True):
|
||||
if leg is None:
|
||||
leg = plt.legend()
|
||||
if fig is None:
|
||||
fig = plt.gcf()
|
||||
if all_axes:
|
||||
axs = fig.get_axes()
|
||||
else:
|
||||
axs = [fig.gca()]
|
||||
|
||||
# lined = dict()
|
||||
# lines = ax.lines
|
||||
# for legline, origline in zip(leg.get_lines(), ax.lines):
|
||||
# legline.set_picker(5)
|
||||
# lined[legline] = origline
|
||||
lined = dict()
|
||||
for lidx, legline in enumerate(leg.get_lines()):
|
||||
legline.set_picker(5)
|
||||
lined[legline] = [ax.lines[lidx] for ax in axs]
|
||||
|
||||
def onpick(event):
|
||||
if event.mouseevent.dblclick:
|
||||
tmp = [(k,v) for k,v in lined.items()]
|
||||
if leg is None:
|
||||
leg = plt.legend()
|
||||
if fig is None:
|
||||
fig = plt.gcf()
|
||||
if all_axes:
|
||||
axs = fig.get_axes()
|
||||
else:
|
||||
tmp = [(event.artist, lined[event.artist])]
|
||||
axs = [fig.gca()]
|
||||
|
||||
for legline, origline in tmp:
|
||||
for ol in origline:
|
||||
vis = not ol.get_visible()
|
||||
ol.set_visible(vis)
|
||||
if vis:
|
||||
legline.set_alpha(1.0)
|
||||
else:
|
||||
legline.set_alpha(0.2)
|
||||
fig.canvas.draw()
|
||||
# lined = dict()
|
||||
# lines = ax.lines
|
||||
# for legline, origline in zip(leg.get_lines(), ax.lines):
|
||||
# legline.set_picker(5)
|
||||
# lined[legline] = origline
|
||||
lined = dict()
|
||||
for lidx, legline in enumerate(leg.get_lines()):
|
||||
legline.set_picker(5)
|
||||
lined[legline] = [ax.lines[lidx] for ax in axs]
|
||||
|
||||
def onpick(event):
|
||||
if event.mouseevent.dblclick:
|
||||
tmp = [(k, v) for k, v in lined.items()]
|
||||
else:
|
||||
tmp = [(event.artist, lined[event.artist])]
|
||||
|
||||
for legline, origline in tmp:
|
||||
for ol in origline:
|
||||
vis = not ol.get_visible()
|
||||
ol.set_visible(vis)
|
||||
if vis:
|
||||
legline.set_alpha(1.0)
|
||||
else:
|
||||
legline.set_alpha(0.2)
|
||||
fig.canvas.draw()
|
||||
|
||||
fig.canvas.mpl_connect('pick_event', onpick)
|
||||
|
||||
fig.canvas.mpl_connect('pick_event', onpick)
|
||||
|
||||
def non_annoying_pause(interval, focus_figure=False):
|
||||
# https://github.com/matplotlib/matplotlib/issues/11131
|
||||
backend = mpl.rcParams['backend']
|
||||
if backend in _interactive_bk:
|
||||
figManager = _pylab_helpers.Gcf.get_active()
|
||||
if figManager is not None:
|
||||
canvas = figManager.canvas
|
||||
if canvas.figure.stale:
|
||||
canvas.draw()
|
||||
if focus_figure:
|
||||
plt.show(block=False)
|
||||
canvas.start_event_loop(interval)
|
||||
return
|
||||
time.sleep(interval)
|
||||
# https://github.com/matplotlib/matplotlib/issues/11131
|
||||
backend = mpl.rcParams['backend']
|
||||
if backend in _interactive_bk:
|
||||
figManager = _pylab_helpers.Gcf.get_active()
|
||||
if figManager is not None:
|
||||
canvas = figManager.canvas
|
||||
if canvas.figure.stale:
|
||||
canvas.draw()
|
||||
if focus_figure:
|
||||
plt.show(block=False)
|
||||
canvas.start_event_loop(interval)
|
||||
return
|
||||
time.sleep(interval)
|
||||
|
||||
|
||||
def remove_all_ticks(fig=None):
|
||||
if fig is None:
|
||||
fig = plt.gcf()
|
||||
for ax in fig.axes:
|
||||
ax.axes.get_xaxis().set_visible(False)
|
||||
ax.axes.get_yaxis().set_visible(False)
|
||||
if fig is None:
|
||||
fig = plt.gcf()
|
||||
for ax in fig.axes:
|
||||
ax.axes.get_xaxis().set_visible(False)
|
||||
ax.axes.get_yaxis().set_visible(False)
|
||||
|
||||
+48
-43
@@ -3,55 +3,60 @@ import matplotlib.pyplot as plt
|
||||
|
||||
from . import geometry
|
||||
|
||||
|
||||
def image_matrix(ims, bgval=0):
|
||||
n = ims.shape[0]
|
||||
m = int( np.ceil(np.sqrt(n)) )
|
||||
h = ims.shape[1]
|
||||
w = ims.shape[2]
|
||||
mat = np.empty((m*h, m*w), dtype=ims.dtype)
|
||||
mat.fill(bgval)
|
||||
idx = 0
|
||||
for r in range(m):
|
||||
for c in range(m):
|
||||
if idx < n:
|
||||
mat[r*h:(r+1)*h, c*w:(c+1)*w] = ims[idx]
|
||||
idx += 1
|
||||
return mat
|
||||
n = ims.shape[0]
|
||||
m = int(np.ceil(np.sqrt(n)))
|
||||
h = ims.shape[1]
|
||||
w = ims.shape[2]
|
||||
mat = np.empty((m * h, m * w), dtype=ims.dtype)
|
||||
mat.fill(bgval)
|
||||
idx = 0
|
||||
for r in range(m):
|
||||
for c in range(m):
|
||||
if idx < n:
|
||||
mat[r * h:(r + 1) * h, c * w:(c + 1) * w] = ims[idx]
|
||||
idx += 1
|
||||
return mat
|
||||
|
||||
|
||||
def image_cat(ims, vertical=False):
|
||||
offx = [0]
|
||||
offy = [0]
|
||||
if vertical:
|
||||
width = max([im.shape[1] for im in ims])
|
||||
offx += [0 for im in ims[:-1]]
|
||||
offy += [im.shape[0] for im in ims[:-1]]
|
||||
height = sum([im.shape[0] for im in ims])
|
||||
else:
|
||||
height = max([im.shape[0] for im in ims])
|
||||
offx += [im.shape[1] for im in ims[:-1]]
|
||||
offy += [0 for im in ims[:-1]]
|
||||
width = sum([im.shape[1] for im in ims])
|
||||
offx = np.cumsum(offx)
|
||||
offy = np.cumsum(offy)
|
||||
offx = [0]
|
||||
offy = [0]
|
||||
if vertical:
|
||||
width = max([im.shape[1] for im in ims])
|
||||
offx += [0 for im in ims[:-1]]
|
||||
offy += [im.shape[0] for im in ims[:-1]]
|
||||
height = sum([im.shape[0] for im in ims])
|
||||
else:
|
||||
height = max([im.shape[0] for im in ims])
|
||||
offx += [im.shape[1] for im in ims[:-1]]
|
||||
offy += [0 for im in ims[:-1]]
|
||||
width = sum([im.shape[1] for im in ims])
|
||||
offx = np.cumsum(offx)
|
||||
offy = np.cumsum(offy)
|
||||
|
||||
im = np.zeros((height,width,*ims[0].shape[2:]), dtype=ims[0].dtype)
|
||||
for im0, ox, oy in zip(ims, offx, offy):
|
||||
im[oy:oy + im0.shape[0], ox:ox + im0.shape[1]] = im0
|
||||
im = np.zeros((height, width, *ims[0].shape[2:]), dtype=ims[0].dtype)
|
||||
for im0, ox, oy in zip(ims, offx, offy):
|
||||
im[oy:oy + im0.shape[0], ox:ox + im0.shape[1]] = im0
|
||||
|
||||
return im, offx, offy
|
||||
|
||||
return im, offx, offy
|
||||
|
||||
def line(li, h, w, ax=None, *args, **kwargs):
|
||||
if ax is None:
|
||||
ax = plt.gca()
|
||||
xs = (-li[2] - li[1] * np.array((0, h-1))) / li[0]
|
||||
ys = (-li[2] - li[0] * np.array((0, w-1))) / li[1]
|
||||
pts = np.array([(0,ys[0]), (w-1, ys[1]), (xs[0], 0), (xs[1], h-1)])
|
||||
pts = pts[np.logical_and(np.logical_and(pts[:,0] >= 0, pts[:,0] < w), np.logical_and(pts[:,1] >= 0, pts[:,1] < h))]
|
||||
ax.plot(pts[:,0], pts[:,1], *args, **kwargs)
|
||||
if ax is None:
|
||||
ax = plt.gca()
|
||||
xs = (-li[2] - li[1] * np.array((0, h - 1))) / li[0]
|
||||
ys = (-li[2] - li[0] * np.array((0, w - 1))) / li[1]
|
||||
pts = np.array([(0, ys[0]), (w - 1, ys[1]), (xs[0], 0), (xs[1], h - 1)])
|
||||
pts = pts[
|
||||
np.logical_and(np.logical_and(pts[:, 0] >= 0, pts[:, 0] < w), np.logical_and(pts[:, 1] >= 0, pts[:, 1] < h))]
|
||||
ax.plot(pts[:, 0], pts[:, 1], *args, **kwargs)
|
||||
|
||||
|
||||
def depthshow(depth, *args, ax=None, **kwargs):
|
||||
if ax is None:
|
||||
ax = plt.gca()
|
||||
d = depth.copy()
|
||||
d[d < 0] = np.NaN
|
||||
ax.imshow(d, *args, **kwargs)
|
||||
if ax is None:
|
||||
ax = plt.gca()
|
||||
d = depth.copy()
|
||||
d[d < 0] = np.NaN
|
||||
ax.imshow(d, *args, **kwargs)
|
||||
|
||||
+37
-27
@@ -4,35 +4,45 @@ from mpl_toolkits.mplot3d import Axes3D
|
||||
|
||||
from . import geometry
|
||||
|
||||
|
||||
def ax3d(fig=None):
|
||||
if fig is None:
|
||||
fig = plt.gcf()
|
||||
return fig.add_subplot(111, projection='3d')
|
||||
if fig is None:
|
||||
fig = plt.gcf()
|
||||
return fig.add_subplot(111, projection='3d')
|
||||
|
||||
def plot_camera(ax=None, R=np.eye(3), t=np.zeros((3,)), size=25, marker_C='.', color='b', linestyle='-', linewidth=0.1, label=None, **kwargs):
|
||||
if ax is None:
|
||||
ax = plt.gca()
|
||||
C0 = geometry.translation_to_cameracenter(R, t).ravel()
|
||||
C1 = C0 + R.T.dot( np.array([[-size],[-size],[3*size]], dtype=np.float32) ).ravel()
|
||||
C2 = C0 + R.T.dot( np.array([[-size],[+size],[3*size]], dtype=np.float32) ).ravel()
|
||||
C3 = C0 + R.T.dot( np.array([[+size],[+size],[3*size]], dtype=np.float32) ).ravel()
|
||||
C4 = C0 + R.T.dot( np.array([[+size],[-size],[3*size]], dtype=np.float32) ).ravel()
|
||||
|
||||
if marker_C != '':
|
||||
ax.plot([C0[0]], [C0[1]], [C0[2]], marker=marker_C, color=color, label=label, **kwargs)
|
||||
ax.plot([C0[0], C1[0]], [C0[1], C1[1]], [C0[2], C1[2]], color=color, label='_nolegend_', linestyle=linestyle, linewidth=linewidth, **kwargs)
|
||||
ax.plot([C0[0], C2[0]], [C0[1], C2[1]], [C0[2], C2[2]], color=color, label='_nolegend_', linestyle=linestyle, linewidth=linewidth, **kwargs)
|
||||
ax.plot([C0[0], C3[0]], [C0[1], C3[1]], [C0[2], C3[2]], color=color, label='_nolegend_', linestyle=linestyle, linewidth=linewidth, **kwargs)
|
||||
ax.plot([C0[0], C4[0]], [C0[1], C4[1]], [C0[2], C4[2]], color=color, label='_nolegend_', linestyle=linestyle, linewidth=linewidth, **kwargs)
|
||||
ax.plot([C1[0], C2[0], C3[0], C4[0], C1[0]], [C1[1], C2[1], C3[1], C4[1], C1[1]], [C1[2], C2[2], C3[2], C4[2], C1[2]], color=color, label='_nolegend_', linestyle=linestyle, linewidth=linewidth, **kwargs)
|
||||
def plot_camera(ax=None, R=np.eye(3), t=np.zeros((3,)), size=25, marker_C='.', color='b', linestyle='-', linewidth=0.1,
|
||||
label=None, **kwargs):
|
||||
if ax is None:
|
||||
ax = plt.gca()
|
||||
C0 = geometry.translation_to_cameracenter(R, t).ravel()
|
||||
C1 = C0 + R.T.dot(np.array([[-size], [-size], [3 * size]], dtype=np.float32)).ravel()
|
||||
C2 = C0 + R.T.dot(np.array([[-size], [+size], [3 * size]], dtype=np.float32)).ravel()
|
||||
C3 = C0 + R.T.dot(np.array([[+size], [+size], [3 * size]], dtype=np.float32)).ravel()
|
||||
C4 = C0 + R.T.dot(np.array([[+size], [-size], [3 * size]], dtype=np.float32)).ravel()
|
||||
|
||||
if marker_C != '':
|
||||
ax.plot([C0[0]], [C0[1]], [C0[2]], marker=marker_C, color=color, label=label, **kwargs)
|
||||
ax.plot([C0[0], C1[0]], [C0[1], C1[1]], [C0[2], C1[2]], color=color, label='_nolegend_', linestyle=linestyle,
|
||||
linewidth=linewidth, **kwargs)
|
||||
ax.plot([C0[0], C2[0]], [C0[1], C2[1]], [C0[2], C2[2]], color=color, label='_nolegend_', linestyle=linestyle,
|
||||
linewidth=linewidth, **kwargs)
|
||||
ax.plot([C0[0], C3[0]], [C0[1], C3[1]], [C0[2], C3[2]], color=color, label='_nolegend_', linestyle=linestyle,
|
||||
linewidth=linewidth, **kwargs)
|
||||
ax.plot([C0[0], C4[0]], [C0[1], C4[1]], [C0[2], C4[2]], color=color, label='_nolegend_', linestyle=linestyle,
|
||||
linewidth=linewidth, **kwargs)
|
||||
ax.plot([C1[0], C2[0], C3[0], C4[0], C1[0]], [C1[1], C2[1], C3[1], C4[1], C1[1]],
|
||||
[C1[2], C2[2], C3[2], C4[2], C1[2]], color=color, label='_nolegend_', linestyle=linestyle,
|
||||
linewidth=linewidth, **kwargs)
|
||||
|
||||
|
||||
def axis_equal(ax=None):
|
||||
if ax is None:
|
||||
ax = plt.gca()
|
||||
extents = np.array([getattr(ax, 'get_{}lim'.format(dim))() for dim in 'xyz'])
|
||||
sz = extents[:,1] - extents[:,0]
|
||||
centers = np.mean(extents, axis=1)
|
||||
maxsize = max(abs(sz))
|
||||
r = maxsize/2
|
||||
for ctr, dim in zip(centers, 'xyz'):
|
||||
getattr(ax, 'set_{}lim'.format(dim))(ctr - r, ctr + r)
|
||||
if ax is None:
|
||||
ax = plt.gca()
|
||||
extents = np.array([getattr(ax, 'get_{}lim'.format(dim))() for dim in 'xyz'])
|
||||
sz = extents[:, 1] - extents[:, 0]
|
||||
centers = np.mean(extents, axis=1)
|
||||
maxsize = max(abs(sz))
|
||||
r = maxsize / 2
|
||||
for ctr, dim in zip(centers, 'xyz'):
|
||||
getattr(ax, 'set_{}lim'.format(dim))(ctr - r, ctr + r)
|
||||
|
||||
+379
-369
@@ -3,443 +3,453 @@ import pandas as pd
|
||||
import enum
|
||||
import itertools
|
||||
|
||||
|
||||
class Table(object):
|
||||
def __init__(self, n_cols):
|
||||
self.n_cols = n_cols
|
||||
self.rows = []
|
||||
self.aligns = ['r' for c in range(n_cols)]
|
||||
def __init__(self, n_cols):
|
||||
self.n_cols = n_cols
|
||||
self.rows = []
|
||||
self.aligns = ['r' for c in range(n_cols)]
|
||||
|
||||
def get_cell_align(self, r, c):
|
||||
align = self.rows[r].cells[c].align
|
||||
if align is None:
|
||||
return self.aligns[c]
|
||||
else:
|
||||
return align
|
||||
|
||||
def add_row(self, row):
|
||||
if row.ncols() != self.n_cols:
|
||||
raise Exception(f'row has invalid number of cols, {row.ncols()} vs. {self.n_cols}')
|
||||
self.rows.append(row)
|
||||
|
||||
def empty_row(self):
|
||||
return Row.Empty(self.n_cols)
|
||||
|
||||
def expand_rows(self, n_add_cols=1):
|
||||
if n_add_cols < 0: raise Exception('n_add_cols has to be positive')
|
||||
self.n_cols += n_add_cols
|
||||
for row in self.rows:
|
||||
row.cells.extend([Cell() for cidx in range(n_add_cols)])
|
||||
|
||||
def add_block(self, data, row=-1, col=0, fmt=None, expand=False):
|
||||
if row < 0: row = len(self.rows)
|
||||
while len(self.rows) < row + len(data):
|
||||
self.add_row(self.empty_row())
|
||||
for r in range(len(data)):
|
||||
cols = data[r]
|
||||
if col + len(cols) > self.n_cols:
|
||||
if expand:
|
||||
self.expand_rows(col + len(cols) - self.n_cols)
|
||||
def get_cell_align(self, r, c):
|
||||
align = self.rows[r].cells[c].align
|
||||
if align is None:
|
||||
return self.aligns[c]
|
||||
else:
|
||||
raise Exception('number of cols does not fit in table')
|
||||
for c in range(len(cols)):
|
||||
self.rows[row+r].cells[col+c] = Cell(data[r][c], fmt)
|
||||
return align
|
||||
|
||||
def add_row(self, row):
|
||||
if row.ncols() != self.n_cols:
|
||||
raise Exception(f'row has invalid number of cols, {row.ncols()} vs. {self.n_cols}')
|
||||
self.rows.append(row)
|
||||
|
||||
def empty_row(self):
|
||||
return Row.Empty(self.n_cols)
|
||||
|
||||
def expand_rows(self, n_add_cols=1):
|
||||
if n_add_cols < 0: raise Exception('n_add_cols has to be positive')
|
||||
self.n_cols += n_add_cols
|
||||
for row in self.rows:
|
||||
row.cells.extend([Cell() for cidx in range(n_add_cols)])
|
||||
|
||||
def add_block(self, data, row=-1, col=0, fmt=None, expand=False):
|
||||
if row < 0: row = len(self.rows)
|
||||
while len(self.rows) < row + len(data):
|
||||
self.add_row(self.empty_row())
|
||||
for r in range(len(data)):
|
||||
cols = data[r]
|
||||
if col + len(cols) > self.n_cols:
|
||||
if expand:
|
||||
self.expand_rows(col + len(cols) - self.n_cols)
|
||||
else:
|
||||
raise Exception('number of cols does not fit in table')
|
||||
for c in range(len(cols)):
|
||||
self.rows[row + r].cells[col + c] = Cell(data[r][c], fmt)
|
||||
|
||||
|
||||
class Row(object):
|
||||
def __init__(self, cells, pre_separator=None, post_separator=None):
|
||||
self.cells = cells
|
||||
self.pre_separator = pre_separator
|
||||
self.post_separator = post_separator
|
||||
def __init__(self, cells, pre_separator=None, post_separator=None):
|
||||
self.cells = cells
|
||||
self.pre_separator = pre_separator
|
||||
self.post_separator = post_separator
|
||||
|
||||
@classmethod
|
||||
def Empty(cls, n_cols):
|
||||
return Row([Cell() for c in range(n_cols)])
|
||||
@classmethod
|
||||
def Empty(cls, n_cols):
|
||||
return Row([Cell() for c in range(n_cols)])
|
||||
|
||||
def add_cell(self, cell):
|
||||
self.cells.append(cell)
|
||||
|
||||
def ncols(self):
|
||||
return sum([c.span for c in self.cells])
|
||||
def add_cell(self, cell):
|
||||
self.cells.append(cell)
|
||||
|
||||
def ncols(self):
|
||||
return sum([c.span for c in self.cells])
|
||||
|
||||
|
||||
class Color(object):
|
||||
def __init__(self, color=(0,0,0), fmt='rgb'):
|
||||
if fmt == 'rgb':
|
||||
self.color = color
|
||||
elif fmt == 'RGB':
|
||||
self.color = tuple(c / 255 for c in color)
|
||||
else:
|
||||
return Exception('invalid color format')
|
||||
def __init__(self, color=(0, 0, 0), fmt='rgb'):
|
||||
if fmt == 'rgb':
|
||||
self.color = color
|
||||
elif fmt == 'RGB':
|
||||
self.color = tuple(c / 255 for c in color)
|
||||
else:
|
||||
return Exception('invalid color format')
|
||||
|
||||
def as_rgb(self):
|
||||
return self.color
|
||||
def as_rgb(self):
|
||||
return self.color
|
||||
|
||||
def as_RGB(self):
|
||||
return tuple(int(c * 255) for c in self.color)
|
||||
def as_RGB(self):
|
||||
return tuple(int(c * 255) for c in self.color)
|
||||
|
||||
@classmethod
|
||||
def rgb(cls, r, g, b):
|
||||
return Color(color=(r,g,b), fmt='rgb')
|
||||
@classmethod
|
||||
def rgb(cls, r, g, b):
|
||||
return Color(color=(r, g, b), fmt='rgb')
|
||||
|
||||
@classmethod
|
||||
def RGB(cls, r, g, b):
|
||||
return Color(color=(r,g,b), fmt='RGB')
|
||||
@classmethod
|
||||
def RGB(cls, r, g, b):
|
||||
return Color(color=(r, g, b), fmt='RGB')
|
||||
|
||||
|
||||
class CellFormat(object):
|
||||
def __init__(self, fmt='%s', fgcolor=None, bgcolor=None, bold=False):
|
||||
self.fmt = fmt
|
||||
self.fgcolor = fgcolor
|
||||
self.bgcolor = bgcolor
|
||||
self.bold = bold
|
||||
def __init__(self, fmt='%s', fgcolor=None, bgcolor=None, bold=False):
|
||||
self.fmt = fmt
|
||||
self.fgcolor = fgcolor
|
||||
self.bgcolor = bgcolor
|
||||
self.bold = bold
|
||||
|
||||
|
||||
class Cell(object):
|
||||
def __init__(self, data=None, fmt=None, span=1, align=None):
|
||||
self.data = data
|
||||
if fmt is None:
|
||||
fmt = CellFormat()
|
||||
self.fmt = fmt
|
||||
self.span = span
|
||||
self.align = align
|
||||
def __init__(self, data=None, fmt=None, span=1, align=None):
|
||||
self.data = data
|
||||
if fmt is None:
|
||||
fmt = CellFormat()
|
||||
self.fmt = fmt
|
||||
self.span = span
|
||||
self.align = align
|
||||
|
||||
def __str__(self):
|
||||
return self.fmt.fmt % self.data
|
||||
|
||||
def __str__(self):
|
||||
return self.fmt.fmt % self.data
|
||||
|
||||
class Separator(enum.Enum):
|
||||
HEAD = 1
|
||||
BOTTOM = 2
|
||||
INNER = 3
|
||||
HEAD = 1
|
||||
BOTTOM = 2
|
||||
INNER = 3
|
||||
|
||||
|
||||
class Renderer(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def cell_str_len(self, cell):
|
||||
return len(str(cell))
|
||||
def cell_str_len(self, cell):
|
||||
return len(str(cell))
|
||||
|
||||
def col_widths(self, table):
|
||||
widths = [0 for c in range(table.n_cols)]
|
||||
for row in table.rows:
|
||||
cidx = 0
|
||||
for cell in row.cells:
|
||||
if cell.span == 1:
|
||||
strlen = self.cell_str_len(cell)
|
||||
widths[cidx] = max(widths[cidx], strlen)
|
||||
cidx += cell.span
|
||||
return widths
|
||||
def col_widths(self, table):
|
||||
widths = [0 for c in range(table.n_cols)]
|
||||
for row in table.rows:
|
||||
cidx = 0
|
||||
for cell in row.cells:
|
||||
if cell.span == 1:
|
||||
strlen = self.cell_str_len(cell)
|
||||
widths[cidx] = max(widths[cidx], strlen)
|
||||
cidx += cell.span
|
||||
return widths
|
||||
|
||||
def render(self, table):
|
||||
raise NotImplementedError('not implemented')
|
||||
def render(self, table):
|
||||
raise NotImplementedError('not implemented')
|
||||
|
||||
def __call__(self, table):
|
||||
return self.render(table)
|
||||
def __call__(self, table):
|
||||
return self.render(table)
|
||||
|
||||
def render_to_file_comment(self):
|
||||
return ''
|
||||
def render_to_file_comment(self):
|
||||
return ''
|
||||
|
||||
def render_to_file(self, path, table):
|
||||
txt = self.render(table)
|
||||
with open(path, 'w') as fp:
|
||||
fp.write(txt)
|
||||
|
||||
def render_to_file(self, path, table):
|
||||
txt = self.render(table)
|
||||
with open(path, 'w') as fp:
|
||||
fp.write(txt)
|
||||
|
||||
class TerminalRenderer(Renderer):
|
||||
def __init__(self, col_sep=' '):
|
||||
super().__init__()
|
||||
self.col_sep = col_sep
|
||||
def __init__(self, col_sep=' '):
|
||||
super().__init__()
|
||||
self.col_sep = col_sep
|
||||
|
||||
def render_cell(self, table, row, col, widths):
|
||||
cell = table.rows[row].cells[col]
|
||||
str = cell.fmt.fmt % cell.data
|
||||
str_width = len(str)
|
||||
cell_width = sum([widths[idx] for idx in range(col, col+cell.span)])
|
||||
cell_width += len(self.col_sep) * (cell.span - 1)
|
||||
if len(str) > cell_width:
|
||||
str = str[:cell_width]
|
||||
if cell.fmt.bold:
|
||||
# str = sty.ef.bold + str + sty.rs.bold_dim
|
||||
# str = sty.ef.bold + str + sty.rs.bold
|
||||
pass
|
||||
if cell.fmt.fgcolor is not None:
|
||||
# color = cell.fmt.fgcolor.as_RGB()
|
||||
# str = sty.fg(*color) + str + sty.rs.fg
|
||||
pass
|
||||
if str_width < cell_width:
|
||||
n_ws = (cell_width - str_width)
|
||||
if table.get_cell_align(row, col) == 'r':
|
||||
str = ' '*n_ws + str
|
||||
elif table.get_cell_align(row, col) == 'l':
|
||||
str = str + ' '*n_ws
|
||||
elif table.get_cell_align(row, col) == 'c':
|
||||
n_ws1 = n_ws // 2
|
||||
n_ws0 = n_ws - n_ws1
|
||||
str = ' '*n_ws0 + str + ' '*n_ws1
|
||||
if cell.fmt.bgcolor is not None:
|
||||
# color = cell.fmt.bgcolor.as_RGB()
|
||||
# str = sty.bg(*color) + str + sty.rs.bg
|
||||
pass
|
||||
return str
|
||||
def render_cell(self, table, row, col, widths):
|
||||
cell = table.rows[row].cells[col]
|
||||
str = cell.fmt.fmt % cell.data
|
||||
str_width = len(str)
|
||||
cell_width = sum([widths[idx] for idx in range(col, col + cell.span)])
|
||||
cell_width += len(self.col_sep) * (cell.span - 1)
|
||||
if len(str) > cell_width:
|
||||
str = str[:cell_width]
|
||||
if cell.fmt.bold:
|
||||
# str = sty.ef.bold + str + sty.rs.bold_dim
|
||||
# str = sty.ef.bold + str + sty.rs.bold
|
||||
pass
|
||||
if cell.fmt.fgcolor is not None:
|
||||
# color = cell.fmt.fgcolor.as_RGB()
|
||||
# str = sty.fg(*color) + str + sty.rs.fg
|
||||
pass
|
||||
if str_width < cell_width:
|
||||
n_ws = (cell_width - str_width)
|
||||
if table.get_cell_align(row, col) == 'r':
|
||||
str = ' ' * n_ws + str
|
||||
elif table.get_cell_align(row, col) == 'l':
|
||||
str = str + ' ' * n_ws
|
||||
elif table.get_cell_align(row, col) == 'c':
|
||||
n_ws1 = n_ws // 2
|
||||
n_ws0 = n_ws - n_ws1
|
||||
str = ' ' * n_ws0 + str + ' ' * n_ws1
|
||||
if cell.fmt.bgcolor is not None:
|
||||
# color = cell.fmt.bgcolor.as_RGB()
|
||||
# str = sty.bg(*color) + str + sty.rs.bg
|
||||
pass
|
||||
return str
|
||||
|
||||
def render_separator(self, separator, tab, col_widths, total_width):
|
||||
if separator == Separator.HEAD:
|
||||
return '='*total_width
|
||||
elif separator == Separator.INNER:
|
||||
return '-'*total_width
|
||||
elif separator == Separator.BOTTOM:
|
||||
return '='*total_width
|
||||
def render_separator(self, separator, tab, col_widths, total_width):
|
||||
if separator == Separator.HEAD:
|
||||
return '=' * total_width
|
||||
elif separator == Separator.INNER:
|
||||
return '-' * total_width
|
||||
elif separator == Separator.BOTTOM:
|
||||
return '=' * total_width
|
||||
|
||||
def render(self, table):
|
||||
widths = self.col_widths(table)
|
||||
total_width = sum(widths) + len(self.col_sep) * (table.n_cols - 1)
|
||||
lines = []
|
||||
for ridx, row in enumerate(table.rows):
|
||||
if row.pre_separator is not None:
|
||||
sepline = self.render_separator(row.pre_separator, table, widths, total_width)
|
||||
if len(sepline) > 0:
|
||||
lines.append(sepline)
|
||||
line = []
|
||||
for cidx, cell in enumerate(row.cells):
|
||||
line.append(self.render_cell(table, ridx, cidx, widths))
|
||||
lines.append(self.col_sep.join(line))
|
||||
if row.post_separator is not None:
|
||||
sepline = self.render_separator(row.post_separator, table, widths, total_width)
|
||||
if len(sepline) > 0:
|
||||
lines.append(sepline)
|
||||
return '\n'.join(lines)
|
||||
|
||||
def render(self, table):
|
||||
widths = self.col_widths(table)
|
||||
total_width = sum(widths) + len(self.col_sep) * (table.n_cols - 1)
|
||||
lines = []
|
||||
for ridx, row in enumerate(table.rows):
|
||||
if row.pre_separator is not None:
|
||||
sepline = self.render_separator(row.pre_separator, table, widths, total_width)
|
||||
if len(sepline) > 0:
|
||||
lines.append(sepline)
|
||||
line = []
|
||||
for cidx, cell in enumerate(row.cells):
|
||||
line.append(self.render_cell(table, ridx, cidx, widths))
|
||||
lines.append(self.col_sep.join(line))
|
||||
if row.post_separator is not None:
|
||||
sepline = self.render_separator(row.post_separator, table, widths, total_width)
|
||||
if len(sepline) > 0:
|
||||
lines.append(sepline)
|
||||
return '\n'.join(lines)
|
||||
|
||||
class MarkdownRenderer(TerminalRenderer):
|
||||
def __init__(self):
|
||||
super().__init__(col_sep='|')
|
||||
self.printed_color_warning = False
|
||||
def __init__(self):
|
||||
super().__init__(col_sep='|')
|
||||
self.printed_color_warning = False
|
||||
|
||||
def print_color_warning(self):
|
||||
if not self.printed_color_warning:
|
||||
print('[WARNING] MarkdownRenderer does not support color yet')
|
||||
self.printed_color_warning = True
|
||||
def print_color_warning(self):
|
||||
if not self.printed_color_warning:
|
||||
print('[WARNING] MarkdownRenderer does not support color yet')
|
||||
self.printed_color_warning = True
|
||||
|
||||
def cell_str_len(self, cell):
|
||||
strlen = len(str(cell))
|
||||
if cell.fmt.bold:
|
||||
strlen += 4
|
||||
strlen = max(5, strlen)
|
||||
return strlen
|
||||
def cell_str_len(self, cell):
|
||||
strlen = len(str(cell))
|
||||
if cell.fmt.bold:
|
||||
strlen += 4
|
||||
strlen = max(5, strlen)
|
||||
return strlen
|
||||
|
||||
def render_cell(self, table, row, col, widths):
|
||||
cell = table.rows[row].cells[col]
|
||||
str = cell.fmt.fmt % cell.data
|
||||
if cell.fmt.bold:
|
||||
str = f'**{str}**'
|
||||
def render_cell(self, table, row, col, widths):
|
||||
cell = table.rows[row].cells[col]
|
||||
str = cell.fmt.fmt % cell.data
|
||||
if cell.fmt.bold:
|
||||
str = f'**{str}**'
|
||||
|
||||
str_width = len(str)
|
||||
cell_width = sum([widths[idx] for idx in range(col, col+cell.span)])
|
||||
cell_width += len(self.col_sep) * (cell.span - 1)
|
||||
if len(str) > cell_width:
|
||||
str = str[:cell_width]
|
||||
else:
|
||||
n_ws = (cell_width - str_width)
|
||||
if table.get_cell_align(row, col) == 'r':
|
||||
str = ' '*n_ws + str
|
||||
elif table.get_cell_align(row, col) == 'l':
|
||||
str = str + ' '*n_ws
|
||||
elif table.get_cell_align(row, col) == 'c':
|
||||
n_ws1 = n_ws // 2
|
||||
n_ws0 = n_ws - n_ws1
|
||||
str = ' '*n_ws0 + str + ' '*n_ws1
|
||||
str_width = len(str)
|
||||
cell_width = sum([widths[idx] for idx in range(col, col + cell.span)])
|
||||
cell_width += len(self.col_sep) * (cell.span - 1)
|
||||
if len(str) > cell_width:
|
||||
str = str[:cell_width]
|
||||
else:
|
||||
n_ws = (cell_width - str_width)
|
||||
if table.get_cell_align(row, col) == 'r':
|
||||
str = ' ' * n_ws + str
|
||||
elif table.get_cell_align(row, col) == 'l':
|
||||
str = str + ' ' * n_ws
|
||||
elif table.get_cell_align(row, col) == 'c':
|
||||
n_ws1 = n_ws // 2
|
||||
n_ws0 = n_ws - n_ws1
|
||||
str = ' ' * n_ws0 + str + ' ' * n_ws1
|
||||
|
||||
if col == 0: str = self.col_sep + str
|
||||
if col == table.n_cols - 1: str += self.col_sep
|
||||
if col == 0: str = self.col_sep + str
|
||||
if col == table.n_cols - 1: str += self.col_sep
|
||||
|
||||
if cell.fmt.fgcolor is not None:
|
||||
self.print_color_warning()
|
||||
if cell.fmt.bgcolor is not None:
|
||||
self.print_color_warning()
|
||||
return str
|
||||
if cell.fmt.fgcolor is not None:
|
||||
self.print_color_warning()
|
||||
if cell.fmt.bgcolor is not None:
|
||||
self.print_color_warning()
|
||||
return str
|
||||
|
||||
def render_separator(self, separator, tab, widths, total_width):
|
||||
sep = ''
|
||||
if separator == Separator.INNER:
|
||||
sep = self.col_sep
|
||||
for idx, width in enumerate(widths):
|
||||
csep = '-' * (width - 2)
|
||||
if tab.get_cell_align(1, idx) == 'r':
|
||||
csep = '-' + csep + ':'
|
||||
elif tab.get_cell_align(1, idx) == 'l':
|
||||
csep = ':' + csep + '-'
|
||||
elif tab.get_cell_align(1, idx) == 'c':
|
||||
csep = ':' + csep + ':'
|
||||
sep += csep + self.col_sep
|
||||
return sep
|
||||
def render_separator(self, separator, tab, widths, total_width):
|
||||
sep = ''
|
||||
if separator == Separator.INNER:
|
||||
sep = self.col_sep
|
||||
for idx, width in enumerate(widths):
|
||||
csep = '-' * (width - 2)
|
||||
if tab.get_cell_align(1, idx) == 'r':
|
||||
csep = '-' + csep + ':'
|
||||
elif tab.get_cell_align(1, idx) == 'l':
|
||||
csep = ':' + csep + '-'
|
||||
elif tab.get_cell_align(1, idx) == 'c':
|
||||
csep = ':' + csep + ':'
|
||||
sep += csep + self.col_sep
|
||||
return sep
|
||||
|
||||
|
||||
class LatexRenderer(Renderer):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def render_cell(self, table, row, col):
|
||||
cell = table.rows[row].cells[col]
|
||||
str = cell.fmt.fmt % cell.data
|
||||
if cell.fmt.bold:
|
||||
str = '{\\bf '+ str + '}'
|
||||
if cell.fmt.fgcolor is not None:
|
||||
color = cell.fmt.fgcolor.as_rgb()
|
||||
str = f'{{\\color[rgb]{{{color[0]},{color[1]},{color[2]}}} ' + str + '}'
|
||||
if cell.fmt.bgcolor is not None:
|
||||
color = cell.fmt.bgcolor.as_rgb()
|
||||
str = f'\\cellcolor[rgb]{{{color[0]},{color[1]},{color[2]}}} ' + str
|
||||
align = table.get_cell_align(row, col)
|
||||
if cell.span != 1 or align != table.aligns[col]:
|
||||
str = f'\\multicolumn{{{cell.span}}}{{{align}}}{{{str}}}'
|
||||
return str
|
||||
def render_cell(self, table, row, col):
|
||||
cell = table.rows[row].cells[col]
|
||||
str = cell.fmt.fmt % cell.data
|
||||
if cell.fmt.bold:
|
||||
str = '{\\bf ' + str + '}'
|
||||
if cell.fmt.fgcolor is not None:
|
||||
color = cell.fmt.fgcolor.as_rgb()
|
||||
str = f'{{\\color[rgb]{{{color[0]},{color[1]},{color[2]}}} ' + str + '}'
|
||||
if cell.fmt.bgcolor is not None:
|
||||
color = cell.fmt.bgcolor.as_rgb()
|
||||
str = f'\\cellcolor[rgb]{{{color[0]},{color[1]},{color[2]}}} ' + str
|
||||
align = table.get_cell_align(row, col)
|
||||
if cell.span != 1 or align != table.aligns[col]:
|
||||
str = f'\\multicolumn{{{cell.span}}}{{{align}}}{{{str}}}'
|
||||
return str
|
||||
|
||||
def render_separator(self, separator):
|
||||
if separator == Separator.HEAD:
|
||||
return '\\toprule'
|
||||
elif separator == Separator.INNER:
|
||||
return '\\midrule'
|
||||
elif separator == Separator.BOTTOM:
|
||||
return '\\bottomrule'
|
||||
def render_separator(self, separator):
|
||||
if separator == Separator.HEAD:
|
||||
return '\\toprule'
|
||||
elif separator == Separator.INNER:
|
||||
return '\\midrule'
|
||||
elif separator == Separator.BOTTOM:
|
||||
return '\\bottomrule'
|
||||
|
||||
def render(self, table):
|
||||
lines = ['\\begin{tabular}{' + ''.join(table.aligns) + '}']
|
||||
for ridx, row in enumerate(table.rows):
|
||||
if row.pre_separator is not None:
|
||||
lines.append(self.render_separator(row.pre_separator))
|
||||
line = []
|
||||
for cidx, cell in enumerate(row.cells):
|
||||
line.append(self.render_cell(table, ridx, cidx))
|
||||
lines.append(' & '.join(line) + ' \\\\')
|
||||
if row.post_separator is not None:
|
||||
lines.append(self.render_separator(row.post_separator))
|
||||
lines.append('\\end{tabular}')
|
||||
return '\n'.join(lines)
|
||||
|
||||
def render(self, table):
|
||||
lines = ['\\begin{tabular}{' + ''.join(table.aligns) + '}']
|
||||
for ridx, row in enumerate(table.rows):
|
||||
if row.pre_separator is not None:
|
||||
lines.append(self.render_separator(row.pre_separator))
|
||||
line = []
|
||||
for cidx, cell in enumerate(row.cells):
|
||||
line.append(self.render_cell(table, ridx, cidx))
|
||||
lines.append(' & '.join(line) + ' \\\\')
|
||||
if row.post_separator is not None:
|
||||
lines.append(self.render_separator(row.post_separator))
|
||||
lines.append('\\end{tabular}')
|
||||
return '\n'.join(lines)
|
||||
|
||||
class HtmlRenderer(Renderer):
|
||||
def __init__(self, html_class='result_table'):
|
||||
super().__init__()
|
||||
self.html_class = html_class
|
||||
def __init__(self, html_class='result_table'):
|
||||
super().__init__()
|
||||
self.html_class = html_class
|
||||
|
||||
def render_cell(self, table, row, col):
|
||||
cell = table.rows[row].cells[col]
|
||||
str = cell.fmt.fmt % cell.data
|
||||
styles = []
|
||||
if cell.fmt.bold:
|
||||
styles.append('font-weight: bold;')
|
||||
if cell.fmt.fgcolor is not None:
|
||||
color = cell.fmt.fgcolor.as_RGB()
|
||||
styles.append(f'color: rgb({color[0]},{color[1]},{color[2]});')
|
||||
if cell.fmt.bgcolor is not None:
|
||||
color = cell.fmt.bgcolor.as_RGB()
|
||||
styles.append(f'background-color: rgb({color[0]},{color[1]},{color[2]});')
|
||||
align = table.get_cell_align(row, col)
|
||||
if align == 'l': align = 'left'
|
||||
elif align == 'r': align = 'right'
|
||||
elif align == 'c': align = 'center'
|
||||
else: raise Exception('invalid align')
|
||||
styles.append(f'text-align: {align};')
|
||||
row = table.rows[row]
|
||||
if row.pre_separator is not None:
|
||||
styles.append(f'border-top: {self.render_separator(row.pre_separator)};')
|
||||
if row.post_separator is not None:
|
||||
styles.append(f'border-bottom: {self.render_separator(row.post_separator)};')
|
||||
style = ' '.join(styles)
|
||||
str = f' <td style="{style}" colspan="{cell.span}">{str}</td>\n'
|
||||
return str
|
||||
def render_cell(self, table, row, col):
|
||||
cell = table.rows[row].cells[col]
|
||||
str = cell.fmt.fmt % cell.data
|
||||
styles = []
|
||||
if cell.fmt.bold:
|
||||
styles.append('font-weight: bold;')
|
||||
if cell.fmt.fgcolor is not None:
|
||||
color = cell.fmt.fgcolor.as_RGB()
|
||||
styles.append(f'color: rgb({color[0]},{color[1]},{color[2]});')
|
||||
if cell.fmt.bgcolor is not None:
|
||||
color = cell.fmt.bgcolor.as_RGB()
|
||||
styles.append(f'background-color: rgb({color[0]},{color[1]},{color[2]});')
|
||||
align = table.get_cell_align(row, col)
|
||||
if align == 'l':
|
||||
align = 'left'
|
||||
elif align == 'r':
|
||||
align = 'right'
|
||||
elif align == 'c':
|
||||
align = 'center'
|
||||
else:
|
||||
raise Exception('invalid align')
|
||||
styles.append(f'text-align: {align};')
|
||||
row = table.rows[row]
|
||||
if row.pre_separator is not None:
|
||||
styles.append(f'border-top: {self.render_separator(row.pre_separator)};')
|
||||
if row.post_separator is not None:
|
||||
styles.append(f'border-bottom: {self.render_separator(row.post_separator)};')
|
||||
style = ' '.join(styles)
|
||||
str = f' <td style="{style}" colspan="{cell.span}">{str}</td>\n'
|
||||
return str
|
||||
|
||||
def render_separator(self, separator):
|
||||
if separator == Separator.HEAD:
|
||||
return '1.5pt solid black'
|
||||
elif separator == Separator.INNER:
|
||||
return '0.75pt solid black'
|
||||
elif separator == Separator.BOTTOM:
|
||||
return '1.5pt solid black'
|
||||
def render_separator(self, separator):
|
||||
if separator == Separator.HEAD:
|
||||
return '1.5pt solid black'
|
||||
elif separator == Separator.INNER:
|
||||
return '0.75pt solid black'
|
||||
elif separator == Separator.BOTTOM:
|
||||
return '1.5pt solid black'
|
||||
|
||||
def render(self, table):
|
||||
lines = [f'<table width="100%" style="border-collapse: collapse" class={self.html_class}>']
|
||||
for ridx, row in enumerate(table.rows):
|
||||
line = [f' <tr>\n']
|
||||
for cidx, cell in enumerate(row.cells):
|
||||
line.append(self.render_cell(table, ridx, cidx))
|
||||
line.append(' </tr>\n')
|
||||
lines.append(' '.join(line))
|
||||
lines.append('</table>')
|
||||
return '\n'.join(lines)
|
||||
def render(self, table):
|
||||
lines = [f'<table width="100%" style="border-collapse: collapse" class={self.html_class}>']
|
||||
for ridx, row in enumerate(table.rows):
|
||||
line = [f' <tr>\n']
|
||||
for cidx, cell in enumerate(row.cells):
|
||||
line.append(self.render_cell(table, ridx, cidx))
|
||||
line.append(' </tr>\n')
|
||||
lines.append(' '.join(line))
|
||||
lines.append('</table>')
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
def pandas_to_table(rowname, colname, valname, data, val_cell_fmt=CellFormat(fmt='%.4f'), best_val_cell_fmt=CellFormat(fmt='%.4f', bold=True), best_is_max=[]):
|
||||
rnames = data[rowname].unique()
|
||||
cnames = data[colname].unique()
|
||||
tab = Table(1+len(cnames))
|
||||
def pandas_to_table(rowname, colname, valname, data, val_cell_fmt=CellFormat(fmt='%.4f'),
|
||||
best_val_cell_fmt=CellFormat(fmt='%.4f', bold=True), best_is_max=[]):
|
||||
rnames = data[rowname].unique()
|
||||
cnames = data[colname].unique()
|
||||
tab = Table(1 + len(cnames))
|
||||
|
||||
header = [Cell('', align='r')]
|
||||
header.extend([Cell(h, align='r') for h in cnames])
|
||||
header = Row(header, pre_separator=Separator.HEAD, post_separator=Separator.INNER)
|
||||
tab.add_row(header)
|
||||
|
||||
for rname in rnames:
|
||||
cells = [Cell(rname, align='l')]
|
||||
for cname in cnames:
|
||||
cdata = data[data[colname] == cname]
|
||||
if cname in best_is_max:
|
||||
bestval = cdata[valname].max()
|
||||
val = cdata[cdata[rowname] == rname][valname].max()
|
||||
else:
|
||||
bestval = cdata[valname].min()
|
||||
val = cdata[cdata[rowname] == rname][valname].min()
|
||||
if val == bestval:
|
||||
fmt = best_val_cell_fmt
|
||||
else:
|
||||
fmt = val_cell_fmt
|
||||
cells.append(Cell(val, align='r', fmt=fmt))
|
||||
tab.add_row(Row(cells))
|
||||
tab.rows[-1].post_separator = Separator.BOTTOM
|
||||
return tab
|
||||
header = [Cell('', align='r')]
|
||||
header.extend([Cell(h, align='r') for h in cnames])
|
||||
header = Row(header, pre_separator=Separator.HEAD, post_separator=Separator.INNER)
|
||||
tab.add_row(header)
|
||||
|
||||
for rname in rnames:
|
||||
cells = [Cell(rname, align='l')]
|
||||
for cname in cnames:
|
||||
cdata = data[data[colname] == cname]
|
||||
if cname in best_is_max:
|
||||
bestval = cdata[valname].max()
|
||||
val = cdata[cdata[rowname] == rname][valname].max()
|
||||
else:
|
||||
bestval = cdata[valname].min()
|
||||
val = cdata[cdata[rowname] == rname][valname].min()
|
||||
if val == bestval:
|
||||
fmt = best_val_cell_fmt
|
||||
else:
|
||||
fmt = val_cell_fmt
|
||||
cells.append(Cell(val, align='r', fmt=fmt))
|
||||
tab.add_row(Row(cells))
|
||||
tab.rows[-1].post_separator = Separator.BOTTOM
|
||||
return tab
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# df = pd.read_pickle('full.df')
|
||||
# best_is_max = ['movF0.5', 'movF1.0']
|
||||
# tab = pandas_to_table(rowname='method', colname='metric', valname='val', data=df, best_is_max=best_is_max)
|
||||
# df = pd.read_pickle('full.df')
|
||||
# best_is_max = ['movF0.5', 'movF1.0']
|
||||
# tab = pandas_to_table(rowname='method', colname='metric', valname='val', data=df, best_is_max=best_is_max)
|
||||
|
||||
# renderer = TerminalRenderer()
|
||||
# print(renderer(tab))
|
||||
# renderer = TerminalRenderer()
|
||||
# print(renderer(tab))
|
||||
|
||||
tab = Table(7)
|
||||
# header = Row([Cell('header', span=7, align='c')], pre_separator=Separator.HEAD, post_separator=Separator.INNER)
|
||||
# tab.add_row(header)
|
||||
# header2 = Row([Cell('thisisaverylongheader', span=4, align='c'), Cell('vals2', span=3, align='c')], post_separator=Separator.INNER)
|
||||
# tab.add_row(header2)
|
||||
tab.add_row(Row([Cell(f'c{c}') for c in range(7)]))
|
||||
tab.rows[-1].post_separator = Separator.INNER
|
||||
tab.add_block(np.arange(15*7).reshape(15,7))
|
||||
tab.rows[4].cells[2].fmt = CellFormat(bold=True)
|
||||
tab.rows[2].cells[1].fmt = CellFormat(fgcolor=Color.rgb(0.2,0.6,0.1))
|
||||
tab.rows[2].cells[2].fmt = CellFormat(bgcolor=Color.rgb(0.7,0.1,0.5))
|
||||
tab.rows[5].cells[3].fmt = CellFormat(bold=True,bgcolor=Color.rgb(0.7,0.1,0.5),fgcolor=Color.rgb(0.1,0.1,0.1))
|
||||
tab.rows[-1].post_separator = Separator.BOTTOM
|
||||
tab = Table(7)
|
||||
# header = Row([Cell('header', span=7, align='c')], pre_separator=Separator.HEAD, post_separator=Separator.INNER)
|
||||
# tab.add_row(header)
|
||||
# header2 = Row([Cell('thisisaverylongheader', span=4, align='c'), Cell('vals2', span=3, align='c')], post_separator=Separator.INNER)
|
||||
# tab.add_row(header2)
|
||||
tab.add_row(Row([Cell(f'c{c}') for c in range(7)]))
|
||||
tab.rows[-1].post_separator = Separator.INNER
|
||||
tab.add_block(np.arange(15 * 7).reshape(15, 7))
|
||||
tab.rows[4].cells[2].fmt = CellFormat(bold=True)
|
||||
tab.rows[2].cells[1].fmt = CellFormat(fgcolor=Color.rgb(0.2, 0.6, 0.1))
|
||||
tab.rows[2].cells[2].fmt = CellFormat(bgcolor=Color.rgb(0.7, 0.1, 0.5))
|
||||
tab.rows[5].cells[3].fmt = CellFormat(bold=True, bgcolor=Color.rgb(0.7, 0.1, 0.5), fgcolor=Color.rgb(0.1, 0.1, 0.1))
|
||||
tab.rows[-1].post_separator = Separator.BOTTOM
|
||||
|
||||
renderer = TerminalRenderer()
|
||||
print(renderer(tab))
|
||||
renderer = MarkdownRenderer()
|
||||
print(renderer(tab))
|
||||
renderer = TerminalRenderer()
|
||||
print(renderer(tab))
|
||||
renderer = MarkdownRenderer()
|
||||
print(renderer(tab))
|
||||
|
||||
# renderer = HtmlRenderer()
|
||||
# html_tab = renderer(tab)
|
||||
# print(html_tab)
|
||||
# with open('test.html', 'w') as fp:
|
||||
# fp.write(html_tab)
|
||||
# renderer = HtmlRenderer()
|
||||
# html_tab = renderer(tab)
|
||||
# print(html_tab)
|
||||
# with open('test.html', 'w') as fp:
|
||||
# fp.write(html_tab)
|
||||
|
||||
# import latex
|
||||
# import latex
|
||||
|
||||
# renderer = LatexRenderer()
|
||||
# ltx_tab = renderer(tab)
|
||||
# print(ltx_tab)
|
||||
# renderer = LatexRenderer()
|
||||
# ltx_tab = renderer(tab)
|
||||
# print(ltx_tab)
|
||||
|
||||
# with open('test.tex', 'w') as fp:
|
||||
# latex.write_doc_prefix(fp, document_class='article')
|
||||
# fp.write('this is text that should appear before the table and should be long enough to wrap around.\n'*40)
|
||||
# fp.write('\\begin{table}')
|
||||
# fp.write(ltx_tab)
|
||||
# fp.write('\\end{table}')
|
||||
# fp.write('this is text that should appear after the table and should be long enough to wrap around.\n'*40)
|
||||
# latex.write_doc_suffix(fp)
|
||||
# with open('test.tex', 'w') as fp:
|
||||
# latex.write_doc_prefix(fp, document_class='article')
|
||||
# fp.write('this is text that should appear before the table and should be long enough to wrap around.\n'*40)
|
||||
# fp.write('\\begin{table}')
|
||||
# fp.write(ltx_tab)
|
||||
# fp.write('\\end{table}')
|
||||
# fp.write('this is text that should appear after the table and should be long enough to wrap around.\n'*40)
|
||||
# latex.write_doc_suffix(fp)
|
||||
|
||||
+56
-52
@@ -8,6 +8,7 @@ import re
|
||||
import pickle
|
||||
import subprocess
|
||||
|
||||
|
||||
def str2bool(v):
|
||||
if v.lower() in ('yes', 'true', 't', 'y', '1'):
|
||||
return True
|
||||
@@ -16,71 +17,74 @@ def str2bool(v):
|
||||
else:
|
||||
raise argparse.ArgumentTypeError('Boolean value expected.')
|
||||
|
||||
|
||||
class StopWatch(object):
|
||||
def __init__(self):
|
||||
self.timings = OrderedDict()
|
||||
self.starts = {}
|
||||
def __init__(self):
|
||||
self.timings = OrderedDict()
|
||||
self.starts = {}
|
||||
|
||||
def start(self, name):
|
||||
self.starts[name] = time.time()
|
||||
def start(self, name):
|
||||
self.starts[name] = time.time()
|
||||
|
||||
def stop(self, name):
|
||||
if name not in self.timings:
|
||||
self.timings[name] = []
|
||||
self.timings[name].append(time.time() - self.starts[name])
|
||||
def stop(self, name):
|
||||
if name not in self.timings:
|
||||
self.timings[name] = []
|
||||
self.timings[name].append(time.time() - self.starts[name])
|
||||
|
||||
def get(self, name=None, reduce=np.sum):
|
||||
if name is not None:
|
||||
return reduce(self.timings[name])
|
||||
else:
|
||||
ret = {}
|
||||
for k in self.timings:
|
||||
ret[k] = reduce(self.timings[k])
|
||||
return ret
|
||||
def get(self, name=None, reduce=np.sum):
|
||||
if name is not None:
|
||||
return reduce(self.timings[name])
|
||||
else:
|
||||
ret = {}
|
||||
for k in self.timings:
|
||||
ret[k] = reduce(self.timings[k])
|
||||
return ret
|
||||
|
||||
def __repr__(self):
|
||||
return ', '.join(['%s: %f[s]' % (k, v) for k, v in self.get().items()])
|
||||
|
||||
def __str__(self):
|
||||
return ', '.join(['%s: %f[s]' % (k, v) for k, v in self.get().items()])
|
||||
|
||||
def __repr__(self):
|
||||
return ', '.join(['%s: %f[s]' % (k,v) for k,v in self.get().items()])
|
||||
def __str__(self):
|
||||
return ', '.join(['%s: %f[s]' % (k,v) for k,v in self.get().items()])
|
||||
|
||||
class ETA(object):
|
||||
def __init__(self, length):
|
||||
self.length = length
|
||||
self.start_time = time.time()
|
||||
self.current_idx = 0
|
||||
self.current_time = time.time()
|
||||
def __init__(self, length):
|
||||
self.length = length
|
||||
self.start_time = time.time()
|
||||
self.current_idx = 0
|
||||
self.current_time = time.time()
|
||||
|
||||
def update(self, idx):
|
||||
self.current_idx = idx
|
||||
self.current_time = time.time()
|
||||
def update(self, idx):
|
||||
self.current_idx = idx
|
||||
self.current_time = time.time()
|
||||
|
||||
def get_elapsed_time(self):
|
||||
return self.current_time - self.start_time
|
||||
def get_elapsed_time(self):
|
||||
return self.current_time - self.start_time
|
||||
|
||||
def get_item_time(self):
|
||||
return self.get_elapsed_time() / (self.current_idx + 1)
|
||||
def get_item_time(self):
|
||||
return self.get_elapsed_time() / (self.current_idx + 1)
|
||||
|
||||
def get_remaining_time(self):
|
||||
return self.get_item_time() * (self.length - self.current_idx + 1)
|
||||
def get_remaining_time(self):
|
||||
return self.get_item_time() * (self.length - self.current_idx + 1)
|
||||
|
||||
def format_time(self, seconds):
|
||||
minutes, seconds = divmod(seconds, 60)
|
||||
hours, minutes = divmod(minutes, 60)
|
||||
hours = int(hours)
|
||||
minutes = int(minutes)
|
||||
return f'{hours:02d}:{minutes:02d}:{seconds:05.2f}'
|
||||
def format_time(self, seconds):
|
||||
minutes, seconds = divmod(seconds, 60)
|
||||
hours, minutes = divmod(minutes, 60)
|
||||
hours = int(hours)
|
||||
minutes = int(minutes)
|
||||
return f'{hours:02d}:{minutes:02d}:{seconds:05.2f}'
|
||||
|
||||
def get_elapsed_time_str(self):
|
||||
return self.format_time(self.get_elapsed_time())
|
||||
def get_elapsed_time_str(self):
|
||||
return self.format_time(self.get_elapsed_time())
|
||||
|
||||
def get_remaining_time_str(self):
|
||||
return self.format_time(self.get_remaining_time())
|
||||
|
||||
def get_remaining_time_str(self):
|
||||
return self.format_time(self.get_remaining_time())
|
||||
|
||||
def git_hash(cwd=None):
|
||||
ret = subprocess.run(['git', 'describe', '--always'], cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
hash = ret.stdout
|
||||
if hash is not None and 'fatal' not in hash.decode():
|
||||
return hash.decode().strip()
|
||||
else:
|
||||
return None
|
||||
|
||||
ret = subprocess.run(['git', 'describe', '--always'], cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
hash = ret.stdout
|
||||
if hash is not None and 'fatal' not in hash.decode():
|
||||
return hash.decode().strip()
|
||||
else:
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user