LiamKhoaLe commited on
Commit
125e09d
·
1 Parent(s): 71c4d7a

Retry with (and len(seg) < best_len) removed

Browse files
Files changed (2) hide show
  1. .DS_Store +0 -0
  2. app.py +50 -49
.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 = 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)]
176
- g = {start: 0}
177
- came = {}; visited = set() # Flag visited for path optimization
178
- H, W = occ.shape
179
  while openq:
180
- _, cur = heapq.heappop(openq)
181
- if cur == goal:
182
- p = [cur]
183
- while cur in came:
184
- cur = came[cur]
185
- p.append(cur)
186
  return p[::-1]
187
- if cur in visited:
188
- continue
189
- visited.add(cur)
190
- for dx, dy in N8:
191
- nx, ny = cur[0] + dx, cur[1] + dy
192
- if not (0 <= nx < W and 0 <= ny < H): continue
193
- if occ[ny, nx] == 0: continue
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
- next_node = (nx, ny)
199
- ng = g[cur] + 1
200
- if next_node not in g or ng < g[next_node]:
201
- g[next_node] = ng
202
- f = ng + h(next_node, goal)
203
- heapq.heappush(openq, (f, next_node))
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 = [] # flag unreachable apart
213
- for t in todo:
214
- seg = astar(cur, tuple(t), occ)
215
- if seg:
216
- if path and path[-1] == seg[0]:
217
- seg = seg[1:]
218
- path.extend(seg)
219
- reachable.append(t)
220
- cur = tuple(t)
221
- else:
222
- unreachable.append(t)
223
- print(f"⚠️ Unreachable garbage at {t}")
224
- return path, reachable
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- # Unreachable targets flagged
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"