Denys Rozumnyi commited on
Commit
a83935b
1 Parent(s): 69a667d
__pycache__/geom_solver.cpython-39.pyc ADDED
Binary file (4.96 kB). View file
 
__pycache__/handcrafted_solution.cpython-39.pyc ADDED
Binary file (7.94 kB). View file
 
__pycache__/helpers.cpython-39.pyc ADDED
Binary file (1.04 kB). View file
 
__pycache__/my_solution.cpython-39.pyc ADDED
Binary file (1.94 kB). View file
 
geom_solver.py ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from pytorch3d.ops import ball_query
3
+ from helpers import *
4
+ from handcrafted_solution import *
5
+ import hoho
6
+ import itertools
7
+ import torch
8
+ from pytorch3d.renderer import PerspectiveCameras
9
+
10
+
11
+ class GeomSolver(object):
12
+
13
+ def __init__(self, entry):
14
+ human_entry = convert_entry_to_human_readable(entry)
15
+ self.human_entry = human_entry
16
+
17
+ col_cams = [hoho.Rt_to_eye_target(human_entry['ade20k'][0], to_K(*human_entry['cameras'][1].params), quaternion_to_rotation_matrix(colmap_img.qvec), colmap_img.tvec) for colmap_img in human_entry['images'].values()]
18
+ eye, target, up, fov = col_cams[0]
19
+
20
+ cameras, images, points3D = human_entry['cameras'], human_entry['images'], human_entry['points3d']
21
+ xyz = np.stack([p.xyz for p in points3D.values()])
22
+ color = np.stack([p.rgb for p in points3D.values()])
23
+
24
+ gestalt_camcet = np.stack([eye for eye, target, up, fov in itertools.starmap(hoho.Rt_to_eye_target, zip(*[human_entry[k] for k in 'ade20k K R t'.split()]))])
25
+ col_camcet = np.stack([eye for eye, target, up, fov in col_cams])
26
+ gestalt_to_colmap_cams = [np.argmin(((gcam - col_camcet)**2).sum(1)**0.5)+1 for gcam in gestalt_camcet]
27
+
28
+
29
+ # def get_vertices(self):
30
+ clr_th = 2.5
31
+ device = 'cuda:0'
32
+ height = cameras[1].height
33
+ width = cameras[1].width
34
+ N = len(gestalt_to_colmap_cams)
35
+ K = to_K(*human_entry['cameras'][1].params)[None].repeat(N, 0)
36
+ R = np.stack([quaternion_to_rotation_matrix(human_entry['images'][gestalt_to_colmap_cams[ind]].qvec) for ind in range(N)])
37
+ T = np.stack([human_entry['images'][gestalt_to_colmap_cams[ind]].tvec for ind in range(N)])
38
+
39
+ R = np.linalg.inv(R)
40
+ image_size=torch.Tensor([height, width]).repeat(N, 1)
41
+ pyt_cameras = PerspectiveCameras(device=device, R=R, T=T, in_ndc=False, focal_length=K[:, 0, :1], principal_point=K[:, :2, 2], image_size=image_size)
42
+
43
+ verts = torch.from_numpy(xyz.astype(np.float32)).to(device)
44
+
45
+ apex_color = np.array(gestalt_color_mapping['apex'])
46
+ eave_end_color = np.array(gestalt_color_mapping['eave_end_point'])
47
+
48
+ dist_points = np.zeros((xyz.shape[0], ))
49
+ visible_counts = np.zeros((xyz.shape[0], ), dtype=int)
50
+ proj_uv = []
51
+ for ki in range(N):
52
+ cki = gestalt_to_colmap_cams[ki]
53
+
54
+ gest = np.array(human_entry['gestalt'][ki])
55
+ apex_mask = cv2.inRange(gest, apex_color-clr_th, apex_color+clr_th)
56
+ eave_end_mask = cv2.inRange(gest, eave_end_color-clr_th, eave_end_color+clr_th)
57
+ vert_mask = apex_mask + eave_end_mask
58
+ vert_mask = (vert_mask > 0).astype(np.uint8)
59
+
60
+ dist = cv2.distanceTransform(1-vert_mask, cv2.DIST_L2, 3)
61
+ dist[dist > 100] = 100
62
+ ndist = np.zeros_like(dist)
63
+ ndist = cv2.normalize(dist, ndist, 0, 1.0, cv2.NORM_MINMAX)
64
+
65
+ in_this_image = np.array([cki in p.image_ids for p in points3D.values()])
66
+ # tempind = 2103
67
+ # print(in_this_image[tempind-1], cki, points3D[tempind].image_ids)
68
+ uv = torch.round(pyt_cameras[ki].transform_points(verts)[:, :2]).cpu().numpy().astype(int)
69
+ uv_inl = (uv[:, 0] >= 0) * (uv[:, 1] >= 0) * (uv[:, 0] < width) * (uv[:, 1] < height) * in_this_image
70
+ proj_uv.append((uv, uv_inl))
71
+ uv = uv[uv_inl]
72
+
73
+ dist_points[uv_inl] += dist[uv[:,1], uv[:,0]]
74
+ visible_counts[uv_inl] += 1
75
+
76
+ selected_points = (dist_points / (visible_counts + 1e-6)) <= 10
77
+ selected_points[visible_counts < 1] = False
78
+
79
+
80
+ pnts = torch.from_numpy(xyz[selected_points].astype(np.float32))[None]
81
+ bdists, inds, nn = ball_query(pnts, pnts, K=3, radius=30)
82
+ dense_pnts = (bdists[0] > 0).sum(1) == 2
83
+
84
+
85
+ criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 200, 0.3)
86
+ flags = cv2.KMEANS_RANDOM_CENTERS
87
+ centers = None
88
+ kmeans_th = 150
89
+ for tempi in range(1,11):
90
+ retval, bestLabels, temp_centers = cv2.kmeans(xyz[selected_points][dense_pnts].astype(np.float32), tempi, None, criteria, 200,flags)
91
+ cpnts = torch.from_numpy(temp_centers.astype(np.float32))[None]
92
+ bdists, inds, nn = ball_query(cpnts, cpnts, K=1, radius=100)
93
+ if bdists.max() > 0:
94
+ closest_nn = (bdists[bdists>0].min()**0.5).item()
95
+ else:
96
+ closest_nn = kmeans_th
97
+ if closest_nn < kmeans_th:
98
+ break
99
+ centers = temp_centers
100
+ # image_ids = np.array([p.id for p in points3D.values()])
101
+ # pyt_centers = torch.from_numpy(centers).to(device)
102
+
103
+ self.vertices = centers
104
+
105
+
106
+ def get_vertices(self, visualize=False):
107
+ if visualize:
108
+ from hoho.viz3d import plot_estimate_and_gt
109
+ plot_estimate_and_gt(self.vertices, [(0,1)], self.human_entry['wf_vertices'], self.human_entry['wf_edges'])
110
+ if self.vertices.shape[0] == 0:
111
+ return my_empty_solution()
112
+ return self.vertices, []
113
+
114
+
115
+
116
+
handcrafted_solution.py ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Description: This file contains the handcrafted solution for the task of wireframe reconstruction
2
+
3
+ import io
4
+ from PIL import Image as PImage
5
+ import numpy as np
6
+ from collections import defaultdict
7
+ import cv2
8
+ from typing import Tuple, List
9
+ from scipy.spatial.distance import cdist
10
+
11
+ from hoho.read_write_colmap import read_cameras_binary, read_images_binary, read_points3D_binary
12
+ from hoho.color_mappings import gestalt_color_mapping, ade20k_color_mapping
13
+
14
+
15
+ def empty_solution():
16
+ '''Return a minimal valid solution, i.e. 2 vertices and 1 edge.'''
17
+ return np.zeros((2,3)), [(0, 1)]
18
+
19
+
20
+ def convert_entry_to_human_readable(entry):
21
+ out = {}
22
+ already_good = ['__key__', 'wf_vertices', 'wf_edges', 'edge_semantics', 'mesh_vertices', 'mesh_faces', 'face_semantics', 'K', 'R', 't']
23
+ for k, v in entry.items():
24
+ if k in already_good:
25
+ out[k] = v
26
+ continue
27
+ if k == 'points3d':
28
+ out[k] = read_points3D_binary(fid=io.BytesIO(v))
29
+ if k == 'cameras':
30
+ out[k] = read_cameras_binary(fid=io.BytesIO(v))
31
+ if k == 'images':
32
+ out[k] = read_images_binary(fid=io.BytesIO(v))
33
+ if k in ['ade20k', 'gestalt']:
34
+ out[k] = [PImage.open(io.BytesIO(x)).convert('RGB') for x in v]
35
+ if k == 'depthcm':
36
+ out[k] = [PImage.open(io.BytesIO(x)) for x in entry['depthcm']]
37
+ return out
38
+
39
+
40
+ def get_vertices_and_edges_from_segmentation(gest_seg_np, edge_th = 50.0):
41
+ '''Get the vertices and edges from the gestalt segmentation mask of the house'''
42
+ vertices = []
43
+ connections = []
44
+ # Apex
45
+ apex_color = np.array(gestalt_color_mapping['apex'])
46
+ apex_mask = cv2.inRange(gest_seg_np, apex_color-0.5, apex_color+0.5)
47
+ if apex_mask.sum() > 0:
48
+ output = cv2.connectedComponentsWithStats(apex_mask, 8, cv2.CV_32S)
49
+ (numLabels, labels, stats, centroids) = output
50
+ stats, centroids = stats[1:], centroids[1:]
51
+
52
+ for i in range(numLabels-1):
53
+ vert = {"xy": centroids[i], "type": "apex"}
54
+ vertices.append(vert)
55
+
56
+ eave_end_color = np.array(gestalt_color_mapping['eave_end_point'])
57
+ eave_end_mask = cv2.inRange(gest_seg_np, eave_end_color-0.5, eave_end_color+0.5)
58
+ if eave_end_mask.sum() > 0:
59
+ output = cv2.connectedComponentsWithStats(eave_end_mask, 8, cv2.CV_32S)
60
+ (numLabels, labels, stats, centroids) = output
61
+ stats, centroids = stats[1:], centroids[1:]
62
+
63
+ for i in range(numLabels-1):
64
+ vert = {"xy": centroids[i], "type": "eave_end_point"}
65
+ vertices.append(vert)
66
+ # Connectivity
67
+ apex_pts = []
68
+ apex_pts_idxs = []
69
+ for j, v in enumerate(vertices):
70
+ apex_pts.append(v['xy'])
71
+ apex_pts_idxs.append(j)
72
+ apex_pts = np.array(apex_pts)
73
+
74
+ # Ridge connects two apex points
75
+ for edge_class in ['eave', 'ridge', 'rake', 'valley']:
76
+ edge_color = np.array(gestalt_color_mapping[edge_class])
77
+ mask = cv2.morphologyEx(cv2.inRange(gest_seg_np,
78
+ edge_color-0.5,
79
+ edge_color+0.5),
80
+ cv2.MORPH_DILATE, np.ones((11, 11)))
81
+ line_img = np.copy(gest_seg_np) * 0
82
+ if mask.sum() > 0:
83
+ output = cv2.connectedComponentsWithStats(mask, 8, cv2.CV_32S)
84
+ (numLabels, labels, stats, centroids) = output
85
+ stats, centroids = stats[1:], centroids[1:]
86
+ edges = []
87
+ for i in range(1, numLabels):
88
+ y,x = np.where(labels == i)
89
+ xleft_idx = np.argmin(x)
90
+ x_left = x[xleft_idx]
91
+ y_left = y[xleft_idx]
92
+ xright_idx = np.argmax(x)
93
+ x_right = x[xright_idx]
94
+ y_right = y[xright_idx]
95
+ edges.append((x_left, y_left, x_right, y_right))
96
+ cv2.line(line_img, (x_left, y_left), (x_right, y_right), (255, 255, 255), 2)
97
+ edges = np.array(edges)
98
+ if (len(apex_pts) < 2) or len(edges) <1:
99
+ continue
100
+ pts_to_edges_dist = np.minimum(cdist(apex_pts, edges[:,:2]), cdist(apex_pts, edges[:,2:]))
101
+ connectivity_mask = pts_to_edges_dist <= edge_th
102
+ edge_connects = connectivity_mask.sum(axis=0)
103
+ for edge_idx, edgesum in enumerate(edge_connects):
104
+ if edgesum>=2:
105
+ connected_verts = np.where(connectivity_mask[:,edge_idx])[0]
106
+ for a_i, a in enumerate(connected_verts):
107
+ for b in connected_verts[a_i+1:]:
108
+ connections.append((a, b))
109
+ return vertices, connections
110
+
111
+ def get_uv_depth(vertices, depth):
112
+ '''Get the depth of the vertices from the depth image'''
113
+ uv = []
114
+ for v in vertices:
115
+ uv.append(v['xy'])
116
+ uv = np.array(uv)
117
+ uv_int = uv.astype(np.int32)
118
+ H, W = depth.shape[:2]
119
+ uv_int[:, 0] = np.clip( uv_int[:, 0], 0, W-1)
120
+ uv_int[:, 1] = np.clip( uv_int[:, 1], 0, H-1)
121
+ vertex_depth = depth[(uv_int[:, 1] , uv_int[:, 0])]
122
+ return uv, vertex_depth
123
+
124
+
125
+ def merge_vertices_3d(vert_edge_per_image, th=0.1):
126
+ '''Merge vertices that are close to each other in 3D space and are of same types'''
127
+ all_3d_vertices = []
128
+ connections_3d = []
129
+ all_indexes = []
130
+ cur_start = 0
131
+ types = []
132
+ for cimg_idx, (vertices, connections, vertices_3d) in vert_edge_per_image.items():
133
+ types += [int(v['type']=='apex') for v in vertices]
134
+ all_3d_vertices.append(vertices_3d)
135
+ connections_3d+=[(x+cur_start,y+cur_start) for (x,y) in connections]
136
+ cur_start+=len(vertices_3d)
137
+ all_3d_vertices = np.concatenate(all_3d_vertices, axis=0)
138
+ #print (connections_3d)
139
+ distmat = cdist(all_3d_vertices, all_3d_vertices)
140
+ types = np.array(types).reshape(-1,1)
141
+ same_types = cdist(types, types)
142
+ mask_to_merge = (distmat <= th) & (same_types==0)
143
+ new_vertices = []
144
+ new_connections = []
145
+ to_merge = sorted(list(set([tuple(a.nonzero()[0].tolist()) for a in mask_to_merge])))
146
+ to_merge_final = defaultdict(list)
147
+ for i in range(len(all_3d_vertices)):
148
+ for j in to_merge:
149
+ if i in j:
150
+ to_merge_final[i]+=j
151
+ for k, v in to_merge_final.items():
152
+ to_merge_final[k] = list(set(v))
153
+ already_there = set()
154
+ merged = []
155
+ for k, v in to_merge_final.items():
156
+ if k in already_there:
157
+ continue
158
+ merged.append(v)
159
+ for vv in v:
160
+ already_there.add(vv)
161
+ old_idx_to_new = {}
162
+ count=0
163
+ for idxs in merged:
164
+ new_vertices.append(all_3d_vertices[idxs].mean(axis=0))
165
+ for idx in idxs:
166
+ old_idx_to_new[idx] = count
167
+ count +=1
168
+ #print (connections_3d)
169
+ new_vertices=np.array(new_vertices)
170
+ #print (connections_3d)
171
+ for conn in connections_3d:
172
+ new_con = sorted((old_idx_to_new[conn[0]], old_idx_to_new[conn[1]]))
173
+ if new_con[0] == new_con[1]:
174
+ continue
175
+ if new_con not in new_connections:
176
+ new_connections.append(new_con)
177
+ #print (f'{len(new_vertices)} left after merging {len(all_3d_vertices)} with {th=}')
178
+ return new_vertices, new_connections
179
+
180
+ def prune_not_connected(all_3d_vertices, connections_3d):
181
+ '''Prune vertices that are not connected to any other vertex'''
182
+ connected = defaultdict(list)
183
+ for c in connections_3d:
184
+ connected[c[0]].append(c)
185
+ connected[c[1]].append(c)
186
+ new_indexes = {}
187
+ new_verts = []
188
+ connected_out = []
189
+ for k,v in connected.items():
190
+ vert = all_3d_vertices[k]
191
+ if tuple(vert) not in new_verts:
192
+ new_verts.append(tuple(vert))
193
+ new_indexes[k]=len(new_verts) -1
194
+ for k,v in connected.items():
195
+ for vv in v:
196
+ connected_out.append((new_indexes[vv[0]],new_indexes[vv[1]]))
197
+ connected_out=list(set(connected_out))
198
+
199
+ return np.array(new_verts), connected_out
200
+
201
+
202
+ def predict(entry, visualize=False) -> Tuple[np.ndarray, List[int]]:
203
+ good_entry = convert_entry_to_human_readable(entry)
204
+ vert_edge_per_image = {}
205
+ for i, (gest, depth, K, R, t) in enumerate(zip(good_entry['gestalt'],
206
+ good_entry['depthcm'],
207
+ good_entry['K'],
208
+ good_entry['R'],
209
+ good_entry['t']
210
+ )):
211
+ gest_seg = gest.resize(depth.size)
212
+ gest_seg_np = np.array(gest_seg).astype(np.uint8)
213
+ # Metric3D
214
+ depth_np = np.array(depth) / 2.5 # 2.5 is the scale estimation coefficient
215
+ vertices, connections = get_vertices_and_edges_from_segmentation(gest_seg_np, edge_th = 20.)
216
+ if (len(vertices) < 2) or (len(connections) < 1):
217
+ print (f'Not enough vertices or connections in image {i}')
218
+ vert_edge_per_image[i] = np.empty((0, 2)), [], np.empty((0, 3))
219
+ continue
220
+ uv, depth_vert = get_uv_depth(vertices, depth_np)
221
+ # Normalize the uv to the camera intrinsics
222
+ xy_local = np.ones((len(uv), 3))
223
+ xy_local[:, 0] = (uv[:, 0] - K[0,2]) / K[0,0]
224
+ xy_local[:, 1] = (uv[:, 1] - K[1,2]) / K[1,1]
225
+ # Get the 3D vertices
226
+ vertices_3d_local = depth_vert[...,None] * (xy_local/np.linalg.norm(xy_local, axis=1)[...,None])
227
+ world_to_cam = np.eye(4)
228
+ world_to_cam[:3, :3] = R
229
+ world_to_cam[:3, 3] = t.reshape(-1)
230
+ cam_to_world = np.linalg.inv(world_to_cam)
231
+ vertices_3d = cv2.transform(cv2.convertPointsToHomogeneous(vertices_3d_local), cam_to_world)
232
+ vertices_3d = cv2.convertPointsFromHomogeneous(vertices_3d).reshape(-1, 3)
233
+ vert_edge_per_image[i] = vertices, connections, vertices_3d
234
+ all_3d_vertices, connections_3d = merge_vertices_3d(vert_edge_per_image, 3.0)
235
+ all_3d_vertices_clean, connections_3d_clean = prune_not_connected(all_3d_vertices, connections_3d)
236
+ if (len(all_3d_vertices_clean) < 2) or len(connections_3d_clean) < 1:
237
+ print (f'Not enough vertices or connections in the 3D vertices')
238
+ return (good_entry['__key__'], *empty_solution())
239
+ if visualize:
240
+ from hoho.viz3d import plot_estimate_and_gt
241
+ plot_estimate_and_gt( all_3d_vertices_clean,
242
+ connections_3d_clean,
243
+ good_entry['wf_vertices'],
244
+ good_entry['wf_edges'])
245
+ return good_entry['__key__'], all_3d_vertices_clean, connections_3d_clean
helpers.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from PIL import Image as PImage
3
+ import io
4
+
5
+
6
+ def my_empty_solution():
7
+ '''Return a minimal valid solution, i.e. 1 vertices and 0 edge.'''
8
+ return np.zeros((1,3)), []
9
+
10
+
11
+ def to_K(f, cx, cy):
12
+ K = np.eye(3)
13
+ K[0,0] = K[1,1] = f
14
+ K[0,2] = cx
15
+ K[1,2] = cy
16
+ return K
17
+
18
+
19
+ def quaternion_to_rotation_matrix(qvec):
20
+ qw, qx, qy, qz = qvec
21
+ R = np.array([
22
+ [1 - 2*qy**2 - 2*qz**2, 2*qx*qy - 2*qz*qw, 2*qx*qz + 2*qy*qw],
23
+ [2*qx*qy + 2*qz*qw, 1 - 2*qx**2 - 2*qz**2, 2*qy*qz - 2*qx*qw],
24
+ [2*qx*qz - 2*qy*qw, 2*qy*qz + 2*qx*qw, 1 - 2*qx**2 - 2*qy**2]
25
+ ])
26
+ return R
27
+
my_solution.py CHANGED
@@ -11,11 +11,8 @@ from scipy.spatial.distance import cdist
11
  from hoho.read_write_colmap import read_cameras_binary, read_images_binary, read_points3D_binary
