added notebooks
Browse files- .gitattributes +1 -0
- examine_results.ipynb +3 -0
- explore.ipynb +3 -0
- handcrafted_solution.py +94 -23
- pointclouds.ipynb +3 -0
- seek_top_level_roofs.ipynb +3 -0
- test_solution.ipynb +0 -0
.gitattributes
CHANGED
@@ -34,3 +34,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
*.whl filter=lfs diff=lfs merge=lfs -text
|
|
|
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
*.whl filter=lfs diff=lfs merge=lfs -text
|
37 |
+
*.ipynb filter=lfs diff=lfs merge=lfs -text
|
examine_results.ipynb
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:a301378de22ca2d5d2798aa02f8380ef75fb75a039d8bcfd7ec64a8f5efe6c0b
|
3 |
+
size 3250980
|
explore.ipynb
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:b8e5020524a5e7fea1d1d97a4a0ed5d2da4fff7ddfd6870a418510018285da0e
|
3 |
+
size 86613
|
handcrafted_solution.py
CHANGED
@@ -146,7 +146,12 @@ def get_vertices(image_gestalt, *, color_range=3.5, dialations=2, erosions=1, ke
|
|
146 |
*_, apex_stats, apex_centroids = cv2.connectedComponentsWithStats(apex_mask, connectivity=4, stats=cv2.CV_32S)
|
147 |
*_, other_stats, other_centroids = cv2.connectedComponentsWithStats(eave_end_point_mask, connectivity=4, stats=cv2.CV_32S)
|
148 |
|
149 |
-
return apex_centroids[1:],
|
|
|
|
|
|
|
|
|
|
|
150 |
|
151 |
|
152 |
def infer_vertices(image_gestalt, *, color_range=4.):
|
@@ -180,14 +185,14 @@ def get_missed_vertices(vertices, inferred_centroids, *, min_missing_distance=20
|
|
180 |
|
181 |
|
182 |
def get_lines_and_directions(gest_seg_np, edge_class, *, color_range=4., rho, theta, threshold, min_line_length,
|
183 |
-
max_line_gap, extend, **kwargs):
|
184 |
edge_color = np.array(gestalt_color_mapping[edge_class])
|
185 |
|
186 |
mask = cv2.inRange(gest_seg_np,
|
187 |
edge_color - color_range,
|
188 |
edge_color + color_range)
|
189 |
mask = cv2.morphologyEx(mask,
|
190 |
-
cv2.MORPH_DILATE, np.ones((
|
191 |
|
192 |
if not np.any(mask):
|
193 |
return [], []
|
@@ -203,20 +208,22 @@ def get_lines_and_directions(gest_seg_np, edge_class, *, color_range=4., rho, th
|
|
203 |
|
204 |
line_directions = []
|
205 |
edges = []
|
|
|
206 |
for line_idx, line in enumerate(lines):
|
207 |
for x1, y1, x2, y2 in line:
|
208 |
if x1 < x2:
|
209 |
x1, y1, x2, y2 = x2, y2, x1, y1
|
210 |
direction = (np.array([x2 - x1, y2 - y1]))
|
211 |
direction = direction / np.linalg.norm(direction)
|
212 |
-
line_directions.append(direction)
|
213 |
|
214 |
-
|
|
|
215 |
|
216 |
-
|
217 |
-
|
218 |
|
219 |
-
|
|
|
220 |
return edges, line_directions
|
221 |
|
222 |
|
@@ -259,7 +266,7 @@ def get_vertices_and_edges_from_segmentation(gest_seg_np, *,
|
|
259 |
|
260 |
rho = 1 # distance resolution in pixels of the Hough grid
|
261 |
theta = np.pi / 180 # angular resolution in radians of the Hough grid
|
262 |
-
threshold =
|
263 |
min_line_length = 60 # minimum number of pixels making up a line
|
264 |
max_line_gap = 40 # maximum gap in pixels between connectable line segments
|
265 |
ridge_edges, ridge_directions = get_lines_and_directions(gest_seg_np, "ridge",
|
@@ -268,6 +275,8 @@ def get_vertices_and_edges_from_segmentation(gest_seg_np, *,
|
|
268 |
threshold=threshold,
|
269 |
min_line_length=min_line_length,
|
270 |
max_line_gap=max_line_gap,
|
|
|
|
|
271 |
**kwargs)
|
272 |
|
273 |
rake_edges, rake_directions = get_lines_and_directions(gest_seg_np, "rake",
|
@@ -295,12 +304,14 @@ def get_vertices_and_edges_from_segmentation(gest_seg_np, *,
|
|
295 |
return [], []
|
296 |
|
297 |
vertex_size = np.full(len(vertices), point_radius/2)
|
298 |
-
apex_radii
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
|
|
|
|
304 |
|
305 |
# for i, coords in enumerate(vertices):
|
306 |
# coords = np.round(coords).astype(np.uint32)
|
@@ -553,7 +564,7 @@ def clean_points3d(entry, clustering_eps):
|
|
553 |
|
554 |
return points3d_kdtree, biggest_cluster_keys, image_dict
|
555 |
|
556 |
-
def
|
557 |
belonging_points3d = []
|
558 |
belonging_points2d = []
|
559 |
point_indices = np.where(image.point3D_ids != -1)[0]
|
@@ -582,6 +593,57 @@ def get_depthmap_from_pointcloud(image, pointcloud, biggest_cluster_keys, R, t):
|
|
582 |
# projected2d = projected2d[important[0]]
|
583 |
projected2d = belonging_points2d[important[0]]
|
584 |
return projected2d, depth
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
585 |
def predict(entry, visualize=False,
|
586 |
scale_estimation_coefficient=2.5,
|
587 |
clustering_eps=100,
|
@@ -597,6 +659,8 @@ def predict(entry, visualize=False,
|
|
597 |
vert_edge_per_image = {}
|
598 |
|
599 |
points3d_kdtree, biggest_cluster_keys, image_dict = clean_points3d(entry, clustering_eps)
|
|
|
|
|
600 |
|
601 |
|
602 |
for i, (gest, depthcm, K, R, t, imagekey) in enumerate(zip(entry['gestalt'],
|
@@ -615,14 +679,18 @@ def predict(entry, visualize=False,
|
|
615 |
print(f'Not enough vertices or connections in image {i}')
|
616 |
vert_edge_per_image[i] = np.empty((0, 2)), [], np.empty((0, 3))
|
617 |
continue
|
|
|
618 |
depth_np = np.array(depthcm) / scale_estimation_coefficient
|
|
|
619 |
# kernel = np.array([[-1,-1,-1], [-1,9,-1], [-1,-1,-1]])
|
620 |
# depth_np = cv2.filter2D(depth_np, -1, kernel)
|
621 |
uv, depth_vert_from_depth_map = get_uv_depth(vertices, depth_np)
|
|
|
622 |
try:
|
|
|
623 |
image = image_dict[imagekey]
|
624 |
|
625 |
-
projected2d, depth =
|
626 |
if len(depth) < 1:
|
627 |
print(f'No 3D points in image {i}')
|
628 |
# vert_edge_per_image[i] = np.empty((0, 2)), [], np.empty((0, 3))
|
@@ -639,6 +707,9 @@ def predict(entry, visualize=False,
|
|
639 |
|
640 |
except KeyError:
|
641 |
#Revert to the depthmap
|
|
|
|
|
|
|
642 |
depthmap_used = True
|
643 |
|
644 |
# Normalize the uv to the camera intrinsics
|
@@ -672,7 +743,7 @@ def predict(entry, visualize=False,
|
|
672 |
vertices_3d_local /= norm_factor_max
|
673 |
else:
|
674 |
vertices_3d_local[depth_vert_nan_idxs] /= norm_factor_max
|
675 |
-
vertices_3d_local[~np.isin(np.arange(len(vertices_3d_local)), depth_vert_nan_idxs)] /=
|
676 |
|
677 |
world_to_cam = np.eye(4)
|
678 |
world_to_cam[:3, :3] = R
|
@@ -682,11 +753,11 @@ def predict(entry, visualize=False,
|
|
682 |
vertices_3d = cv2.transform(cv2.convertPointsToHomogeneous(vertices_3d_local), cam_to_world)
|
683 |
vertices_3d = cv2.convertPointsFromHomogeneous(vertices_3d).reshape(-1, 3)
|
684 |
|
685 |
-
if not depthmap_used:
|
686 |
-
|
687 |
-
|
688 |
-
|
689 |
-
|
690 |
|
691 |
vert_edge_per_image[i] = vertices, connections, vertices_3d
|
692 |
all_3d_vertices, connections_3d = merge_vertices_3d(vert_edge_per_image, **kwargs)
|
|
|
146 |
*_, apex_stats, apex_centroids = cv2.connectedComponentsWithStats(apex_mask, connectivity=4, stats=cv2.CV_32S)
|
147 |
*_, other_stats, other_centroids = cv2.connectedComponentsWithStats(eave_end_point_mask, connectivity=4, stats=cv2.CV_32S)
|
148 |
|
149 |
+
return (apex_centroids[1:],
|
150 |
+
other_centroids[1:],
|
151 |
+
apex_mask,
|
152 |
+
eave_end_point_mask,
|
153 |
+
np.maximum(apex_stats[1:, cv2.CC_STAT_WIDTH], apex_stats[1:, cv2.CC_STAT_HEIGHT])/2,
|
154 |
+
np.maximum(other_stats[1:, cv2.CC_STAT_WIDTH], other_stats[1:, cv2.CC_STAT_HEIGHT])/2)
|
155 |
|
156 |
|
157 |
def infer_vertices(image_gestalt, *, color_range=4.):
|
|
|
185 |
|
186 |
|
187 |
def get_lines_and_directions(gest_seg_np, edge_class, *, color_range=4., rho, theta, threshold, min_line_length,
|
188 |
+
max_line_gap, extend=30, kernel_size=3, dilation_iterations=1, **kwargs):
|
189 |
edge_color = np.array(gestalt_color_mapping[edge_class])
|
190 |
|
191 |
mask = cv2.inRange(gest_seg_np,
|
192 |
edge_color - color_range,
|
193 |
edge_color + color_range)
|
194 |
mask = cv2.morphologyEx(mask,
|
195 |
+
cv2.MORPH_DILATE, np.ones((kernel_size, kernel_size)), iterations=dilation_iterations)
|
196 |
|
197 |
if not np.any(mask):
|
198 |
return [], []
|
|
|
208 |
|
209 |
line_directions = []
|
210 |
edges = []
|
211 |
+
|
212 |
for line_idx, line in enumerate(lines):
|
213 |
for x1, y1, x2, y2 in line:
|
214 |
if x1 < x2:
|
215 |
x1, y1, x2, y2 = x2, y2, x1, y1
|
216 |
direction = (np.array([x2 - x1, y2 - y1]))
|
217 |
direction = direction / np.linalg.norm(direction)
|
|
|
218 |
|
219 |
+
for extend_value in range(0, int(extend), 5):
|
220 |
+
new_direction = extend_value * direction
|
221 |
|
222 |
+
x1, y1 = -new_direction + (x1, y1)
|
223 |
+
x2, y2 = + new_direction + (x2, y2)
|
224 |
|
225 |
+
line_directions.append(direction)
|
226 |
+
edges.append((x1, y1, x2, y2))
|
227 |
return edges, line_directions
|
228 |
|
229 |
|
|
|
266 |
|
267 |
rho = 1 # distance resolution in pixels of the Hough grid
|
268 |
theta = np.pi / 180 # angular resolution in radians of the Hough grid
|
269 |
+
threshold = 40 # minimum number of votes (intersections in Hough grid cell)
|
270 |
min_line_length = 60 # minimum number of pixels making up a line
|
271 |
max_line_gap = 40 # maximum gap in pixels between connectable line segments
|
272 |
ridge_edges, ridge_directions = get_lines_and_directions(gest_seg_np, "ridge",
|
|
|
275 |
threshold=threshold,
|
276 |
min_line_length=min_line_length,
|
277 |
max_line_gap=max_line_gap,
|
278 |
+
kernel_size=3,
|
279 |
+
dilation_iterations=3,
|
280 |
**kwargs)
|
281 |
|
282 |
rake_edges, rake_directions = get_lines_and_directions(gest_seg_np, "rake",
|
|
|
304 |
return [], []
|
305 |
|
306 |
vertex_size = np.full(len(vertices), point_radius/2)
|
307 |
+
if len(apex_radii) > 0 and len(eave_radii) > 0:
|
308 |
+
apex_radii *= point_radius_scale
|
309 |
+
eave_radii *= point_radius_scale
|
310 |
+
apex_radii = np.maximum(apex_radii, 10)
|
311 |
+
eave_radii = np.maximum(eave_radii, 10)
|
312 |
+
point_radius = np.max([np.max(apex_radii), np.max(eave_radii)])
|
313 |
+
vertex_size[:len(apex_radii)] = apex_radii
|
314 |
+
vertex_size[len(apex_radii):len(apex_radii) + len(eave_radii)] = eave_radii
|
315 |
|
316 |
# for i, coords in enumerate(vertices):
|
317 |
# coords = np.round(coords).astype(np.uint32)
|
|
|
564 |
|
565 |
return points3d_kdtree, biggest_cluster_keys, image_dict
|
566 |
|
567 |
+
def get_depth_from_pointcloud(image, pointcloud, biggest_cluster_keys, R, t):
|
568 |
belonging_points3d = []
|
569 |
belonging_points2d = []
|
570 |
point_indices = np.where(image.point3D_ids != -1)[0]
|
|
|
593 |
# projected2d = projected2d[important[0]]
|
594 |
projected2d = belonging_points2d[important[0]]
|
595 |
return projected2d, depth
|
596 |
+
|
597 |
+
def get_depthmap_from_pointcloud(pointcloud, biggest_cluster_keys, K, R, t, depthmap):
|
598 |
+
belonging_points3d = []
|
599 |
+
for point_id in biggest_cluster_keys:
|
600 |
+
belonging_points3d.append(pointcloud[point_id].xyz)
|
601 |
+
belonging_points3d = np.array(belonging_points3d)
|
602 |
+
|
603 |
+
projected2d, _ = cv2.projectPoints(belonging_points3d, R, t, K, 0)
|
604 |
+
projected2d = projected2d.reshape(-1, 2)
|
605 |
+
important = np.where(np.all(projected2d >= 0, axis=1))[0]
|
606 |
+
# Normalize the uv to the camera intrinsics
|
607 |
+
world_to_cam = np.eye(4)
|
608 |
+
world_to_cam[:3, :3] = R
|
609 |
+
world_to_cam[:3, 3] = t
|
610 |
+
|
611 |
+
homo_belonging_points = cv2.convertPointsToHomogeneous(belonging_points3d)
|
612 |
+
depth = cv2.convertPointsFromHomogeneous(cv2.transform(homo_belonging_points, world_to_cam))
|
613 |
+
depth = depth[:, 0, 2]
|
614 |
+
# projected2d = projected2d[:, 0, :]
|
615 |
+
depth = depth[important]
|
616 |
+
projected2d = projected2d[important]
|
617 |
+
|
618 |
+
projected2d = projected2d.astype(np.int32)
|
619 |
+
projected2d[:, 1] = np.clip(projected2d[:, 1], 0, depthmap.shape[1]-1)
|
620 |
+
projected2d[:, 0] = np.clip(projected2d[:, 0], 0, depthmap.shape[0]-1)
|
621 |
+
pointcloud_depthmap = np.full_like(depthmap, 4000)
|
622 |
+
|
623 |
+
sorted_indices = np.argsort(depth)[::-1]
|
624 |
+
projected2d, depth = projected2d[sorted_indices], depth[sorted_indices]
|
625 |
+
idx = np.searchsorted(depth, 1000)
|
626 |
+
projected2d, depth = projected2d[:idx], depth[:idx]
|
627 |
+
for point, depth_value in zip(projected2d, depth):
|
628 |
+
cv2.circle(pointcloud_depthmap, point, 20, depth_value, -1)
|
629 |
+
return pointcloud_depthmap
|
630 |
+
|
631 |
+
def get_mesh_depthmap(mesh, K, R, t, depthmap, fill_value=4000):
|
632 |
+
|
633 |
+
world_to_cam = np.eye(4)
|
634 |
+
world_to_cam[:3, :3] = R
|
635 |
+
world_to_cam[:3, 3] = t
|
636 |
+
camera_transform = np.linalg.inv(world_to_cam)
|
637 |
+
import trimesh
|
638 |
+
camera = trimesh.scene.Camera(focal=(K[0, 0], K[1, 1]), resolution=depthmap.shape[::-1])
|
639 |
+
scene = trimesh.scene.scene.Scene(camera=camera, camera_transform=camera_transform)
|
640 |
+
ray_origins, ray_directions, corresponding_pixels = scene.camera_rays()
|
641 |
+
locations, index_ray, index_tri = mesh.ray.intersects_location(ray_origins, -ray_directions, multiple_hits=False)
|
642 |
+
mesh_depths = np.linalg.norm(ray_origins[index_ray] - locations, axis=1)
|
643 |
+
mesh_depthmap = np.full(camera.resolution, fill_value)
|
644 |
+
mesh_depthmap[corresponding_pixels[index_ray, 0], corresponding_pixels[index_ray, 1]] = mesh_depths
|
645 |
+
mesh_depthmap = cv2.flip(cv2.rotate(mesh_depthmap, cv2.ROTATE_90_COUNTERCLOCKWISE), 1)
|
646 |
+
return mesh_depthmap
|
647 |
def predict(entry, visualize=False,
|
648 |
scale_estimation_coefficient=2.5,
|
649 |
clustering_eps=100,
|
|
|
659 |
vert_edge_per_image = {}
|
660 |
|
661 |
points3d_kdtree, biggest_cluster_keys, image_dict = clean_points3d(entry, clustering_eps)
|
662 |
+
# import trimesh
|
663 |
+
# mesh = trimesh.Trimesh(vertices=entry["mesh_vertices"], faces=entry["mesh_faces"][:,1:], use_embree=True)
|
664 |
|
665 |
|
666 |
for i, (gest, depthcm, K, R, t, imagekey) in enumerate(zip(entry['gestalt'],
|
|
|
679 |
print(f'Not enough vertices or connections in image {i}')
|
680 |
vert_edge_per_image[i] = np.empty((0, 2)), [], np.empty((0, 3))
|
681 |
continue
|
682 |
+
|
683 |
depth_np = np.array(depthcm) / scale_estimation_coefficient
|
684 |
+
# depth_np = get_mesh_depthmap(mesh, K, R, t, depth_np, 4000).astype(np.float32)
|
685 |
# kernel = np.array([[-1,-1,-1], [-1,9,-1], [-1,-1,-1]])
|
686 |
# depth_np = cv2.filter2D(depth_np, -1, kernel)
|
687 |
uv, depth_vert_from_depth_map = get_uv_depth(vertices, depth_np)
|
688 |
+
|
689 |
try:
|
690 |
+
# raise KeyError
|
691 |
image = image_dict[imagekey]
|
692 |
|
693 |
+
projected2d, depth = get_depth_from_pointcloud(image, entry["points3d"], biggest_cluster_keys, R, t)
|
694 |
if len(depth) < 1:
|
695 |
print(f'No 3D points in image {i}')
|
696 |
# vert_edge_per_image[i] = np.empty((0, 2)), [], np.empty((0, 3))
|
|
|
707 |
|
708 |
except KeyError:
|
709 |
#Revert to the depthmap
|
710 |
+
# if len(biggest_cluster_keys) > 0:
|
711 |
+
# depth_map = get_depthmap_from_pointcloud(entry["points3d"], biggest_cluster_keys, K, R, t, depth_np)
|
712 |
+
# uv, depth_vert_from_depth_map = get_uv_depth(vertices, depth_map)
|
713 |
depthmap_used = True
|
714 |
|
715 |
# Normalize the uv to the camera intrinsics
|
|
|
743 |
vertices_3d_local /= norm_factor_max
|
744 |
else:
|
745 |
vertices_3d_local[depth_vert_nan_idxs] /= norm_factor_max
|
746 |
+
vertices_3d_local[~np.isin(np.arange(len(vertices_3d_local)), depth_vert_nan_idxs)] /= norm_factor_max
|
747 |
|
748 |
world_to_cam = np.eye(4)
|
749 |
world_to_cam[:3, :3] = R
|
|
|
753 |
vertices_3d = cv2.transform(cv2.convertPointsToHomogeneous(vertices_3d_local), cam_to_world)
|
754 |
vertices_3d = cv2.convertPointsFromHomogeneous(vertices_3d).reshape(-1, 3)
|
755 |
|
756 |
+
# if not depthmap_used:
|
757 |
+
# not_nan_items = np.all(~np.isnan(vertices_3d), axis=1)
|
758 |
+
# _, closest_fitted = points3d_kdtree.query(vertices_3d[not_nan_items])
|
759 |
+
#
|
760 |
+
# vertices_3d[not_nan_items] = points3d_kdtree.data[closest_fitted]
|
761 |
|
762 |
vert_edge_per_image[i] = vertices, connections, vertices_3d
|
763 |
all_3d_vertices, connections_3d = merge_vertices_3d(vert_edge_per_image, **kwargs)
|
pointclouds.ipynb
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:965401b68d1da3152141a478322bc4ebfc4d4b4b7a16b3cffefcd7c0b5e0e915
|
3 |
+
size 151533905
|
seek_top_level_roofs.ipynb
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:9e0d1b2dd8a7fcb8683520c0aa8890ec6b9788e669627468efe8a45a7a3ec160
|
3 |
+
size 8948456
|
test_solution.ipynb
CHANGED
The diff for this file is too large to render.
See raw diff
|
|