Commit
·
125e09d
1
Parent(s):
71c4d7a
Retry with (and len(seg) < best_len) removed
Browse files
.DS_Store
CHANGED
Binary files a/.DS_Store and b/.DS_Store differ
|
|
app.py
CHANGED
@@ -170,58 +170,63 @@ def highlight_water_mask_on_frame(frame, binary_mask, color=(255, 0, 0), alpha=0
|
|
170 |
|
171 |
# ── A* and KNN over binary water grid ─────────────────────────────────
|
172 |
def astar(start, goal, occ):
|
173 |
-
h
|
174 |
-
N8
|
175 |
-
openq
|
176 |
-
g = {start: 0}
|
177 |
-
came = {}; visited = set() # Flag visited for path optimization
|
178 |
-
H, W = occ.shape
|
179 |
while openq:
|
180 |
-
_,
|
181 |
-
if cur
|
182 |
-
p
|
183 |
-
while cur in came:
|
184 |
-
cur = came[cur]
|
185 |
-
p.append(cur)
|
186 |
return p[::-1]
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
if
|
193 |
-
|
194 |
-
# prevent corner-cutting on diagonals
|
195 |
-
if abs(dx) == 1 and abs(dy) == 1:
|
196 |
-
if occ[cur[1] + dy, cur[0]] == 0 or occ[cur[1], cur[0] + dx] == 0:
|
197 |
continue
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
f
|
203 |
-
|
204 |
-
came[next_node] = cur
|
205 |
-
print(f"[A*] No path from {start} to {goal}")
|
206 |
return []
|
207 |
-
|
208 |
# KNN fit optimal path
|
209 |
def knn_path(start, targets, occ):
|
210 |
todo = targets[:]; path=[]
|
211 |
cur = tuple(start)
|
212 |
-
reachable = []; unreachable = []
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
225 |
|
226 |
|
227 |
# ── Robot sprite/class -──────────────────────────────────────────────────
|
@@ -483,11 +488,7 @@ def _pipeline(uid,img_path):
|
|
483 |
robot = Robot(SPRITE)
|
484 |
# Robot will be spawn on the closest movable mask to top-left
|
485 |
robot.pos = [spawn_x, spawn_y]
|
486 |
-
|
487 |
-
path, unreachable_targets = knn_path(robot.pos, centres, movable_mask)
|
488 |
-
objs = [{"pos": p, "col": False, "unreachable": False} for p in centres if p not in unreachable_targets]
|
489 |
-
objs += [{"pos": p, "col": False, "unreachable": True} for p in unreachable_targets]
|
490 |
-
|
491 |
|
492 |
# 4- Video synthesis
|
493 |
out_tmp=f"{OUTPUT_DIR}/{uid}_tmp.mp4"
|
|
|
170 |
|
171 |
# ── A* and KNN over binary water grid ─────────────────────────────────
|
172 |
def astar(start, goal, occ):
|
173 |
+
h = lambda a,b: abs(a[0]-b[0])+abs(a[1]-b[1])
|
174 |
+
N8 = [(-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1)]
|
175 |
+
openq=[(0,start)]; g={start:0}; came={}
|
|
|
|
|
|
|
176 |
while openq:
|
177 |
+
_,cur=heapq.heappop(openq)
|
178 |
+
if cur==goal:
|
179 |
+
p=[cur]; # reconstruct
|
180 |
+
while cur in came: cur=came[cur]; p.append(cur)
|
|
|
|
|
181 |
return p[::-1]
|
182 |
+
for dx,dy in N8:
|
183 |
+
nx,ny=cur[0]+dx,cur[1]+dy
|
184 |
+
# out-of-bounds / blocked
|
185 |
+
if not (0<=nx<640 and 0<=ny<640) or occ[ny,nx]==0: continue
|
186 |
+
# if diagonal, ensure both orthogonals are free
|
187 |
+
if abs(dx)==1 and abs(dy)==1:
|
188 |
+
if occ[cur[1]+dy, cur[0]]==0 or occ[cur[1], cur[0]+dx]==0:
|
|
|
|
|
|
|
189 |
continue
|
190 |
+
ng=g[cur]+1
|
191 |
+
if (nx,ny) not in g or ng<g[(nx,ny)]:
|
192 |
+
g[(nx,ny)]=ng
|
193 |
+
f=ng+h((nx,ny),goal)
|
194 |
+
heapq.heappush(openq,(f,(nx,ny)))
|
195 |
+
came[(nx,ny)]=cur
|
|
|
|
|
196 |
return []
|
|
|
197 |
# KNN fit optimal path
|
198 |
def knn_path(start, targets, occ):
|
199 |
todo = targets[:]; path=[]
|
200 |
cur = tuple(start)
|
201 |
+
reachable = []; unreachable = []
|
202 |
+
while todo:
|
203 |
+
# KNN follow a Greedy approach, which may not guarantee shortest path, hence only use A*
|
204 |
+
# nbrs = NearestNeighbors(n_neighbors=1).fit(todo)
|
205 |
+
# _,idx = nbrs.kneighbors([cur]); nxt=tuple(todo[idx[0][0]])
|
206 |
+
# seg = astar(cur, nxt, occ)
|
207 |
+
# if seg:
|
208 |
+
# if path and seg[0]==path[-1]: seg=seg[1:]
|
209 |
+
# path.extend(seg)
|
210 |
+
# cur = nxt; todo.remove(list(nxt))
|
211 |
+
best = None
|
212 |
+
best_len = float('inf')
|
213 |
+
best_seg = []
|
214 |
+
# Try A* to each target, find shortest actual path
|
215 |
+
for t in todo:
|
216 |
+
seg = astar(cur, tuple(t), occ)
|
217 |
+
if seg: # and len(seg) < best_len: # index error?
|
218 |
+
best = tuple(t)
|
219 |
+
best_len = len(seg)
|
220 |
+
best_seg = seg
|
221 |
+
if not best:
|
222 |
+
print("⚠️ Some garbage unreachable")
|
223 |
+
break # stop if no reachable targets left
|
224 |
+
if path and path[-1] == best_seg[0]:
|
225 |
+
best_seg = best_seg[1:] # avoid duplicate point
|
226 |
+
path.extend(best_seg)
|
227 |
+
cur = best
|
228 |
+
todo.remove(list(best))
|
229 |
+
return path
|
230 |
|
231 |
|
232 |
# ── Robot sprite/class -──────────────────────────────────────────────────
|
|
|
488 |
robot = Robot(SPRITE)
|
489 |
# Robot will be spawn on the closest movable mask to top-left
|
490 |
robot.pos = [spawn_x, spawn_y]
|
491 |
+
path = knn_path(robot.pos, centres, movable_mask)
|
|
|
|
|
|
|
|
|
492 |
|
493 |
# 4- Video synthesis
|
494 |
out_tmp=f"{OUTPUT_DIR}/{uid}_tmp.mp4"
|