12
  from hoho.color_mappings import gestalt_color_mapping, ade20k_color_mapping
13
 
14
-
15
- def empty_solution():
16
- '''Return a minimal valid solution, i.e. 2 vertices and 1 edge.'''
17
- return np.zeros((2,3)), [(0, 1)]
18
-
19
 
20
  def convert_entry_to_human_readable(entry):
21
  out = {}
@@ -37,209 +34,15 @@ def convert_entry_to_human_readable(entry):
37
  return out
38
 
39
 
40
- def get_vertices_and_edges_from_segmentation(gest_seg_np, edge_th = 50.0):
41
- '''Get the vertices and edges from the gestalt segmentation mask of the house'''
42
- vertices = []
43
- connections = []
44
- # Apex
45
- apex_color = np.array(gestalt_color_mapping['apex'])
46
- apex_mask = cv2.inRange(gest_seg_np, apex_color-0.5, apex_color+0.5)
47
- if apex_mask.sum() > 0:
48
- output = cv2.connectedComponentsWithStats(apex_mask, 8, cv2.CV_32S)
49
- (numLabels, labels, stats, centroids) = output
50
- stats, centroids = stats[1:], centroids[1:]
51
-
52
- for i in range(numLabels-1):
53
- vert = {"xy": centroids[i], "type": "apex"}
54
- vertices.append(vert)
55
-
56
- eave_end_color = np.array(gestalt_color_mapping['eave_end_point'])
57
- eave_end_mask = cv2.inRange(gest_seg_np, eave_end_color-0.5, eave_end_color+0.5)
58
- if eave_end_mask.sum() > 0:
59
- output = cv2.connectedComponentsWithStats(eave_end_mask, 8, cv2.CV_32S)
60
- (numLabels, labels, stats, centroids) = output
61
- stats, centroids = stats[1:], centroids[1:]
62
-
63
- for i in range(numLabels-1):
64
- vert = {"xy": centroids[i], "type": "eave_end_point"}
65
- vertices.append(vert)
66
- # Connectivity
67
- apex_pts = []
68
- apex_pts_idxs = []
69
- for j, v in enumerate(vertices):
70
- apex_pts.append(v['xy'])
71
- apex_pts_idxs.append(j)
72
- apex_pts = np.array(apex_pts)
73
-
74
- # Ridge connects two apex points
75
- for edge_class in ['eave', 'ridge', 'rake', 'valley']:
76
- edge_color = np.array(gestalt_color_mapping[edge_class])
77
- mask = cv2.morphologyEx(cv2.inRange(gest_seg_np,
78
- edge_color-0.5,
79
- edge_color+0.5),
80
- cv2.MORPH_DILATE, np.ones((11, 11)))
81
- line_img = np.copy(gest_seg_np) * 0
82
- if mask.sum() > 0:
83
- output = cv2.connectedComponentsWithStats(mask, 8, cv2.CV_32S)
84
- (numLabels, labels, stats, centroids) = output
85
- stats, centroids = stats[1:], centroids[1:]
86
- edges = []
87
- for i in range(1, numLabels):
88
- y,x = np.where(labels == i)
89
- xleft_idx = np.argmin(x)
90
- x_left = x[xleft_idx]
91
- y_left = y[xleft_idx]
92
- xright_idx = np.argmax(x)
93
- x_right = x[xright_idx]
94
- y_right = y[xright_idx]
95
- edges.append((x_left, y_left, x_right, y_right))
96
- cv2.line(line_img, (x_left, y_left), (x_right, y_right), (255, 255, 255), 2)
97
- edges = np.array(edges)
98
- if (len(apex_pts) < 2) or len(edges) <1:
99
- continue
100
- pts_to_edges_dist = np.minimum(cdist(apex_pts, edges[:,:2]), cdist(apex_pts, edges[:,2:]))
101
- connectivity_mask = pts_to_edges_dist <= edge_th
102
- edge_connects = connectivity_mask.sum(axis=0)
103
- for edge_idx, edgesum in enumerate(edge_connects):
104
- if edgesum>=2:
105
- connected_verts = np.where(connectivity_mask[:,edge_idx])[0]
106
- for a_i, a in enumerate(connected_verts):
107
- for b in connected_verts[a_i+1:]:
108
- connections.append((a, b))
109
- return vertices, connections
110
-
111
- def get_uv_depth(vertices, depth):
112
- '''Get the depth of the vertices from the depth image'''
113
- uv = []
114
- for v in vertices:
115
- uv.append(v['xy'])
116
- uv = np.array(uv)
117
- uv_int = uv.astype(np.int32)
118
- H, W = depth.shape[:2]
119
- uv_int[:, 0] = np.clip( uv_int[:, 0], 0, W-1)
120
- uv_int[:, 1] = np.clip( uv_int[:, 1], 0, H-1)
121
- vertex_depth = depth[(uv_int[:, 1] , uv_int[:, 0])]
122
- return uv, vertex_depth
123
-
124
-
125
- def merge_vertices_3d(vert_edge_per_image, th=0.1):
126
- '''Merge vertices that are close to each other in 3D space and are of same types'''
127
- all_3d_vertices = []
128
- connections_3d = []
129
- all_indexes = []
130
- cur_start = 0
131
- types = []
132
- for cimg_idx, (vertices, connections, vertices_3d) in vert_edge_per_image.items():
133
- types += [int(v['type']=='apex') for v in vertices]
134
- all_3d_vertices.append(vertices_3d)
135
- connections_3d+=[(x+cur_start,y+cur_start) for (x,y) in connections]
136
- cur_start+=len(vertices_3d)
137
- all_3d_vertices = np.concatenate(all_3d_vertices, axis=0)
138
- #print (connections_3d)
139
- distmat = cdist(all_3d_vertices, all_3d_vertices)
140
- types = np.array(types).reshape(-1,1)
141
- same_types = cdist(types, types)
142
- mask_to_merge = (distmat <= th) & (same_types==0)
143
- new_vertices = []
144
- new_connections = []
145
- to_merge = sorted(list(set([tuple(a.nonzero()[0].tolist()) for a in mask_to_merge])))
146
- to_merge_final = defaultdict(list)
147
- for i in range(len(all_3d_vertices)):
148
- for j in to_merge:
149
- if i in j:
150
- to_merge_final[i]+=j
151
- for k, v in to_merge_final.items():
152
- to_merge_final[k] = list(set(v))
153
- already_there = set()
154
- merged = []
155
- for k, v in to_merge_final.items():
156
- if k in already_there:
157
- continue
158
- merged.append(v)
159
- for vv in v:
160
- already_there.add(vv)
161
- old_idx_to_new = {}
162
- count=0
163
- for idxs in merged:
164
- new_vertices.append(all_3d_vertices[idxs].mean(axis=0))
165
- for idx in idxs:
166
- old_idx_to_new[idx] = count
167
- count +=1
168
- #print (connections_3d)
169
- new_vertices=np.array(new_vertices)
170
- #print (connections_3d)
171
- for conn in connections_3d:
172
- new_con = sorted((old_idx_to_new[conn[0]], old_idx_to_new[conn[1]]))
173
- if new_con[0] == new_con[1]:
174
- continue
175
- if new_con not in new_connections:
176
- new_connections.append(new_con)
177
- #print (f'{len(new_vertices)} left after merging {len(all_3d_vertices)} with {th=}')
178
- return new_vertices, new_connections
179
-
180
- def prune_not_connected(all_3d_vertices, connections_3d):
181
- '''Prune vertices that are not connected to any other vertex'''
182
- connected = defaultdict(list)
183
- for c in connections_3d:
184
- connected[c[0]].append(c)
185
- connected[c[1]].append(c)
186
- new_indexes = {}
187
- new_verts = []
188
- connected_out = []
189
- for k,v in connected.items():
190
- vert = all_3d_vertices[k]
191
- if tuple(vert) not in new_verts:
192
- new_verts.append(tuple(vert))
193
- new_indexes[k]=len(new_verts) -1
194
- for k,v in connected.items():
195
- for vv in v:
196
- connected_out.append((new_indexes[vv[0]],new_indexes[vv[1]]))
197
- connected_out=list(set(connected_out))
198
-
199
- return np.array(new_verts), connected_out
200
-
201
-
202
  def predict(entry, visualize=False) -> Tuple[np.ndarray, List[int]]:
