algorithm update: in order to be fit, the detected line is required to be in the same direction as proposed one
Browse files- handcrafted_solution.py +90 -48
handcrafted_solution.py
CHANGED
@@ -104,45 +104,38 @@ def get_vertices_and_edges_from_segmentation(gest_seg_np, edge_th=50.0):
|
|
104 |
color_range = 4.
|
105 |
connections = []
|
106 |
edge_th = edge_th ** 2
|
|
|
107 |
|
108 |
apex_centroids, eave_end_point_centroids, apex_mask, eave_end_point_mask = get_vertices(gest_seg_np)
|
109 |
|
110 |
-
|
111 |
|
112 |
-
# vertex_mask = np.zeros_like(apex_mask)
|
113 |
-
# for i in apex_centroids:
|
114 |
-
# cv2.circle(vertex_mask, np.round(i).astype(np.uint32), 20, (255,), 40, -1)
|
115 |
-
# for i in eave_end_point_centroids:
|
116 |
-
# cv2.circle(vertex_mask, np.round(i).astype(np.uint32), 15, (255,), 30, -1)
|
117 |
-
# vertex_mask = np.bitwise_not(vertex_mask)
|
118 |
|
119 |
scale = 1
|
120 |
-
vertex_size = np.zeros(
|
121 |
-
for i, coords in enumerate(
|
122 |
# coords = np.round(coords).astype(np.uint32)
|
123 |
radius = 25#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))
|
124 |
vertex_size[i] = (scale*radius) ** 2 # because we are using squared distances
|
125 |
-
|
126 |
-
# vertex_size *= scale
|
127 |
for edge_class in ['eave', 'ridge', 'rake', 'valley', 'flashing', 'step_flashing']:
|
128 |
-
if len(
|
129 |
break
|
130 |
edge_color = np.array(gestalt_color_mapping[edge_class])
|
131 |
|
132 |
mask = cv2.inRange(gest_seg_np,
|
133 |
edge_color-color_range,
|
134 |
edge_color+color_range)
|
135 |
-
# mask = cv2.bitwise_and(mask, vertex_mask)
|
136 |
-
|
137 |
mask = cv2.morphologyEx(mask,
|
138 |
cv2.MORPH_DILATE, np.ones((3, 3)), iterations=1)
|
139 |
-
|
140 |
-
# mask = cv2.morphologyEx(mask,
|
141 |
-
# cv2.MORPH_DILATE, np.ones((11, 11)), iterations=1)
|
142 |
|
143 |
|
144 |
if np.any(mask):
|
145 |
|
|
|
|
|
|
|
146 |
rho = 1 # distance resolution in pixels of the Hough grid
|
147 |
theta = np.pi / 180 # angular resolution in radians of the Hough grid
|
148 |
threshold = 20 # minimum number of votes (intersections in Hough grid cell)
|
@@ -155,59 +148,108 @@ def get_vertices_and_edges_from_segmentation(gest_seg_np, edge_th=50.0):
|
|
155 |
lines = cv2.HoughLinesP(mask, rho, theta, threshold, np.array([]),
|
156 |
min_line_length, max_line_gap)
|
157 |
|
|
|
158 |
edges = []
|
159 |
|
160 |
-
|
|
|
|
|
|
|
|
|
161 |
for x1,y1,x2,y2 in line:
|
162 |
-
extend =
|
163 |
if x1 < x2:
|
164 |
x1, y1, x2, y2 = x2, y2, x1, y1
|
165 |
-
# cv2.line(mask_copy,(x1,y1),(x2,y2),(255,),10, cv2.LINE_AA)
|
166 |
direction = (np.array([x2 - x1, y2 - y1]))
|
167 |
-
direction =
|
|
|
|
|
|
|
168 |
|
169 |
-
# norm = extend * np.linalg.norm(np.array([y2 - y1, x1 - x2]))
|
170 |
x1,y1 = (-direction + (x1, y1)).astype(np.int32)
|
171 |
x2,y2 = (+ direction + (x2, y2)).astype(np.int32)
|
172 |
|
173 |
-
|
174 |
edges.append((x1, y1, x2, y2))
|
175 |
|
176 |
-
edges = np.array(edges)
|
177 |
-
if len(edges) < 1:
|
178 |
-
continue
|
179 |
|
180 |
-
begin_distances = cdist(apex_pts, edges[:, :2], metric="sqeuclidean")
|
181 |
-
end_distances = cdist(apex_pts, edges[:, 2:], metric="sqeuclidean")
|
182 |
|
183 |
-
begin_closest_points = np.argmin(begin_distances, axis=0) # index of the closest point for each edge end point
|
184 |
-
end_closest_points = np.argmin(end_distances, axis=0)
|
185 |
|
186 |
-
|
187 |
-
|
|
|
|
|
|
|
|
|
188 |
|
|
|
|
|
189 |
|
|
|
|
|
190 |
|
191 |
-
begin_in_range_mask = begin_closest_point_distances < vertex_size[begin_closest_points]
|
192 |
-
end_in_range_mask = end_closest_point_distances < vertex_size[end_closest_points]
|
193 |
|
194 |
# where both ends are in range
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
208 |
continue
|
209 |
-
connections.extend(unique_edges)
|
210 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
211 |
|
212 |
vertices = [{"xy": v, "type": "apex"} for v in apex_centroids]
|
213 |
vertices += [{"xy": v, "type": "eave_end_point"} for v in eave_end_point_centroids]
|
|
|
104 |
color_range = 4.
|
105 |
connections = []
|
106 |
edge_th = edge_th ** 2
|
107 |
+
deviation_threshold = np.cos(np.deg2rad(5))
|
108 |
|
109 |
apex_centroids, eave_end_point_centroids, apex_mask, eave_end_point_mask = get_vertices(gest_seg_np)
|
110 |
|
111 |
+
vertices = np.concatenate([apex_centroids, eave_end_point_centroids])
|
112 |
|
|
|
|
|
|
|
|
|
|
|
|
|
113 |
|
114 |
scale = 1
|
115 |
+
vertex_size = np.zeros(vertices.shape[0])
|
116 |
+
for i, coords in enumerate(vertices):
|
117 |
# coords = np.round(coords).astype(np.uint32)
|
118 |
radius = 25#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))
|
119 |
vertex_size[i] = (scale*radius) ** 2 # because we are using squared distances
|
120 |
+
|
|
|
121 |
for edge_class in ['eave', 'ridge', 'rake', 'valley', 'flashing', 'step_flashing']:
|
122 |
+
if len(vertices) < 2:
|
123 |
break
|
124 |
edge_color = np.array(gestalt_color_mapping[edge_class])
|
125 |
|
126 |
mask = cv2.inRange(gest_seg_np,
|
127 |
edge_color-color_range,
|
128 |
edge_color+color_range)
|
|
|
|
|
129 |
mask = cv2.morphologyEx(mask,
|
130 |
cv2.MORPH_DILATE, np.ones((3, 3)), iterations=1)
|
131 |
+
|
|
|
|
|
132 |
|
133 |
|
134 |
if np.any(mask):
|
135 |
|
136 |
+
|
137 |
+
|
138 |
+
|
139 |
rho = 1 # distance resolution in pixels of the Hough grid
|
140 |
theta = np.pi / 180 # angular resolution in radians of the Hough grid
|
141 |
threshold = 20 # minimum number of votes (intersections in Hough grid cell)
|
|
|
148 |
lines = cv2.HoughLinesP(mask, rho, theta, threshold, np.array([]),
|
149 |
min_line_length, max_line_gap)
|
150 |
|
151 |
+
|
152 |
edges = []
|
153 |
|
154 |
+
if lines is None:
|
155 |
+
continue
|
156 |
+
|
157 |
+
line_directions = np.zeros((len(lines), 2))
|
158 |
+
for line_idx, line in enumerate(lines):
|
159 |
for x1,y1,x2,y2 in line:
|
160 |
+
extend = 35
|
161 |
if x1 < x2:
|
162 |
x1, y1, x2, y2 = x2, y2, x1, y1
|
|
|
163 |
direction = (np.array([x2 - x1, y2 - y1]))
|
164 |
+
direction = direction / np.linalg.norm(direction)
|
165 |
+
line_directions[line_idx] = direction
|
166 |
+
|
167 |
+
direction = extend * direction
|
168 |
|
|
|
169 |
x1,y1 = (-direction + (x1, y1)).astype(np.int32)
|
170 |
x2,y2 = (+ direction + (x2, y2)).astype(np.int32)
|
171 |
|
|
|
172 |
edges.append((x1, y1, x2, y2))
|
173 |
|
|
|
|
|
|
|
174 |
|
|
|
|
|
175 |
|
|
|
|
|
176 |
|
177 |
+
edges = np.array(edges)
|
178 |
+
if len(edges) <1:
|
179 |
+
continue
|
180 |
+
# calculate the distances between the vertices and the edge ends
|
181 |
+
begin_distances = cdist(vertices, edges[:, :2], metric="sqeuclidean")
|
182 |
+
end_distances = cdist(vertices, edges[:, 2:], metric="sqeuclidean")
|
183 |
|
184 |
+
begin_in_range_mask = begin_distances < vertex_size[:, np.newaxis]
|
185 |
+
end_in_range_mask = end_distances < vertex_size[:, np.newaxis]
|
186 |
|
187 |
+
in_range_connected_mask = np.logical_and(np.any(begin_in_range_mask, axis=0),
|
188 |
+
np.any(end_in_range_mask, axis=0))
|
189 |
|
|
|
|
|
190 |
|
191 |
# where both ends are in range
|
192 |
+
begin_in_range_mask = np.logical_and(begin_in_range_mask, in_range_connected_mask)
|
193 |
+
end_in_range_mask = np.logical_and(end_in_range_mask, in_range_connected_mask)
|
194 |
+
|
195 |
+
begin_candidates = np.array(np.where(begin_in_range_mask))
|
196 |
+
end_candidates = np.array(np.where(end_in_range_mask))
|
197 |
+
|
198 |
+
# sort the candidates by line index; required for the seamlessnes np.split
|
199 |
+
sorted_begin_indices = np.argsort(begin_candidates[1])
|
200 |
+
sorted_end_indices = np.argsort(end_candidates[1])
|
201 |
+
begin_candidates = begin_candidates[:, sorted_begin_indices]
|
202 |
+
end_candidates = end_candidates[:, sorted_end_indices]
|
203 |
+
|
204 |
+
|
205 |
+
# create all possible connections between begin and end candidates that correspond to a line
|
206 |
+
grouped_begins = np.split(begin_candidates[0], np.unique(begin_candidates[1], return_index=True)[1][1:])
|
207 |
+
grouped_ends = np.split(end_candidates[0], np.unique(end_candidates[1], return_index=True)[1][1:])
|
208 |
+
line_indices = np.unique(begin_candidates[1])
|
209 |
+
|
210 |
+
# create all possible connections between begin and end candidates that correspond to a line
|
211 |
+
begin_vertex_list = []
|
212 |
+
end_vertex_list = []
|
213 |
+
line_idx_list = []
|
214 |
+
for begin_vertex, end_vertex, line_idx in zip(grouped_begins, grouped_ends, line_indices):
|
215 |
+
begin_vertex, end_vertex = np.meshgrid(begin_vertex, end_vertex)
|
216 |
+
begin_vertex_list.extend(begin_vertex.flatten())
|
217 |
+
end_vertex_list.extend(end_vertex.flatten())
|
218 |
+
line_idx_list.extend([line_idx] * len(begin_vertex.flatten()))
|
219 |
+
|
220 |
+
line_idx_list = np.array(line_idx_list)
|
221 |
+
all_connections = np.array([begin_vertex_list, end_vertex_list])
|
222 |
+
|
223 |
+
# decrease the number of possible connections to reduce number of calculations
|
224 |
+
possible_connections = np.unique(all_connections, axis=1)
|
225 |
+
possible_connections = np.sort(possible_connections, axis=0)
|
226 |
+
possible_connections = np.unique(possible_connections, axis=1)
|
227 |
+
possible_connections = possible_connections[:, possible_connections[0, :] != possible_connections[1, :]]
|
228 |
+
|
229 |
+
if possible_connections.shape[1] < 1:
|
230 |
continue
|
|
|
231 |
|
232 |
+
# precalculate the possible direction vectors
|
233 |
+
possible_direction_vectors = vertices[possible_connections[0]] - vertices[possible_connections[1]]
|
234 |
+
possible_direction_vectors = possible_direction_vectors / np.linalg.norm(possible_direction_vectors, axis=1)[:, np.newaxis]
|
235 |
+
|
236 |
+
owned_lines_per_possible_connections = [list() for i in range(possible_connections.shape[1])]
|
237 |
+
|
238 |
+
# assign lines to possible connections
|
239 |
+
for line_idx, i,j in zip(line_idx_list, begin_vertex_list, end_vertex_list):
|
240 |
+
if i == j:
|
241 |
+
continue
|
242 |
+
i, j = min(i, j), max(i, j)
|
243 |
+
for connection_idx, connection in enumerate(possible_connections.T):
|
244 |
+
if np.all((i, j) == connection):
|
245 |
+
owned_lines_per_possible_connections[connection_idx].append(line_idx)
|
246 |
+
break
|
247 |
+
|
248 |
+
# check if the lines are in the same direction as the possible connection
|
249 |
+
for fitted_line_idx, owned_lines_per_possible_connection in enumerate(owned_lines_per_possible_connections):
|
250 |
+
line_deviations = np.abs(np.dot(line_directions[owned_lines_per_possible_connection], possible_direction_vectors[fitted_line_idx]))
|
251 |
+
if np.any(line_deviations > deviation_threshold):
|
252 |
+
connections.append(possible_connections[:, fitted_line_idx])
|
253 |
|
254 |
vertices = [{"xy": v, "type": "apex"} for v in apex_centroids]
|
255 |
vertices += [{"xy": v, "type": "eave_end_point"} for v in eave_end_point_centroids]
|