a lot of changes, I want to see how it performs (rewrote evaluation to load data in batches)
Browse files- handcrafted_solution.py +188 -90
- script.py +36 -23
handcrafted_solution.py
CHANGED
@@ -14,6 +14,12 @@ from hoho.read_write_colmap import read_cameras_binary, read_images_binary, read
|
|
14 |
from scipy.spatial import KDTree
|
15 |
from scipy.spatial.distance import cdist
|
16 |
from sklearn.cluster import DBSCAN
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
|
18 |
apex_color = gestalt_color_mapping["apex"]
|
19 |
eave_end_point = gestalt_color_mapping["eave_end_point"]
|
@@ -23,7 +29,24 @@ apex_color, eave_end_point, flashing_end_point = [np.array(i) for i in [apex_col
|
|
23 |
unclassified = np.array([(215, 62, 138)])
|
24 |
line_classes = ['eave', 'ridge', 'rake', 'valley']
|
25 |
|
26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
def empty_solution():
|
28 |
'''Return a minimal valid solution, i.e. 2 vertices and 1 edge.'''
|
29 |
return np.zeros((2, 3)), [(0, 1)]
|
@@ -70,15 +93,15 @@ def remove_undesired_objects(image):
|
|
70 |
def clean_image(image_gestalt) -> np.ndarray:
|
71 |
# clears image in from of unclassified and disconected components
|
72 |
image_gestalt = np.array(image_gestalt)
|
73 |
-
unclassified_mask = cv2.inRange(image_gestalt, unclassified
|
74 |
-
unclassified_mask = cv2.bitwise_not(unclassified_mask)
|
75 |
-
mask = remove_undesired_objects(unclassified_mask).astype(np.uint8)
|
76 |
-
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, np.ones((11, 11), np.uint8), iterations=11)
|
77 |
-
mask = cv2.morphologyEx(mask, cv2.MORPH_DILATE, np.ones((11, 11), np.uint8), iterations=2)
|
78 |
-
|
79 |
-
image_gestalt[:, :, 0] *= mask
|
80 |
-
image_gestalt[:, :, 1] *= mask
|
81 |
-
image_gestalt[:, :, 2] *= mask
|
82 |
return image_gestalt
|
83 |
|
84 |
|
@@ -98,10 +121,10 @@ def get_vertices(image_gestalt, *, color_range=4., dialations=3, erosions=1, ker
|
|
98 |
eave_end_point_mask = cv2.morphologyEx(eave_end_point_mask, cv2.MORPH_DILATE, kernel, iterations=dialations)
|
99 |
eave_end_point_mask = cv2.morphologyEx(eave_end_point_mask, cv2.MORPH_ERODE, kernel, iterations=erosions)
|
100 |
|
101 |
-
*_, apex_centroids = cv2.connectedComponentsWithStats(apex_mask, connectivity=4, stats=cv2.CV_32S)
|
102 |
-
*_, other_centroids = cv2.connectedComponentsWithStats(eave_end_point_mask, connectivity=4, stats=cv2.CV_32S)
|
103 |
|
104 |
-
return apex_centroids[1:], other_centroids[1:], apex_mask, eave_end_point_mask
|
105 |
|
106 |
|
107 |
def infer_vertices(image_gestalt, *, color_range=4.):
|
@@ -168,8 +191,8 @@ def get_lines_and_directions(gest_seg_np, edge_class, *, color_range=4., rho, th
|
|
168 |
|
169 |
direction = extend * direction
|
170 |
|
171 |
-
x1, y1 =
|
172 |
-
x2, y2 =
|
173 |
|
174 |
edges.append((x1, y1, x2, y2))
|
175 |
return edges, line_directions
|
@@ -189,14 +212,19 @@ def infer_missing_vertices(ridge_edges, rake_edges):
|
|
189 |
return ridge_ends.data[missing_candidates]
|
190 |
|
191 |
|
192 |
-
def get_vertices_and_edges_from_segmentation(gest_seg_np, *,
|
|
|
|
|
|
|
193 |
**kwargs):
|
194 |
'''Get the vertices and edges from the gestalt segmentation mask of the house'''
|
195 |
# Apex
|
196 |
connections = []
|
197 |
deviation_threshold = np.cos(np.deg2rad(max_angle))
|
198 |
|
199 |
-
apex_centroids, eave_end_point_centroids,
|
|
|
|
|
200 |
|
201 |
vertices = np.concatenate([apex_centroids, eave_end_point_centroids])
|
202 |
# inferred_vertices, inferred_mask = infer_vertices(gest_seg_np)
|
@@ -206,12 +234,6 @@ def get_vertices_and_edges_from_segmentation(gest_seg_np, *, point_radius=30, ma
|
|
206 |
if len(vertices) < 2:
|
207 |
return [], []
|
208 |
|
209 |
-
# scale = 1
|
210 |
-
# vertex_size = np.zeros(vertices.shape[0])
|
211 |
-
# for i, coords in enumerate(vertices):
|
212 |
-
# # coords = np.round(coords).astype(np.uint32)
|
213 |
-
# radius = point_radius # np.clip(int(max_depth//2 + depth_np[coords[1], coords[0]]), 10, 30)#int(np.clip(max_depth - depth_np[coords[1], coords[0]], 10, 20))
|
214 |
-
# vertex_size[i] = (scale * radius) ** 2 # because we are using squared distances
|
215 |
|
216 |
edges = []
|
217 |
line_directions = []
|
@@ -251,6 +273,19 @@ def get_vertices_and_edges_from_segmentation(gest_seg_np, *, point_radius=30, ma
|
|
251 |
missed_vertices = get_missed_vertices(vertices, inferred_vertices, **kwargs)
|
252 |
vertices = np.concatenate([vertices, missed_vertices])
|
253 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
254 |
vertices = KDTree(vertices)
|
255 |
|
256 |
for edge_class in ['eave',
|
@@ -291,12 +326,29 @@ def get_vertices_and_edges_from_segmentation(gest_seg_np, *, point_radius=30, ma
|
|
291 |
end_vertex_list = []
|
292 |
line_idx_list = []
|
293 |
for line_idx in line_indices:
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
298 |
|
299 |
-
|
|
|
|
|
|
|
|
|
300 |
|
301 |
line_idx_list = np.array(line_idx_list)
|
302 |
all_connections = np.array([begin_vertex_list, end_vertex_list])
|
@@ -334,9 +386,9 @@ def get_vertices_and_edges_from_segmentation(gest_seg_np, *, point_radius=30, ma
|
|
334 |
if np.any(line_deviations > deviation_threshold):
|
335 |
connections.append(possible_connections[:, fitted_line_idx])
|
336 |
|
337 |
-
vertices = [{"xy": v, "type":
|
338 |
-
vertices += [{"xy": v, "type":
|
339 |
-
vertices += [{"xy": v, "type":
|
340 |
return vertices, connections
|
341 |
|
342 |
|
@@ -376,7 +428,7 @@ def merge_vertices_3d(vert_edge_per_image, merge_th=0.1, **kwargs):
|
|
376 |
new_vertex_mapping = dict(zip(left_vertex_indices, new_indices))
|
377 |
|
378 |
vertices = [v for i, v in enumerate(vertices) if i in new_vertex_mapping]
|
379 |
-
types += [int(v['type'] ==
|
380 |
vertices_3d = vertices_3d[left_vertex_indices]
|
381 |
connections = [[new_vertex_mapping[a] + cur_start, new_vertex_mapping[b] + cur_start] for a, b in connections]
|
382 |
|
@@ -454,8 +506,13 @@ def prune_not_connected(all_3d_vertices, connections_3d):
|
|
454 |
return np.array(new_verts), connected_out
|
455 |
|
456 |
|
457 |
-
def predict(entry, visualize=False,
|
458 |
-
|
|
|
|
|
|
|
|
|
|
|
459 |
if 'gestalt' not in entry or 'depthcm' not in entry or 'K' not in entry or 'R' not in entry or 't' not in entry:
|
460 |
print('Missing required fields in the entry')
|
461 |
return (entry['__key__'], *empty_solution())
|
@@ -486,10 +543,13 @@ def predict(entry, visualize=False, scale_estimation_coefficient=2.5, clustering
|
|
486 |
clustered_keys = np.split(point_keys, cluster_indices[1:])
|
487 |
|
488 |
biggest_cluster_index = np.argmax([len(i) for i in clustered_points])
|
489 |
-
|
490 |
biggest_cluster_keys = clustered_keys[biggest_cluster_index]
|
491 |
biggest_cluster_keys = set(biggest_cluster_keys)
|
492 |
|
|
|
|
|
|
|
493 |
for i, (gest, depthcm, K, R, t, imagekey) in enumerate(zip(entry['gestalt'],
|
494 |
entry['depthcm'],
|
495 |
entry['K'],
|
@@ -498,89 +558,127 @@ def predict(entry, visualize=False, scale_estimation_coefficient=2.5, clustering
|
|
498 |
entry['__imagekey__']
|
499 |
)):
|
500 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
501 |
try:
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
print(f'Not enough vertices or connections in image {i}')
|
507 |
-
vert_edge_per_image[i] = np.empty((0, 2)), [], np.empty((0, 3))
|
508 |
-
continue
|
509 |
-
belonging_points = []
|
510 |
-
for point_id in image_dict[imagekey].point3D_ids[np.where(image_dict[imagekey].point3D_ids != -1)]:
|
511 |
if point_id in biggest_cluster_keys:
|
512 |
-
|
|
|
513 |
|
514 |
-
if len(
|
515 |
print(f'No 3D points in image {i}')
|
516 |
vert_edge_per_image[i] = np.empty((0, 2)), [], np.empty((0, 3))
|
517 |
raise KeyError
|
518 |
-
|
519 |
-
|
|
|
|
|
520 |
# Normalize the uv to the camera intrinsics
|
521 |
world_to_cam = np.eye(4)
|
522 |
world_to_cam[:3, :3] = R
|
523 |
world_to_cam[:3, 3] = t
|
524 |
|
525 |
-
homo_belonging_points = cv2.convertPointsToHomogeneous(
|
526 |
depth = cv2.convertPointsFromHomogeneous(cv2.transform(homo_belonging_points, world_to_cam))
|
527 |
depth = depth[:, 0, 2]
|
528 |
-
|
529 |
-
|
|
|
|
|
|
|
|
|
|
|
530 |
if len(depth) < 1:
|
531 |
print(f'No 3D points in image {i}')
|
532 |
vert_edge_per_image[i] = np.empty((0, 2)), [], np.empty((0, 3))
|
533 |
raise KeyError
|
534 |
# print(projected2d.shape, depth.shape)
|
535 |
|
536 |
-
interpolator = si.NearestNDInterpolator(projected2d, depth, rescale=True)
|
537 |
-
|
|
|
538 |
|
539 |
-
|
540 |
-
xi, yi =
|
541 |
-
|
542 |
-
|
543 |
-
xy_local[:, 0] = (vertex_coordinates[:, 0] - K[0, 2]) / K[0, 0]
|
544 |
-
xy_local[:, 1] = (vertex_coordinates[:, 1] - K[1, 2]) / K[1, 1]
|
545 |
-
# Get the 3D vertices
|
546 |
-
vertices_3d_local = depth_vert[..., None] * (xy_local / np.linalg.norm(xy_local, axis=1)[..., None])
|
547 |
-
world_to_cam = np.eye(4)
|
548 |
-
world_to_cam[:3, :3] = R
|
549 |
-
world_to_cam[:3, 3] = t.reshape(-1)
|
550 |
-
cam_to_world = np.linalg.inv(world_to_cam)
|
551 |
-
vertices_3d = cv2.transform(cv2.convertPointsToHomogeneous(vertices_3d_local), cam_to_world)
|
552 |
-
vertices_3d = cv2.convertPointsFromHomogeneous(vertices_3d).reshape(-1, 3)
|
553 |
|
|
|
554 |
except KeyError:
|
555 |
-
|
556 |
-
gest_seg_np = np.array(gest_seg).astype(np.uint8)
|
557 |
# Metric3D
|
558 |
-
|
559 |
-
|
560 |
-
# cv2.medianBlur(depth_np, 5)
|
561 |
-
# depth_np = np.zeros_like(depth_np)
|
562 |
-
vertices, connections = get_vertices_and_edges_from_segmentation(gest_seg_np, **kwargs)
|
563 |
-
if (len(vertices) < 2) or (len(connections) < 1):
|
564 |
-
print(f'Not enough vertices or connections in image {i}')
|
565 |
-
vert_edge_per_image[i] = np.empty((0, 2)), [], np.empty((0, 3))
|
566 |
-
continue
|
567 |
-
uv, depth_vert = get_uv_depth(vertices, depth_np)
|
568 |
# Normalize the uv to the camera intrinsics
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
580 |
|
581 |
vert_edge_per_image[i] = vertices, connections, vertices_3d
|
582 |
all_3d_vertices, connections_3d = merge_vertices_3d(vert_edge_per_image, **kwargs)
|
583 |
all_3d_vertices_clean, connections_3d_clean = all_3d_vertices, connections_3d
|
|
|
|
|
|
|
|
|
584 |
# all_3d_vertices_clean, connections_3d_clean = prune_not_connected(all_3d_vertices, connections_3d)
|
585 |
if (len(all_3d_vertices_clean) < 2) or len(connections_3d_clean) < 1:
|
586 |
print(f'Not enough vertices or connections in the 3D vertices')
|
|
|
14 |
from scipy.spatial import KDTree
|
15 |
from scipy.spatial.distance import cdist
|
16 |
from sklearn.cluster import DBSCAN
|
17 |
+
from scipy.spatial import cKDTree
|
18 |
+
|
19 |
+
from enum import Enum
|
20 |
+
|
21 |
+
|
22 |
+
|
23 |
|
24 |
apex_color = gestalt_color_mapping["apex"]
|
25 |
eave_end_point = gestalt_color_mapping["eave_end_point"]
|
|
|
29 |
unclassified = np.array([(215, 62, 138)])
|
30 |
line_classes = ['eave', 'ridge', 'rake', 'valley']
|
31 |
|
32 |
+
class VertexType(Enum):
|
33 |
+
APEX = 0
|
34 |
+
EAVE_END_POINT = 1
|
35 |
+
|
36 |
+
class NearestNDInterpolatorWithThreshold(si.NearestNDInterpolator):
|
37 |
+
def __init__(self, points, values, max_distance):
|
38 |
+
super().__init__(points, values)
|
39 |
+
self.max_distance = max_distance
|
40 |
+
self.tree = cKDTree(points)
|
41 |
+
|
42 |
+
def __call__(self, *args):
|
43 |
+
# Convert the input to a 2D array of query points
|
44 |
+
query_points = np.array(args).T
|
45 |
+
distances, indices = self.tree.query(query_points)
|
46 |
+
values = np.full(query_points.shape[:-1], np.nan)
|
47 |
+
valid_mask = distances <= self.max_distance
|
48 |
+
values[valid_mask] = self.values[indices[valid_mask]]
|
49 |
+
return values.T
|
50 |
def empty_solution():
|
51 |
'''Return a minimal valid solution, i.e. 2 vertices and 1 edge.'''
|
52 |
return np.zeros((2, 3)), [(0, 1)]
|
|
|
93 |
def clean_image(image_gestalt) -> np.ndarray:
|
94 |
# clears image in from of unclassified and disconected components
|
95 |
image_gestalt = np.array(image_gestalt)
|
96 |
+
# unclassified_mask = cv2.inRange(image_gestalt, unclassified - 1, unclassified + 1)
|
97 |
+
# unclassified_mask = cv2.bitwise_not(unclassified_mask)
|
98 |
+
# mask = remove_undesired_objects(unclassified_mask).astype(np.uint8)
|
99 |
+
# mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, np.ones((11, 11), np.uint8), iterations=11)
|
100 |
+
# mask = cv2.morphologyEx(mask, cv2.MORPH_DILATE, np.ones((11, 11), np.uint8), iterations=2)
|
101 |
+
|
102 |
+
# image_gestalt[:, :, 0] *= mask
|
103 |
+
# image_gestalt[:, :, 1] *= mask
|
104 |
+
# image_gestalt[:, :, 2] *= mask
|
105 |
return image_gestalt
|
106 |
|
107 |
|
|
|
121 |
eave_end_point_mask = cv2.morphologyEx(eave_end_point_mask, cv2.MORPH_DILATE, kernel, iterations=dialations)
|
122 |
eave_end_point_mask = cv2.morphologyEx(eave_end_point_mask, cv2.MORPH_ERODE, kernel, iterations=erosions)
|
123 |
|
124 |
+
*_, apex_stats, apex_centroids = cv2.connectedComponentsWithStats(apex_mask, connectivity=4, stats=cv2.CV_32S)
|
125 |
+
*_, other_stats, other_centroids = cv2.connectedComponentsWithStats(eave_end_point_mask, connectivity=4, stats=cv2.CV_32S)
|
126 |
|
127 |
+
return apex_centroids[1:], other_centroids[1:], apex_mask, eave_end_point_mask, apex_stats[1:, cv2.CC_STAT_WIDTH]/2, other_stats[1:, cv2.CC_STAT_WIDTH]/2
|
128 |
|
129 |
|
130 |
def infer_vertices(image_gestalt, *, color_range=4.):
|
|
|
191 |
|
192 |
direction = extend * direction
|
193 |
|
194 |
+
x1, y1 = -direction + (x1, y1)
|
195 |
+
x2, y2 = + direction + (x2, y2)
|
196 |
|
197 |
edges.append((x1, y1, x2, y2))
|
198 |
return edges, line_directions
|
|
|
212 |
return ridge_ends.data[missing_candidates]
|
213 |
|
214 |
|
215 |
+
def get_vertices_and_edges_from_segmentation(gest_seg_np, *,
|
216 |
+
point_radius=30,
|
217 |
+
max_angle=5.,
|
218 |
+
point_radius_scale=1,
|
219 |
**kwargs):
|
220 |
'''Get the vertices and edges from the gestalt segmentation mask of the house'''
|
221 |
# Apex
|
222 |
connections = []
|
223 |
deviation_threshold = np.cos(np.deg2rad(max_angle))
|
224 |
|
225 |
+
(apex_centroids, eave_end_point_centroids,
|
226 |
+
apex_mask, eave_end_point_mask,
|
227 |
+
apex_radii, eave_radii) = get_vertices(gest_seg_np)
|
228 |
|
229 |
vertices = np.concatenate([apex_centroids, eave_end_point_centroids])
|
230 |
# inferred_vertices, inferred_mask = infer_vertices(gest_seg_np)
|
|
|
234 |
if len(vertices) < 2:
|
235 |
return [], []
|
236 |
|
|
|
|
|
|
|
|
|
|
|
|
|
237 |
|
238 |
edges = []
|
239 |
line_directions = []
|
|
|
273 |
missed_vertices = get_missed_vertices(vertices, inferred_vertices, **kwargs)
|
274 |
vertices = np.concatenate([vertices, missed_vertices])
|
275 |
|
276 |
+
vertex_size = np.full(len(vertices), point_radius/2)
|
277 |
+
apex_radii *= point_radius_scale
|
278 |
+
eave_radii *= point_radius_scale
|
279 |
+
apex_radii = np.clip(apex_radii, 10, point_radius)
|
280 |
+
eave_radii = np.clip(eave_radii, 10, point_radius)
|
281 |
+
vertex_size[:len(apex_radii)] = apex_radii
|
282 |
+
vertex_size[len(apex_radii):len(apex_radii) + len(eave_radii)] = eave_radii
|
283 |
+
|
284 |
+
# for i, coords in enumerate(vertices):
|
285 |
+
# coords = np.round(coords).astype(np.uint32)
|
286 |
+
# radius = point_radius # np.clip(int(max_depth//2 + depth_np[coords[1], coords[0]]), 10, 30)#int(np.clip(max_depth - depth_np[coords[1], coords[0]], 10, 20))
|
287 |
+
# vertex_size[i] = scale * radius
|
288 |
+
|
289 |
vertices = KDTree(vertices)
|
290 |
|
291 |
for edge_class in ['eave',
|
|
|
326 |
end_vertex_list = []
|
327 |
line_idx_list = []
|
328 |
for line_idx in line_indices:
|
329 |
+
begin_vertices, end_vertices = begin_indices[line_idx], end_indices[line_idx]
|
330 |
+
begin_vertices, end_vertices = np.array(begin_vertices), np.array(end_vertices)
|
331 |
+
begin_value = begin_edges.data[line_idx]
|
332 |
+
end_value = end_edges.data[line_idx]
|
333 |
+
begin_in_range_indices = np.where(
|
334 |
+
np.linalg.norm(vertices.data[begin_vertices] - begin_value, axis=1)
|
335 |
+
<
|
336 |
+
vertex_size[begin_vertices])[0]
|
337 |
+
end_in_range_indices = np.where(
|
338 |
+
np.linalg.norm(vertices.data[end_vertices] - end_value, axis=1)
|
339 |
+
<
|
340 |
+
vertex_size[end_vertices])[0]
|
341 |
+
begin_vertices = begin_vertices[begin_in_range_indices]
|
342 |
+
end_vertices = end_vertices[end_in_range_indices]
|
343 |
+
if len(begin_vertices) < 1 or len(end_vertices) < 1:
|
344 |
+
continue
|
345 |
+
|
346 |
|
347 |
+
begin_vertices, end_vertices = np.meshgrid(begin_vertices, end_vertices)
|
348 |
+
begin_vertex_list.extend(begin_vertices.flatten())
|
349 |
+
end_vertex_list.extend(end_vertices.flatten())
|
350 |
+
|
351 |
+
line_idx_list.extend([line_idx] * len(begin_vertices.flatten()))
|
352 |
|
353 |
line_idx_list = np.array(line_idx_list)
|
354 |
all_connections = np.array([begin_vertex_list, end_vertex_list])
|
|
|
386 |
if np.any(line_deviations > deviation_threshold):
|
387 |
connections.append(possible_connections[:, fitted_line_idx])
|
388 |
|
389 |
+
vertices = [{"xy": v, "type": VertexType.APEX} for v in apex_centroids]
|
390 |
+
vertices += [{"xy": v, "type": VertexType.APEX} for v in missed_vertices]
|
391 |
+
vertices += [{"xy": v, "type": VertexType.EAVE_END_POINT} for v in eave_end_point_centroids]
|
392 |
return vertices, connections
|
393 |
|
394 |
|
|
|
428 |
new_vertex_mapping = dict(zip(left_vertex_indices, new_indices))
|
429 |
|
430 |
vertices = [v for i, v in enumerate(vertices) if i in new_vertex_mapping]
|
431 |
+
types += [int(v['type'] == VertexType.APEX) for v in vertices]
|
432 |
vertices_3d = vertices_3d[left_vertex_indices]
|
433 |
connections = [[new_vertex_mapping[a] + cur_start, new_vertex_mapping[b] + cur_start] for a, b in connections]
|
434 |
|
|
|
506 |
return np.array(new_verts), connected_out
|
507 |
|
508 |
|
509 |
+
def predict(entry, visualize=False,
|
510 |
+
scale_estimation_coefficient=2.5,
|
511 |
+
clustering_eps=100,
|
512 |
+
dist_coeff=0,
|
513 |
+
pointcloud_depth_coeff = 1,
|
514 |
+
interpolation_radius=200,
|
515 |
+
**kwargs) -> Tuple[np.ndarray, List[int]]:
|
516 |
if 'gestalt' not in entry or 'depthcm' not in entry or 'K' not in entry or 'R' not in entry or 't' not in entry:
|
517 |
print('Missing required fields in the entry')
|
518 |
return (entry['__key__'], *empty_solution())
|
|
|
543 |
clustered_keys = np.split(point_keys, cluster_indices[1:])
|
544 |
|
545 |
biggest_cluster_index = np.argmax([len(i) for i in clustered_points])
|
546 |
+
biggest_cluster = clustered_points[biggest_cluster_index]
|
547 |
biggest_cluster_keys = clustered_keys[biggest_cluster_index]
|
548 |
biggest_cluster_keys = set(biggest_cluster_keys)
|
549 |
|
550 |
+
points3d_kdtree = KDTree(biggest_cluster)
|
551 |
+
|
552 |
+
|
553 |
for i, (gest, depthcm, K, R, t, imagekey) in enumerate(zip(entry['gestalt'],
|
554 |
entry['depthcm'],
|
555 |
entry['K'],
|
|
|
558 |
entry['__imagekey__']
|
559 |
)):
|
560 |
|
561 |
+
gest_seg = gest.resize(depthcm.size)
|
562 |
+
gest_seg_np = np.array(gest_seg).astype(np.uint8)
|
563 |
+
vertices, connections = get_vertices_and_edges_from_segmentation(gest_seg_np, **kwargs)
|
564 |
+
|
565 |
+
if (len(vertices) < 2) or (len(connections) < 1):
|
566 |
+
print(f'Not enough vertices or connections in image {i}')
|
567 |
+
vert_edge_per_image[i] = np.empty((0, 2)), [], np.empty((0, 3))
|
568 |
+
continue
|
569 |
+
depth_np = np.array(depthcm) / scale_estimation_coefficient
|
570 |
+
uv, depth_vert_from_depth_map = get_uv_depth(vertices, depth_np)
|
571 |
try:
|
572 |
+
belonging_points3d = []
|
573 |
+
belonging_points2d = []
|
574 |
+
point_indices = np.where(image_dict[imagekey].point3D_ids != -1)[0]
|
575 |
+
for idx, point_id in zip(point_indices, image_dict[imagekey].point3D_ids[point_indices]):
|
|
|
|
|
|
|
|
|
|
|
576 |
if point_id in biggest_cluster_keys:
|
577 |
+
belonging_points3d.append(entry["points3d"][point_id].xyz)
|
578 |
+
belonging_points2d.append(image_dict[imagekey].xys[idx])
|
579 |
|
580 |
+
if len(belonging_points3d) < 1:
|
581 |
print(f'No 3D points in image {i}')
|
582 |
vert_edge_per_image[i] = np.empty((0, 2)), [], np.empty((0, 3))
|
583 |
raise KeyError
|
584 |
+
belonging_points3d = np.array(belonging_points3d)
|
585 |
+
belonging_points2d = np.array(belonging_points2d)
|
586 |
+
# projected2d, _ = cv2.projectPoints(belonging_points3d, R, t, K, dist_coeff)
|
587 |
+
important = np.where(np.all(belonging_points2d >= 0, axis=1))
|
588 |
# Normalize the uv to the camera intrinsics
|
589 |
world_to_cam = np.eye(4)
|
590 |
world_to_cam[:3, :3] = R
|
591 |
world_to_cam[:3, 3] = t
|
592 |
|
593 |
+
homo_belonging_points = cv2.convertPointsToHomogeneous(belonging_points3d)
|
594 |
depth = cv2.convertPointsFromHomogeneous(cv2.transform(homo_belonging_points, world_to_cam))
|
595 |
depth = depth[:, 0, 2]
|
596 |
+
# projected2d = projected2d[:, 0, :]
|
597 |
+
depth = depth[important[0]]
|
598 |
+
# projected2d = projected2d[important[0]]
|
599 |
+
projected2d = belonging_points2d[important[0]]
|
600 |
+
# print(projected2d.shape)
|
601 |
+
# print(depth.shape)
|
602 |
+
depth *= pointcloud_depth_coeff
|
603 |
if len(depth) < 1:
|
604 |
print(f'No 3D points in image {i}')
|
605 |
vert_edge_per_image[i] = np.empty((0, 2)), [], np.empty((0, 3))
|
606 |
raise KeyError
|
607 |
# print(projected2d.shape, depth.shape)
|
608 |
|
609 |
+
# interpolator = si.NearestNDInterpolator(projected2d, depth, rescale=True)
|
610 |
+
interpolator = NearestNDInterpolatorWithThreshold(projected2d, depth, interpolation_radius)
|
611 |
+
# interpolator = si.LinearNDInterpolator(projected2d, depth, np.nan)
|
612 |
|
613 |
+
uv = np.array([v['xy'] for v in vertices])
|
614 |
+
xi, yi = uv[:, 0], uv[:, 1]
|
615 |
+
depth_vert_from_pointcloud = interpolator(xi, yi)
|
616 |
+
depthmap_used = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
617 |
|
618 |
+
# Get the 3D vertices
|
619 |
except KeyError:
|
620 |
+
#Revert to the depthmap
|
|
|
621 |
# Metric3D
|
622 |
+
depthmap_used = True
|
623 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
624 |
# Normalize the uv to the camera intrinsics
|
625 |
+
|
626 |
+
xy_local = np.ones((len(uv), 3))
|
627 |
+
xy_local[:, 0] = (uv[:, 0] - K[0, 2]) / K[0, 0]
|
628 |
+
xy_local[:, 1] = (uv[:, 1] - K[1, 2]) / K[1, 1]
|
629 |
+
# Get the 3D vertices
|
630 |
+
|
631 |
+
depth_vert_nan_idxs = None
|
632 |
+
if depthmap_used:
|
633 |
+
# norm_factor = np.max(np.linalg.norm(xy_local, axis=1)[..., None])
|
634 |
+
depth_vert = depth_vert_from_depth_map
|
635 |
+
else:
|
636 |
+
# 1. query detected vertices in projected2d
|
637 |
+
# if the vertex is beyond some radius, use the depthmap
|
638 |
+
# isnt uv
|
639 |
+
depth_vert_nan_idxs = np.where(np.isnan(depth_vert_from_pointcloud))[0]
|
640 |
+
depth_vert_from_pointcloud[depth_vert_nan_idxs] = depth_vert_from_depth_map[depth_vert_nan_idxs]
|
641 |
+
depth_vert = depth_vert_from_pointcloud
|
642 |
+
|
643 |
+
norm_factor = np.linalg.norm(xy_local, axis=1)[..., None]
|
644 |
+
if depth_vert_nan_idxs is not None and len(depth_vert_nan_idxs) > 0:
|
645 |
+
norm_factor_min = np.min(norm_factor[depth_vert_nan_idxs])
|
646 |
+
if len(depth_vert_nan_idxs) != len(norm_factor):
|
647 |
+
norm_factor_max = np.max(norm_factor[~np.isin(np.arange(len(norm_factor)), depth_vert_nan_idxs)])
|
648 |
+
else:
|
649 |
+
norm_factor_max = np.max(norm_factor)
|
650 |
+
else:
|
651 |
+
norm_factor_min = np.min(norm_factor)
|
652 |
+
norm_factor_max = np.max(norm_factor)
|
653 |
+
|
654 |
+
vertices_3d_local = depth_vert[..., None] * xy_local
|
655 |
+
if depthmap_used:
|
656 |
+
vertices_3d_local /= norm_factor_max
|
657 |
+
else:
|
658 |
+
vertices_3d_local[depth_vert_nan_idxs] /= norm_factor_max
|
659 |
+
vertices_3d_local[~np.isin(np.arange(len(vertices_3d_local)), depth_vert_nan_idxs)] /= norm_factor_min
|
660 |
+
|
661 |
+
# vertices_3d_local = depth_vert[..., None] * (xy_local / norm_factor)
|
662 |
+
world_to_cam = np.eye(4)
|
663 |
+
world_to_cam[:3, :3] = R
|
664 |
+
world_to_cam[:3, 3] = t
|
665 |
+
|
666 |
+
cam_to_world = np.linalg.inv(world_to_cam)
|
667 |
+
vertices_3d = cv2.transform(cv2.convertPointsToHomogeneous(vertices_3d_local), cam_to_world)
|
668 |
+
vertices_3d = cv2.convertPointsFromHomogeneous(vertices_3d).reshape(-1, 3)
|
669 |
+
|
670 |
+
# not_nan_items = np.all(~np.isnan(vertices_3d), axis=1)
|
671 |
+
# _, closest_fitted = points3d_kdtree.query(vertices_3d[not_nan_items])
|
672 |
+
|
673 |
+
# vertices_3d[not_nan_items] = points3d_kdtree.data[closest_fitted]
|
674 |
|
675 |
vert_edge_per_image[i] = vertices, connections, vertices_3d
|
676 |
all_3d_vertices, connections_3d = merge_vertices_3d(vert_edge_per_image, **kwargs)
|
677 |
all_3d_vertices_clean, connections_3d_clean = all_3d_vertices, connections_3d
|
678 |
+
# highest_edges = np.argpartition(all_3d_vertices_clean[:, 1], 4)[:4].tolist()
|
679 |
+
#
|
680 |
+
# connections_3d_clean.append(highest_edges[:2])
|
681 |
+
# connections_3d_clean.append(highest_edges[2:])
|
682 |
# all_3d_vertices_clean, connections_3d_clean = prune_not_connected(all_3d_vertices, connections_3d)
|
683 |
if (len(all_3d_vertices_clean) < 2) or len(connections_3d_clean) < 1:
|
684 |
print(f'Not enough vertices or connections in the 3D vertices')
|
script.py
CHANGED
@@ -116,7 +116,18 @@ def save_submission(submission, path):
|
|
116 |
sub = pd.DataFrame(submission, columns=["__key__", "wf_vertices", "wf_edges"])
|
117 |
sub.to_parquet(path)
|
118 |
print(f"Submission saved to {path}")
|
119 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
120 |
if __name__ == "__main__":
|
121 |
from handcrafted_solution import predict
|
122 |
print ("------------ Loading dataset------------ ")
|
@@ -127,28 +138,30 @@ if __name__ == "__main__":
|
|
127 |
solution = []
|
128 |
from concurrent.futures import ProcessPoolExecutor
|
129 |
with ProcessPoolExecutor(max_workers=8) as pool:
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
|
|
|
|
152 |
if i % 100 == 0:
|
153 |
# incrementally save the results in case we run out of time
|
154 |
print(f"Processed {i} samples")
|
|
|
116 |
sub = pd.DataFrame(submission, columns=["__key__", "wf_vertices", "wf_edges"])
|
117 |
sub.to_parquet(path)
|
118 |
print(f"Submission saved to {path}")
|
119 |
+
batch_size = 48 # You can adjust this according to your needs
|
120 |
+
|
121 |
+
# Define a generator function to yield batches of samples
|
122 |
+
def batch_generator(dataset, batch_size):
|
123 |
+
batch = []
|
124 |
+
for i, sample in enumerate(dataset):
|
125 |
+
batch.append(sample)
|
126 |
+
if len(batch) == batch_size:
|
127 |
+
yield batch
|
128 |
+
batch = []
|
129 |
+
if batch: # Yield the remaining samples
|
130 |
+
yield batch
|
131 |
if __name__ == "__main__":
|
132 |
from handcrafted_solution import predict
|
133 |
print ("------------ Loading dataset------------ ")
|
|
|
138 |
solution = []
|
139 |
from concurrent.futures import ProcessPoolExecutor
|
140 |
with ProcessPoolExecutor(max_workers=8) as pool:
|
141 |
+
for batch in tqdm(batch_generator(dataset, batch_size), desc='Batches'):
|
142 |
+
results = []
|
143 |
+
for i, sample in enumerate(batch):
|
144 |
+
results.append(pool.submit(predict, sample,
|
145 |
+
point_radius=40,
|
146 |
+
max_angle=15,
|
147 |
+
extend=25,
|
148 |
+
merge_th=80.0,
|
149 |
+
min_missing_distance=350.0,
|
150 |
+
scale_estimation_coefficient=2.54,
|
151 |
+
clustering_eps=120,
|
152 |
+
interpolation_radius=200,
|
153 |
+
point_radius_scale=0.5,
|
154 |
+
# dist_coeff=0,
|
155 |
+
# pointcloud_depth_coeff=1,
|
156 |
+
))
|
157 |
+
|
158 |
+
for result in tqdm(results, desc='Results', total=len(results), position=0):
|
159 |
+
key, pred_vertices, pred_edges = result.result()
|
160 |
+
solution.append({
|
161 |
+
'__key__': key,
|
162 |
+
'wf_vertices': pred_vertices.tolist(),
|
163 |
+
'wf_edges': pred_edges
|
164 |
+
})
|
165 |
if i % 100 == 0:
|
166 |
# incrementally save the results in case we run out of time
|
167 |
print(f"Processed {i} samples")
|