203
- good_entry = convert_entry_to_human_readable(entry)
204
- vert_edge_per_image = {}
205
- for i, (gest, depth, K, R, t) in enumerate(zip(good_entry['gestalt'],
206
- good_entry['depthcm'],
207
- good_entry['K'],
208
- good_entry['R'],
209
- good_entry['t']
210
- )):
211
- gest_seg = gest.resize(depth.size)
212
- gest_seg_np = np.array(gest_seg).astype(np.uint8)
213
- # Metric3D
214
- depth_np = np.array(depth) / 2.5 # 2.5 is the scale estimation coefficient
215
- vertices, connections = get_vertices_and_edges_from_segmentation(gest_seg_np, edge_th = 20.)
216
- if (len(vertices) < 2) or (len(connections) < 1):
217
- print (f'Not enough vertices or connections in image {i}')
218
- vert_edge_per_image[i] = np.empty((0, 2)), [], np.empty((0, 3))
219
- continue
220
- uv, depth_vert = get_uv_depth(vertices, depth_np)
221
- # Normalize the uv to the camera intrinsics
222
- xy_local = np.ones((len(uv), 3))
223
- xy_local[:, 0] = (uv[:, 0] - K[0,2]) / K[0,0]
224
- xy_local[:, 1] = (uv[:, 1] - K[1,2]) / K[1,1]
225
- # Get the 3D vertices
226
- vertices_3d_local = depth_vert[...,None] * (xy_local/np.linalg.norm(xy_local, axis=1)[...,None])
227
- world_to_cam = np.eye(4)
228
- world_to_cam[:3, :3] = R
229
- world_to_cam[:3, 3] = t.reshape(-1)
230
- cam_to_world = np.linalg.inv(world_to_cam)
231
- vertices_3d = cv2.transform(cv2.convertPointsToHomogeneous(vertices_3d_local), cam_to_world)
232
- vertices_3d = cv2.convertPointsFromHomogeneous(vertices_3d).reshape(-1, 3)
233
- vert_edge_per_image[i] = vertices, connections, vertices_3d
234
- all_3d_vertices, connections_3d = merge_vertices_3d(vert_edge_per_image, 3.0)
235
- all_3d_vertices_clean, connections_3d_clean = prune_not_connected(all_3d_vertices, connections_3d)
236
- if (len(all_3d_vertices_clean) < 2) or len(connections_3d_clean) < 1:
237
- print (f'Not enough vertices or connections in the 3D vertices')
238
- return (good_entry['__key__'], *empty_solution())
239
  if visualize:
