Upload folder using huggingface_hub
Browse files- README.md +14 -0
- example_notebook.ipynb +0 -0
- example_on_training.ipynb +0 -0
- handcrafted_solution.py +513 -0
- params.json +23 -0
- script.py +132 -0
README.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Empty solution example for the S23DR competition
|
| 2 |
+
|
| 3 |
+
This repo provides a minimalistic example of a valid, but empty submission to S23DR competition.
|
| 4 |
+
We recommend you take a look at [this example](https://huggingface.co/usm3d/handcrafted_baseline_submission),
|
| 5 |
+
which implements some primitive algorithms and provides useful I/O and visualization functions.
|
| 6 |
+
|
| 7 |
+
This example seeks to simply provide minimal code which succeeds at reading the dataset and producing a solution (in this case two vertices at the origin and edge of zero length connecting them).
|
| 8 |
+
|
| 9 |
+
`script.py` - is the main file which is run by the competition space. It should produce `submission.parquet` as the result of the run. Please see the additional comments in the `script.py` file.
|
| 10 |
+
|
| 11 |
+
---
|
| 12 |
+
license: apache-2.0
|
| 13 |
+
---
|
| 14 |
+
|
example_notebook.ipynb
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
example_on_training.ipynb
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
handcrafted_solution.py
ADDED
|
@@ -0,0 +1,513 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
from scipy.optimize import minimize
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def empty_solution():
|
| 15 |
+
'''Return a minimal valid solution, i.e. 2 vertices and 1 edge.'''
|
| 16 |
+
return np.zeros((2,3)), [(0, 1)]
|
| 17 |
+
|
| 18 |
+
def read_colmap_rec(colmap_data):
|
| 19 |
+
import pycolmap
|
| 20 |
+
import tempfile,zipfile
|
| 21 |
+
import io
|
| 22 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 23 |
+
with zipfile.ZipFile(io.BytesIO(colmap_data), "r") as zf:
|
| 24 |
+
zf.extractall(tmpdir) # unpacks cameras.txt, images.txt, etc. to tmpdir
|
| 25 |
+
# Now parse with pycolmap
|
| 26 |
+
rec = pycolmap.Reconstruction(tmpdir)
|
| 27 |
+
return rec
|
| 28 |
+
|
| 29 |
+
def convert_entry_to_human_readable(entry):
|
| 30 |
+
out = {}
|
| 31 |
+
for k, v in entry.items():
|
| 32 |
+
if 'colmap' in k:
|
| 33 |
+
out[k] = read_colmap_rec(v)
|
| 34 |
+
elif k in ['wf_vertices', 'wf_edges', 'K', 'R', 't']:
|
| 35 |
+
out[k] = np.array(v)
|
| 36 |
+
else:
|
| 37 |
+
out[k]=v
|
| 38 |
+
out['__key__'] = entry['order_id']
|
| 39 |
+
return out
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def point_to_segment_dist(pt, seg_p1, seg_p2):
|
| 43 |
+
"""
|
| 44 |
+
Computes the Euclidean distance from pt to the line segment p1->p2.
|
| 45 |
+
pt, seg_p1, seg_p2: (x, y) as np.ndarray
|
| 46 |
+
"""
|
| 47 |
+
# If both endpoints are the same, just return distance to one of them
|
| 48 |
+
if np.allclose(seg_p1, seg_p2):
|
| 49 |
+
return np.linalg.norm(pt - seg_p1)
|
| 50 |
+
seg_vec = seg_p2 - seg_p1
|
| 51 |
+
pt_vec = pt - seg_p1
|
| 52 |
+
seg_len2 = seg_vec.dot(seg_vec)
|
| 53 |
+
t = max(0, min(1, pt_vec.dot(seg_vec)/seg_len2))
|
| 54 |
+
proj = seg_p1 + t*seg_vec
|
| 55 |
+
return np.linalg.norm(pt - proj)
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def get_vertices_and_edges_from_segmentation(gest_seg_np, edge_th=25.0):
|
| 59 |
+
"""
|
| 60 |
+
Identify apex and eave-end vertices, then detect lines for eave/ridge/rake/valley.
|
| 61 |
+
For each connected component, we do a line fit with cv2.fitLine, then measure
|
| 62 |
+
segment endpoints more robustly. We then associate apex points that are within
|
| 63 |
+
'edge_th' of the line segment. We record those apex–apex connections for edges
|
| 64 |
+
if at least 2 apexes lie near the same component line.
|
| 65 |
+
"""
|
| 66 |
+
from hoho.color_mappings import gestalt_color_mapping # for apex, eave_end_point, etc.
|
| 67 |
+
|
| 68 |
+
#--------------------------------------------------------------------------------
|
| 69 |
+
# Step A: Collect apex and eave_end vertices
|
| 70 |
+
#--------------------------------------------------------------------------------
|
| 71 |
+
vertices = []
|
| 72 |
+
# Apex
|
| 73 |
+
apex_color = np.array(gestalt_color_mapping['apex'])
|
| 74 |
+
apex_mask = cv2.inRange(gest_seg_np, apex_color-0.5, apex_color+0.5)
|
| 75 |
+
if apex_mask.sum() > 0:
|
| 76 |
+
output = cv2.connectedComponentsWithStats(apex_mask, 8, cv2.CV_32S)
|
| 77 |
+
(numLabels, labels, stats, centroids) = output
|
| 78 |
+
stats, centroids = stats[1:], centroids[1:] # skip background
|
| 79 |
+
for i in range(numLabels-1):
|
| 80 |
+
vert = {"xy": centroids[i], "type": "apex"}
|
| 81 |
+
vertices.append(vert)
|
| 82 |
+
|
| 83 |
+
# Eave end
|
| 84 |
+
eave_end_color = np.array(gestalt_color_mapping['eave_end_point'])
|
| 85 |
+
eave_end_mask = cv2.inRange(gest_seg_np, eave_end_color-0.5, eave_end_color+0.5)
|
| 86 |
+
if eave_end_mask.sum() > 0:
|
| 87 |
+
output = cv2.connectedComponentsWithStats(eave_end_mask, 8, cv2.CV_32S)
|
| 88 |
+
(numLabels, labels, stats, centroids) = output
|
| 89 |
+
stats, centroids = stats[1:], centroids[1:]
|
| 90 |
+
for i in range(numLabels-1):
|
| 91 |
+
vert = {"xy": centroids[i], "type": "eave_end_point"}
|
| 92 |
+
vertices.append(vert)
|
| 93 |
+
|
| 94 |
+
# Consolidate apex points as array:
|
| 95 |
+
apex_pts = []
|
| 96 |
+
apex_idx_map = [] # keep track of index in 'vertices'
|
| 97 |
+
for idx, v in enumerate(vertices):
|
| 98 |
+
apex_pts.append(v['xy'])
|
| 99 |
+
apex_idx_map.append(idx)
|
| 100 |
+
apex_pts = np.array(apex_pts)
|
| 101 |
+
|
| 102 |
+
connections = []
|
| 103 |
+
edge_classes = ['eave', 'ridge', 'rake', 'valley']
|
| 104 |
+
for edge_class in edge_classes:
|
| 105 |
+
edge_color = np.array(gestalt_color_mapping[edge_class])
|
| 106 |
+
mask_raw = cv2.inRange(gest_seg_np, edge_color-0.5, edge_color+0.5)
|
| 107 |
+
|
| 108 |
+
# Possibly do morphological open/close to avoid merges or small holes
|
| 109 |
+
kernel = np.ones((5, 5), np.uint8) # smaller kernel to reduce over-merge
|
| 110 |
+
mask = cv2.morphologyEx(mask_raw, cv2.MORPH_CLOSE, kernel)
|
| 111 |
+
|
| 112 |
+
if mask.sum() == 0:
|
| 113 |
+
continue
|
| 114 |
+
|
| 115 |
+
# Connected components
|
| 116 |
+
output = cv2.connectedComponentsWithStats(mask, 8, cv2.CV_32S)
|
| 117 |
+
(numLabels, labels, stats, centroids) = output
|
| 118 |
+
# skip the background
|
| 119 |
+
stats, centroids = stats[1:], centroids[1:]
|
| 120 |
+
label_indices = range(1, numLabels)
|
| 121 |
+
|
| 122 |
+
# For each connected component, do a line fit
|
| 123 |
+
for lbl in label_indices:
|
| 124 |
+
ys, xs = np.where(labels == lbl)
|
| 125 |
+
if len(xs) < 2:
|
| 126 |
+
continue
|
| 127 |
+
# Fit a line using cv2.fitLine
|
| 128 |
+
pts_for_fit = np.column_stack([xs, ys]).astype(np.float32)
|
| 129 |
+
# (vx, vy, x0, y0) = direction + a point on the line
|
| 130 |
+
line_params = cv2.fitLine(pts_for_fit, distType=cv2.DIST_L2,
|
| 131 |
+
param=0, reps=0.01, aeps=0.01)
|
| 132 |
+
vx, vy, x0, y0 = line_params.ravel()
|
| 133 |
+
# We'll approximate endpoints by projecting (xs, ys) onto the line,
|
| 134 |
+
# then taking min and max in the 1D param along the line.
|
| 135 |
+
|
| 136 |
+
# param along the line = ( (x - x0)*vx + (y - y0)*vy )
|
| 137 |
+
proj = ( (xs - x0)*vx + (ys - y0)*vy )
|
| 138 |
+
proj_min, proj_max = proj.min(), proj.max()
|
| 139 |
+
p1 = np.array([x0 + proj_min*vx, y0 + proj_min*vy])
|
| 140 |
+
p2 = np.array([x0 + proj_max*vx, y0 + proj_max*vy])
|
| 141 |
+
|
| 142 |
+
#--------------------------------------------------------------------------------
|
| 143 |
+
# Step C: If apex points are within 'edge_th' of segment, they are connected
|
| 144 |
+
#--------------------------------------------------------------------------------
|
| 145 |
+
if len(apex_pts) < 2:
|
| 146 |
+
continue
|
| 147 |
+
|
| 148 |
+
# Distance from each apex to the line segment
|
| 149 |
+
dists = np.array([
|
| 150 |
+
point_to_segment_dist(apex_pts[i], p1, p2)
|
| 151 |
+
for i in range(len(apex_pts))
|
| 152 |
+
])
|
| 153 |
+
|
| 154 |
+
# Indices of apex points that are near
|
| 155 |
+
near_mask = (dists <= edge_th)
|
| 156 |
+
near_indices = np.where(near_mask)[0]
|
| 157 |
+
if len(near_indices) < 2:
|
| 158 |
+
continue
|
| 159 |
+
|
| 160 |
+
# Connect each pair among these near apex points
|
| 161 |
+
for i in range(len(near_indices)):
|
| 162 |
+
for j in range(i+1, len(near_indices)):
|
| 163 |
+
a_idx = near_indices[i]
|
| 164 |
+
b_idx = near_indices[j]
|
| 165 |
+
# 'a_idx' and 'b_idx' are indices in apex_pts / apex_idx_map
|
| 166 |
+
vA = apex_idx_map[a_idx]
|
| 167 |
+
vB = apex_idx_map[b_idx]
|
| 168 |
+
# Store the connection using sorted indexing
|
| 169 |
+
conn = tuple(sorted((vA, vB)))
|
| 170 |
+
connections.append(conn)
|
| 171 |
+
|
| 172 |
+
return vertices, connections
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
def get_uv_depth(vertices, depth_fitted, sparse_depth, search_radius=10):
|
| 176 |
+
"""
|
| 177 |
+
For each vertex, returns a 2D array of (u,v) and a matching 1D array of depths.
|
| 178 |
+
|
| 179 |
+
We attempt to use the sparse_depth if available in a local neighborhood:
|
| 180 |
+
1. For each vertex coordinate (x, y), define a local window in sparse_depth
|
| 181 |
+
of size (2*search_radius + 1).
|
| 182 |
+
2. Collect all valid (nonzero) values in that window.
|
| 183 |
+
3. If any exist, we take the median (robust) as the vertex depth.
|
| 184 |
+
4. Otherwise, we use depth_fitted[y, x].
|
| 185 |
+
|
| 186 |
+
Parameters
|
| 187 |
+
----------
|
| 188 |
+
vertices : List[dict]
|
| 189 |
+
Each dict must have "xy" at least, e.g. {"xy": (x, y), ...}
|
| 190 |
+
depth_fitted : np.ndarray
|
| 191 |
+
A 2D array (H, W), the dense (or corrected) depth for fallback.
|
| 192 |
+
sparse_depth : np.ndarray
|
| 193 |
+
A 2D array (H, W), mostly zeros except where accurate data is available.
|
| 194 |
+
search_radius : int
|
| 195 |
+
Pixel radius around the vertex in which to look for sparse depth values.
|
| 196 |
+
|
| 197 |
+
Returns
|
| 198 |
+
-------
|
| 199 |
+
uv : np.ndarray of shape (N, 2)
|
| 200 |
+
2D float coordinates of each vertex (x, y).
|
| 201 |
+
vertex_depth : np.ndarray of shape (N,)
|
| 202 |
+
Depth value chosen for each vertex.
|
| 203 |
+
"""
|
| 204 |
+
|
| 205 |
+
# Collect each vertex's (x, y)
|
| 206 |
+
uv = np.array([v['xy'] for v in vertices], dtype=np.float32)
|
| 207 |
+
# Convert to integer pixel coordinates (round or floor)
|
| 208 |
+
uv_int = np.round(uv).astype(np.int32)
|
| 209 |
+
|
| 210 |
+
H, W = depth_fitted.shape[:2]
|
| 211 |
+
# Clip coordinates to stay within image bounds
|
| 212 |
+
uv_int[:, 0] = np.clip(uv_int[:, 0], 0, W-1)
|
| 213 |
+
uv_int[:, 1] = np.clip(uv_int[:, 1], 0, H-1)
|
| 214 |
+
|
| 215 |
+
# Prepare output array of depths
|
| 216 |
+
vertex_depth = np.zeros(len(vertices), dtype=np.float32)
|
| 217 |
+
|
| 218 |
+
for i, (x_i, y_i) in enumerate(uv_int):
|
| 219 |
+
# Local region in [x_i - search_radius, x_i + search_radius]
|
| 220 |
+
x0 = max(0, x_i - search_radius)
|
| 221 |
+
x1 = min(W, x_i + search_radius + 1)
|
| 222 |
+
y0 = max(0, y_i - search_radius)
|
| 223 |
+
y1 = min(H, y_i + search_radius + 1)
|
| 224 |
+
|
| 225 |
+
region = sparse_depth[y0:y1, x0:x1]
|
| 226 |
+
valid_vals = region[region > 0]
|
| 227 |
+
if len(valid_vals) > 0:
|
| 228 |
+
# Use median of valid sparse depth
|
| 229 |
+
vertex_depth[i] = np.median(valid_vals)
|
| 230 |
+
else:
|
| 231 |
+
# Fallback to depth_fitted at this pixel
|
| 232 |
+
vertex_depth[i] = depth_fitted[y_i, x_i]
|
| 233 |
+
|
| 234 |
+
return uv, vertex_depth
|
| 235 |
+
|
| 236 |
+
def merge_vertices_3d(vert_edge_per_image, th=0.5):
|
| 237 |
+
'''Merge vertices that are close to each other in 3D space and are of same types'''
|
| 238 |
+
all_3d_vertices = []
|
| 239 |
+
connections_3d = []
|
| 240 |
+
all_indexes = []
|
| 241 |
+
cur_start = 0
|
| 242 |
+
types = []
|
| 243 |
+
for cimg_idx, (vertices, connections, vertices_3d) in vert_edge_per_image.items():
|
| 244 |
+
types += [int(v['type']=='apex') for v in vertices]
|
| 245 |
+
all_3d_vertices.append(vertices_3d)
|
| 246 |
+
connections_3d+=[(x+cur_start,y+cur_start) for (x,y) in connections]
|
| 247 |
+
cur_start+=len(vertices_3d)
|
| 248 |
+
all_3d_vertices = np.concatenate(all_3d_vertices, axis=0)
|
| 249 |
+
#print (connections_3d)
|
| 250 |
+
distmat = cdist(all_3d_vertices, all_3d_vertices)
|
| 251 |
+
types = np.array(types).reshape(-1,1)
|
| 252 |
+
same_types = cdist(types, types)
|
| 253 |
+
mask_to_merge = (distmat <= th) & (same_types==0)
|
| 254 |
+
new_vertices = []
|
| 255 |
+
new_connections = []
|
| 256 |
+
to_merge = sorted(list(set([tuple(a.nonzero()[0].tolist()) for a in mask_to_merge])))
|
| 257 |
+
to_merge_final = defaultdict(list)
|
| 258 |
+
for i in range(len(all_3d_vertices)):
|
| 259 |
+
for j in to_merge:
|
| 260 |
+
if i in j:
|
| 261 |
+
to_merge_final[i]+=j
|
| 262 |
+
for k, v in to_merge_final.items():
|
| 263 |
+
to_merge_final[k] = list(set(v))
|
| 264 |
+
already_there = set()
|
| 265 |
+
merged = []
|
| 266 |
+
for k, v in to_merge_final.items():
|
| 267 |
+
if k in already_there:
|
| 268 |
+
continue
|
| 269 |
+
merged.append(v)
|
| 270 |
+
for vv in v:
|
| 271 |
+
already_there.add(vv)
|
| 272 |
+
old_idx_to_new = {}
|
| 273 |
+
count=0
|
| 274 |
+
for idxs in merged:
|
| 275 |
+
new_vertices.append(all_3d_vertices[idxs].mean(axis=0))
|
| 276 |
+
for idx in idxs:
|
| 277 |
+
old_idx_to_new[idx] = count
|
| 278 |
+
count +=1
|
| 279 |
+
#print (connections_3d)
|
| 280 |
+
new_vertices=np.array(new_vertices)
|
| 281 |
+
#print (connections_3d)
|
| 282 |
+
for conn in connections_3d:
|
| 283 |
+
new_con = sorted((old_idx_to_new[conn[0]], old_idx_to_new[conn[1]]))
|
| 284 |
+
if new_con[0] == new_con[1]:
|
| 285 |
+
continue
|
| 286 |
+
if new_con not in new_connections:
|
| 287 |
+
new_connections.append(new_con)
|
| 288 |
+
#print (f'{len(new_vertices)} left after merging {len(all_3d_vertices)} with {th=}')
|
| 289 |
+
return new_vertices, new_connections
|
| 290 |
+
|
| 291 |
+
|
| 292 |
+
def prune_not_connected(all_3d_vertices, connections_3d, keep_largest=True):
|
| 293 |
+
"""
|
| 294 |
+
Prune vertices not connected to anything. If keep_largest=True, also
|
| 295 |
+
keep only the largest connected component in the graph.
|
| 296 |
+
"""
|
| 297 |
+
if len(all_3d_vertices) == 0:
|
| 298 |
+
return np.array([]), []
|
| 299 |
+
|
| 300 |
+
# adjacency
|
| 301 |
+
adj = defaultdict(set)
|
| 302 |
+
for (i, j) in connections_3d:
|
| 303 |
+
adj[i].add(j)
|
| 304 |
+
adj[j].add(i)
|
| 305 |
+
|
| 306 |
+
# keep only vertices that appear in at least one edge
|
| 307 |
+
used_idxs = set()
|
| 308 |
+
for (i, j) in connections_3d:
|
| 309 |
+
used_idxs.add(i)
|
| 310 |
+
used_idxs.add(j)
|
| 311 |
+
|
| 312 |
+
if not used_idxs:
|
| 313 |
+
return np.empty((0,3)), []
|
| 314 |
+
|
| 315 |
+
# If we only want to remove truly isolated points, but keep multiple subgraphs:
|
| 316 |
+
if not keep_largest:
|
| 317 |
+
new_map = {}
|
| 318 |
+
used_list = sorted(list(used_idxs))
|
| 319 |
+
for new_id, old_id in enumerate(used_list):
|
| 320 |
+
new_map[old_id] = new_id
|
| 321 |
+
new_vertices = np.array([all_3d_vertices[old_id] for old_id in used_list])
|
| 322 |
+
new_conns = []
|
| 323 |
+
for (i, j) in connections_3d:
|
| 324 |
+
if i in used_idxs and j in used_idxs:
|
| 325 |
+
new_conns.append((new_map[i], new_map[j]))
|
| 326 |
+
return new_vertices, new_conns
|
| 327 |
+
|
| 328 |
+
# Otherwise find the largest connected component:
|
| 329 |
+
visited = set()
|
| 330 |
+
def bfs(start):
|
| 331 |
+
queue = [start]
|
| 332 |
+
comp = []
|
| 333 |
+
visited.add(start)
|
| 334 |
+
while queue:
|
| 335 |
+
cur = queue.pop()
|
| 336 |
+
comp.append(cur)
|
| 337 |
+
for neigh in adj[cur]:
|
| 338 |
+
if neigh not in visited:
|
| 339 |
+
visited.add(neigh)
|
| 340 |
+
queue.append(neigh)
|
| 341 |
+
return comp
|
| 342 |
+
|
| 343 |
+
# Collect all subgraphs
|
| 344 |
+
comps = []
|
| 345 |
+
for idx in used_idxs:
|
| 346 |
+
if idx not in visited:
|
| 347 |
+
c = bfs(idx)
|
| 348 |
+
comps.append(c)
|
| 349 |
+
|
| 350 |
+
# pick largest
|
| 351 |
+
comps.sort(key=lambda c: len(c), reverse=True)
|
| 352 |
+
largest = comps[0] if len(comps)>0 else []
|
| 353 |
+
|
| 354 |
+
# Remap
|
| 355 |
+
new_map = {}
|
| 356 |
+
for new_id, old_id in enumerate(largest):
|
| 357 |
+
new_map[old_id] = new_id
|
| 358 |
+
|
| 359 |
+
new_vertices = np.array([all_3d_vertices[old_id] for old_id in largest])
|
| 360 |
+
new_conns = []
|
| 361 |
+
for (i, j) in connections_3d:
|
| 362 |
+
if i in largest and j in largest:
|
| 363 |
+
new_conns.append((new_map[i], new_map[j]))
|
| 364 |
+
|
| 365 |
+
# remove duplicates
|
| 366 |
+
new_conns = list(set([tuple(sorted(c)) for c in new_conns]))
|
| 367 |
+
return new_vertices, new_conns
|
| 368 |
+
|
| 369 |
+
|
| 370 |
+
|
| 371 |
+
def get_sparse_depth(colmap_rec, img_id, K, R, t, depth):
|
| 372 |
+
H, W = depth.shape
|
| 373 |
+
xyz = []
|
| 374 |
+
rgb = []
|
| 375 |
+
found = False
|
| 376 |
+
for img_id_c, col_img in colmap_rec.images.items():
|
| 377 |
+
if col_img.name == img_id:
|
| 378 |
+
found = True
|
| 379 |
+
break
|
| 380 |
+
if not found:
|
| 381 |
+
return np.zeros((H, W), dtype=np.float32), False
|
| 382 |
+
mat4x4 = np.eye(4)
|
| 383 |
+
mat4x4[:3 ] = col_img.cam_from_world.matrix()
|
| 384 |
+
for pid,p in colmap_rec.points3D.items():
|
| 385 |
+
if col_img.has_point3D(pid):
|
| 386 |
+
xyz.append(p.xyz)
|
| 387 |
+
rgb.append(p.color)
|
| 388 |
+
xyz = np.array(xyz)
|
| 389 |
+
rgb = np.array(rgb)
|
| 390 |
+
xyz_projected = cv2.transform(cv2.convertPointsToHomogeneous(xyz), mat4x4)
|
| 391 |
+
xyz_projected = cv2.convertPointsFromHomogeneous(xyz_projected).reshape(-1, 3)
|
| 392 |
+
uv, _ = cv2.projectPoints(xyz_projected, np.zeros(3), np.zeros(3), np.array(K), np.zeros(4))
|
| 393 |
+
uv = uv.squeeze()
|
| 394 |
+
u, v = uv[:, 0].astype(np.int32), uv[:, 1].astype(np.int32)
|
| 395 |
+
mask = (u >= 0) & (u < W) & (v >= 0) & (v < H)
|
| 396 |
+
u, v = u[mask], v[mask]
|
| 397 |
+
xyz_projected, rgb = xyz_projected[mask], rgb[mask]
|
| 398 |
+
depth = np.zeros((H, W), dtype=np.float32)
|
| 399 |
+
depth[v, u] = xyz_projected[:, 2]
|
| 400 |
+
return depth, True
|
| 401 |
+
|
| 402 |
+
|
| 403 |
+
def fit_scale_robust_median(depth, sparse_depth):
|
| 404 |
+
"""
|
| 405 |
+
Fits the model sparse_depth ~ k * depth + b by minimizing the median
|
| 406 |
+
of absolute residuals, i.e. median( |sparse_depth - (k*depth + b)| ).
|
| 407 |
+
|
| 408 |
+
Parameters
|
| 409 |
+
----------
|
| 410 |
+
depth : np.ndarray
|
| 411 |
+
Array of depth estimates (same shape as sparse_depth).
|
| 412 |
+
sparse_depth : np.ndarray
|
| 413 |
+
Sparse array with precise depth at certain locations
|
| 414 |
+
(0 where data is unavailable).
|
| 415 |
+
|
| 416 |
+
Returns
|
| 417 |
+
-------
|
| 418 |
+
k : float
|
| 419 |
+
The slope of the robust best-fit affine transform.
|
| 420 |
+
b : float
|
| 421 |
+
The intercept of the robust best-fit affine transform.
|
| 422 |
+
depth_fitted : np.ndarray
|
| 423 |
+
The depth array adjusted by the affine fit: k*depth + b.
|
| 424 |
+
"""
|
| 425 |
+
|
| 426 |
+
# 1. Create mask of valid (nonzero) locations in sparse_depth
|
| 427 |
+
mask = (sparse_depth != 0)
|
| 428 |
+
X = depth[mask]
|
| 429 |
+
Y = sparse_depth[mask]
|
| 430 |
+
|
| 431 |
+
# 2. Define the objective: median of absolute residuals
|
| 432 |
+
def median_abs_resid(params, xvals, yvals):
|
| 433 |
+
k, b = params
|
| 434 |
+
return np.median(np.abs(yvals - (k*xvals)))
|
| 435 |
+
|
| 436 |
+
# 3. Get an initial guess from a standard least-squares fit
|
| 437 |
+
# (this helps the optimizer start in a reasonable region)
|
| 438 |
+
k_init, b_init = np.polyfit(X, Y, deg=1)
|
| 439 |
+
|
| 440 |
+
# 4. Optimize using a derivative-free method (Nelder-Mead)
|
| 441 |
+
res = minimize(
|
| 442 |
+
fun=median_abs_resid,
|
| 443 |
+
x0=[k_init, b_init],
|
| 444 |
+
args=(X, Y),
|
| 445 |
+
method='Nelder-Mead'
|
| 446 |
+
)
|
| 447 |
+
|
| 448 |
+
k_robust, b_robust = res.x
|
| 449 |
+
|
| 450 |
+
# 5. Construct the fitted depth array
|
| 451 |
+
depth_fitted = k_robust * depth #+ b_robust
|
| 452 |
+
|
| 453 |
+
return k_robust, depth_fitted
|
| 454 |
+
|
| 455 |
+
|
| 456 |
+
|
| 457 |
+
|
| 458 |
+
def predict(entry, visualize=False) -> Tuple[np.ndarray, List[int]]:
|
| 459 |
+
good_entry = convert_entry_to_human_readable(entry)
|
| 460 |
+
vert_edge_per_image = {}
|
| 461 |
+
for i, (gest, depth, K, R, t, img_id) in enumerate(zip(good_entry['gestalt'],
|
| 462 |
+
good_entry['depth'],
|
| 463 |
+
good_entry['K'],
|
| 464 |
+
good_entry['R'],
|
| 465 |
+
good_entry['t'],
|
| 466 |
+
good_entry['image_ids']
|
| 467 |
+
)):
|
| 468 |
+
colmap_rec = good_entry['colmap_binary']
|
| 469 |
+
K = np.array(K)
|
| 470 |
+
R = np.array(R)
|
| 471 |
+
t = np.array(t)
|
| 472 |
+
gest_seg = gest.resize(depth.size)
|
| 473 |
+
gest_seg_np = np.array(gest_seg).astype(np.uint8)
|
| 474 |
+
# Metric3D
|
| 475 |
+
depth_np = np.array(depth) / 1000.
|
| 476 |
+
depth_sparse, found = get_sparse_depth(colmap_rec, img_id, K, R, t, depth_np)
|
| 477 |
+
if not found:
|
| 478 |
+
print (f'No sparse depth found for image {i}')
|
| 479 |
+
vert_edge_per_image[i] = np.empty((0, 2)), [], np.empty((0, 3))
|
| 480 |
+
continue
|
| 481 |
+
k, depth_fitted = fit_scale_robust_median(depth_np, depth_sparse)#fit_affine_robust_median(depth_np, depth_sparse)
|
| 482 |
+
print (k)
|
| 483 |
+
vertices, connections = get_vertices_and_edges_from_segmentation(gest_seg_np, edge_th = 50.)
|
| 484 |
+
if (len(vertices) < 2) or (len(connections) < 1):
|
| 485 |
+
print (f'Not enough vertices or connections in image {i}')
|
| 486 |
+
vert_edge_per_image[i] = np.empty((0, 2)), [], np.empty((0, 3))
|
| 487 |
+
continue
|
| 488 |
+
|
| 489 |
+
uv, depth_vert = get_uv_depth(vertices, depth_fitted, depth_sparse, 50)
|
| 490 |
+
# Normalize the uv to the camera intrinsics
|
| 491 |
+
X = (uv[:, 0] - K[0, 2]) / K[0, 0] * depth_vert
|
| 492 |
+
Y = (uv[:, 1] - K[1, 2]) / K[1, 1] * depth_vert
|
| 493 |
+
Z = depth_vert
|
| 494 |
+
vertices_3d_local = np.column_stack([X, Y, Z])
|
| 495 |
+
world_to_cam = np.eye(4)
|
| 496 |
+
world_to_cam[:3, :3] = R
|
| 497 |
+
world_to_cam[:3, 3] = t.reshape(-1)
|
| 498 |
+
cam_to_world = np.linalg.inv(world_to_cam)
|
| 499 |
+
vertices_3d = cv2.transform(cv2.convertPointsToHomogeneous(vertices_3d_local), cam_to_world)
|
| 500 |
+
vertices_3d = cv2.convertPointsFromHomogeneous(vertices_3d).reshape(-1, 3)
|
| 501 |
+
vert_edge_per_image[i] = vertices, connections, vertices_3d
|
| 502 |
+
all_3d_vertices, connections_3d = merge_vertices_3d(vert_edge_per_image, 0.25)
|
| 503 |
+
all_3d_vertices_clean, connections_3d_clean = prune_not_connected(all_3d_vertices, connections_3d, keep_largest=False)
|
| 504 |
+
if (len(all_3d_vertices_clean) < 2) or len(connections_3d_clean) < 1:
|
| 505 |
+
print (f'Not enough vertices or connections in the 3D vertices')
|
| 506 |
+
return empty_solution()
|
| 507 |
+
if visualize:
|
| 508 |
+
from hoho.viz3d import plot_estimate_and_gt
|
| 509 |
+
plot_estimate_and_gt( all_3d_vertices_clean,
|
| 510 |
+
connections_3d_clean,
|
| 511 |
+
good_entry['wf_vertices'],
|
| 512 |
+
good_entry['wf_edges'])
|
| 513 |
+
return all_3d_vertices_clean, connections_3d_clean
|
params.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"competition_id": "usm3d/S23DR2025",
|
| 3 |
+
"competition_type": "script",
|
| 4 |
+
"metric": "custom",
|
| 5 |
+
"token": "hf_******",
|
| 6 |
+
"team_id": "xxxxxxxxx_your_team_name_xxxxxxxxxx",
|
| 7 |
+
"submission_id": "xxxxxxxxx_your_sub_id_xxxxxxxxxx",
|
| 8 |
+
"submission_id_col": "order_id",
|
| 9 |
+
"submission_cols": [
|
| 10 |
+
"order_id",
|
| 11 |
+
"wf_vertices",
|
| 12 |
+
"wf_edges",
|
| 13 |
+
"wf_classifications"
|
| 14 |
+
],
|
| 15 |
+
"submission_rows": 267,
|
| 16 |
+
"output_path": "/tmp/model",
|
| 17 |
+
"submission_repo": "<your submission repo>",
|
| 18 |
+
"time_limit": 7200,
|
| 19 |
+
"dataset": "usm3d/hoho25k_test_x",
|
| 20 |
+
"submission_filenames": [
|
| 21 |
+
"submission.parquet"
|
| 22 |
+
]
|
| 23 |
+
}
|
script.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
### This is example of the script that will be run in the test environment.
|
| 2 |
+
|
| 3 |
+
### You can change the rest of the code to define and test your solution.
|
| 4 |
+
### However, you should not change the signature of the provided function.
|
| 5 |
+
### The script saves "submission.parquet" file in the current directory.
|
| 6 |
+
### You can use any additional files and subdirectories to organize your code.
|
| 7 |
+
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
from tqdm import tqdm
|
| 10 |
+
import pandas as pd
|
| 11 |
+
import numpy as np
|
| 12 |
+
from datasets import load_dataset
|
| 13 |
+
from typing import Dict
|
| 14 |
+
|
| 15 |
+
def empty_solution(sample):
|
| 16 |
+
'''Return a minimal valid solution, i.e. 2 vertices and 1 edge.'''
|
| 17 |
+
return np.zeros((2,3)), [(0, 1)]
|
| 18 |
+
from handcrafted_solution import predict
|
| 19 |
+
|
| 20 |
+
class Sample(Dict):
|
| 21 |
+
def pick_repr_data(self, x):
|
| 22 |
+
if hasattr(x, 'shape'):
|
| 23 |
+
return x.shape
|
| 24 |
+
if isinstance(x, (str, float, int)):
|
| 25 |
+
return x
|
| 26 |
+
if isinstance(x, list):
|
| 27 |
+
return [type(x[0])] if len(x) > 0 else []
|
| 28 |
+
return type(x)
|
| 29 |
+
|
| 30 |
+
def __repr__(self):
|
| 31 |
+
# return str({k: v.shape if hasattr(v, 'shape') else [type(v[0])] if isinstance(v, list) else type(v) for k,v in self.items()})
|
| 32 |
+
return str({k: self.pick_repr_data(v) for k,v in self.items()})
|
| 33 |
+
|
| 34 |
+
import json
|
| 35 |
+
if __name__ == "__main__":
|
| 36 |
+
print ("------------ Loading dataset------------ ")
|
| 37 |
+
param_path = Path('params.json')
|
| 38 |
+
print(param_path)
|
| 39 |
+
with param_path.open() as f:
|
| 40 |
+
params = json.load(f)
|
| 41 |
+
print(params)
|
| 42 |
+
import os
|
| 43 |
+
|
| 44 |
+
print('pwd:')
|
| 45 |
+
os.system('pwd')
|
| 46 |
+
print(os.system('ls -lahtr'))
|
| 47 |
+
print('/tmp/data/')
|
| 48 |
+
print(os.system('ls -lahtr /tmp/data/'))
|
| 49 |
+
print('/tmp/data/data')
|
| 50 |
+
print(os.system('ls -lahtrR /tmp/data/data'))
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
data_path_test_server = Path('/tmp/data')
|
| 54 |
+
data_path_local = Path().home() / '.cache/huggingface/datasets/usm3d___hoho25k_test_x/'
|
| 55 |
+
|
| 56 |
+
if data_path_test_server.exists():
|
| 57 |
+
# data_path = data_path_test_server
|
| 58 |
+
TEST_ENV = True
|
| 59 |
+
else:
|
| 60 |
+
# data_path = data_path_local
|
| 61 |
+
TEST_ENV = False
|
| 62 |
+
from huggingface_hub import snapshot_download
|
| 63 |
+
_ = snapshot_download(
|
| 64 |
+
repo_id=params['dataset'],
|
| 65 |
+
local_dir="/tmp/data",
|
| 66 |
+
repo_type="dataset",
|
| 67 |
+
)
|
| 68 |
+
data_path = data_path_test_server
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
print(data_path)
|
| 72 |
+
|
| 73 |
+
# dataset = load_dataset(params['dataset'], trust_remote_code=True, use_auth_token=params['token'])
|
| 74 |
+
# data_files = {
|
| 75 |
+
# "validation": [str(p) for p in [*data_path.rglob('*validation*.arrow')]+[*data_path.rglob('*public*/**/*.tar')]],
|
| 76 |
+
# "test": [str(p) for p in [*data_path.rglob('*test*.arrow')]+[*data_path.rglob('*private*/**/*.tar')]],
|
| 77 |
+
# }
|
| 78 |
+
data_files = {
|
| 79 |
+
"validation": [str(p) for p in data_path.rglob('*public*/**/*.tar')],
|
| 80 |
+
"test": [str(p) for p in data_path.rglob('*private*/**/*.tar')],
|
| 81 |
+
}
|
| 82 |
+
print(data_files)
|
| 83 |
+
dataset = load_dataset(
|
| 84 |
+
str(data_path / 'hoho25k_test_x.py'),
|
| 85 |
+
data_files=data_files,
|
| 86 |
+
trust_remote_code=True,
|
| 87 |
+
writer_batch_size=100
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
# if TEST_ENV:
|
| 91 |
+
# dataset = load_dataset(
|
| 92 |
+
# "webdataset",
|
| 93 |
+
# data_files=data_files,
|
| 94 |
+
# trust_remote_code=True,
|
| 95 |
+
# # streaming=True
|
| 96 |
+
# )
|
| 97 |
+
print('load with webdataset')
|
| 98 |
+
# else:
|
| 99 |
+
|
| 100 |
+
# dataset = load_dataset(
|
| 101 |
+
# "arrow",
|
| 102 |
+
# data_files=data_files,
|
| 103 |
+
# trust_remote_code=True,
|
| 104 |
+
# # streaming=True
|
| 105 |
+
# )
|
| 106 |
+
# print('load with arrow')
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
print(dataset, flush=True)
|
| 110 |
+
# dataset = load_dataset('webdataset', data_files={)
|
| 111 |
+
|
| 112 |
+
print('------------ Now you can do your solution ---------------')
|
| 113 |
+
solution = []
|
| 114 |
+
for subset_name in dataset:
|
| 115 |
+
for i, sample in enumerate(tqdm(dataset[subset_name])):
|
| 116 |
+
# replace this with your solution
|
| 117 |
+
print(Sample(sample), flush=True)
|
| 118 |
+
print('------')
|
| 119 |
+
try:
|
| 120 |
+
pred_vertices, pred_edges = predict(sample, visualize=False)
|
| 121 |
+
except:
|
| 122 |
+
pred_vertices, pred_edges = empty_solution(sample)
|
| 123 |
+
solution.append({
|
| 124 |
+
'order_id': sample['order_id'],
|
| 125 |
+
'wf_vertices': pred_vertices.tolist(),
|
| 126 |
+
'wf_edges': pred_edges
|
| 127 |
+
})
|
| 128 |
+
|
| 129 |
+
print('------------ Saving results ---------------')
|
| 130 |
+
sub = pd.DataFrame(solution, columns=["order_id", "wf_vertices", "wf_edges"])
|
| 131 |
+
sub.to_parquet("submission.parquet")
|
| 132 |
+
print("------------ Done ------------ ")
|