240
  from hoho.viz3d import plot_estimate_and_gt
241
- plot_estimate_and_gt( all_3d_vertices_clean,
242
- connections_3d_clean,
243
- good_entry['wf_vertices'],
244
- good_entry['wf_edges'])
245
- return good_entry['__key__'], all_3d_vertices_clean, connections_3d_clean
 
11
  from hoho.read_write_colmap import read_cameras_binary, read_images_binary, read_points3D_binary
12
  from hoho.color_mappings import gestalt_color_mapping, ade20k_color_mapping
13
 
14
+ from helpers import my_empty_solution
15
+ from geom_solver import GeomSolver
 
 
 
16
 
17
  def convert_entry_to_human_readable(entry):
18
  out = {}
 
34
  return out
35
 
36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  def predict(entry, visualize=False) -> Tuple[np.ndarray, List[int]]:
38
+ # return (entry['__key__'], *my_empty_solution())
39
+ solver = GeomSolver(entry)
40
+ vertices, edges = solver.get_vertices()
41
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  if visualize:
43
  from hoho.viz3d import plot_estimate_and_gt
44
+ plot_estimate_and_gt( vertices,
45
+ edges,
46
+ entry['wf_vertices'],
47
+ entry['wf_edges'])
48
+ return entry['__key__'], vertices, edges
script.py CHANGED
@@ -116,7 +116,7 @@ def save_submission(submission, path):
116
  print(f"Submission saved to {path}")
117
 
118
  if __name__ == "__main__":
119
- from handcrafted_solution import my_solution
120
  print ("------------ Loading dataset------------ ")
121
  params = hoho.get_params()
122
  dataset = hoho.get_dataset(decode=None, split='all', dataset_type='webdataset')
 
116
  print(f"Submission saved to {path}")
117
 
118
  if __name__ == "__main__":
119
+ from my_solution import predict
120
  print ("------------ Loading dataset------------ ")
121
  params = hoho.get_params()
122
  dataset = hoho.get_dataset(decode=None, split='all', dataset_type='webdataset')
testing.ipynb CHANGED
The diff for this file is too large to render. See raw diff