maple-matrix commited on
Commit
4f2086c
·
verified ·
1 Parent(s): a485b34

add predict_batch interface for chute template

Browse files
Files changed (1) hide show
  1. miner.py +64 -65
miner.py CHANGED
@@ -148,74 +148,73 @@ class Miner:
148
  keep.sort()
149
  return np.array(keep, dtype=np.intp)
150
 
151
- def run(self, frames: list[ndarray]) -> list[TVFrameResult]:
152
- results: list[TVFrameResult] = []
153
- for frame_id, frame in enumerate(frames):
154
- if frame is None:
155
- results.append(TVFrameResult(frame_id=frame_id, boxes=[], keypoints=[]))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  continue
157
-
158
- x, scale, (dx, dy), (W, H) = self._preprocess(frame)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  try:
160
- out = self.session.run(None, {self.input_name: x})[0]
161
  except Exception as e:
162
- print(f"frame {frame_id} ONNX error: {e}")
163
- results.append(TVFrameResult(frame_id=frame_id, boxes=[], keypoints=[]))
164
- continue
165
-
166
- # output shape: [1, 300, 6] — (x1, y1, x2, y2, conf, cls_id)
167
- raw = out[0]
168
- if raw.shape[0] == 0:
169
- results.append(TVFrameResult(frame_id=frame_id, boxes=[], keypoints=[]))
170
- continue
171
-
172
- # Apply conf filter (keep raw for fallback)
173
- mask_conf = raw[:, 4] >= self.conf_thresh
174
- primary = raw[mask_conf]
175
-
176
- # Per-class dedup on the conf-filtered set
177
- final_boxes = []
178
- if len(primary) > 0:
179
- xyxy = primary[:, :4].astype(np.float32)
180
- scores = primary[:, 4].astype(np.float32)
181
- cls_ids = primary[:, 5].astype(np.int32)
182
- keep_idx = self._dedup(xyxy, scores, cls_ids)
183
- primary = primary[keep_idx]
184
-
185
- for det in primary:
186
- final_boxes.append(det)
187
-
188
- # Fallback: nothing left → return the single highest-conf raw box (if above floor)
189
- if not final_boxes:
190
- if raw.shape[0] > 0:
191
- top = raw[np.argmax(raw[:, 4])]
192
- if top[4] >= self.fallback_min_conf:
193
- final_boxes.append(top)
194
-
195
- # Build BoundingBox list with un-letterbox + remap cls_id
196
- boxes_out: list[BoundingBox] = []
197
- for det in final_boxes:
198
- x1, y1, x2, y2, conf, model_cls_id = det
199
- # un-letterbox: subtract padding then scale back
200
- x1 = (x1 - dx) / scale
201
- x2 = (x2 - dx) / scale
202
- y1 = (y1 - dy) / scale
203
- y2 = (y2 - dy) / scale
204
- # clip
205
- x1 = max(0.0, min(W - 1.0, x1))
206
- x2 = max(0.0, min(W - 1.0, x2))
207
- y1 = max(0.0, min(H - 1.0, y1))
208
- y2 = max(0.0, min(H - 1.0, y2))
209
- if x2 <= x1 or y2 <= y1:
210
- continue
211
- # cls remap: v8 model order → validator-visible order
212
- mapped_cls = int(self.cls_remap[int(model_cls_id)])
213
- boxes_out.append(BoundingBox(
214
- x1=int(x1), y1=int(y1), x2=int(x2), y2=int(y2),
215
- cls_id=mapped_cls, conf=float(conf),
216
- ))
217
-
218
  results.append(TVFrameResult(
219
- frame_id=frame_id, boxes=boxes_out, keypoints=[],
 
 
220
  ))
221
  return results
 
 
 
 
 
148
  keep.sort()
149
  return np.array(keep, dtype=np.intp)
150
 
151
+ def _predict_one(self, frame: ndarray) -> list[BoundingBox]:
152
+ x, scale, (dx, dy), (W, H) = self._preprocess(frame)
153
+ out = self.session.run(None, {self.input_name: x})[0]
154
+ # output shape: [1, 300, 6] — (x1, y1, x2, y2, conf, cls_id)
155
+ raw = out[0]
156
+ if raw.shape[0] == 0:
157
+ return []
158
+
159
+ # Apply conf filter (keep raw for fallback)
160
+ primary = raw[raw[:, 4] >= self.conf_thresh]
161
+
162
+ # Per-class dedup on the conf-filtered set
163
+ final_dets = []
164
+ if len(primary) > 0:
165
+ xyxy = primary[:, :4].astype(np.float32)
166
+ scores = primary[:, 4].astype(np.float32)
167
+ cls_ids = primary[:, 5].astype(np.int32)
168
+ keep_idx = self._dedup(xyxy, scores, cls_ids)
169
+ primary = primary[keep_idx]
170
+ for det in primary:
171
+ final_dets.append(det)
172
+
173
+ # Fallback: nothing left → return single highest-conf raw box (above floor)
174
+ if not final_dets and raw.shape[0] > 0:
175
+ top = raw[np.argmax(raw[:, 4])]
176
+ if top[4] >= self.fallback_min_conf:
177
+ final_dets.append(top)
178
+
179
+ # Build BoundingBox list with un-letterbox + cls remap
180
+ boxes_out: list[BoundingBox] = []
181
+ for det in final_dets:
182
+ x1, y1, x2, y2, conf, model_cls_id = det
183
+ x1 = (x1 - dx) / scale; x2 = (x2 - dx) / scale
184
+ y1 = (y1 - dy) / scale; y2 = (y2 - dy) / scale
185
+ x1 = max(0.0, min(W - 1.0, x1)); x2 = max(0.0, min(W - 1.0, x2))
186
+ y1 = max(0.0, min(H - 1.0, y1)); y2 = max(0.0, min(H - 1.0, y2))
187
+ if x2 <= x1 or y2 <= y1:
188
  continue
189
+ mapped_cls = int(self.cls_remap[int(model_cls_id)])
190
+ boxes_out.append(BoundingBox(
191
+ x1=int(x1), y1=int(y1), x2=int(x2), y2=int(y2),
192
+ cls_id=mapped_cls, conf=float(conf),
193
+ ))
194
+ return boxes_out
195
+
196
+ def predict_batch(
197
+ self,
198
+ batch_images: list[ndarray],
199
+ offset: int,
200
+ n_keypoints: int,
201
+ ) -> list[TVFrameResult]:
202
+ """Required interface for chute template (sv_chutes_*.py)."""
203
+ results: list[TVFrameResult] = []
204
+ for frame_number_in_batch, image in enumerate(batch_images):
205
  try:
206
+ boxes = self._predict_one(image)
207
  except Exception as e:
208
+ print(f"⚠️ Inference failed for frame "
209
+ f"{offset + frame_number_in_batch}: {e}")
210
+ boxes = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  results.append(TVFrameResult(
212
+ frame_id=offset + frame_number_in_batch,
213
+ boxes=boxes,
214
+ keypoints=[(0, 0) for _ in range(max(0, int(n_keypoints)))],
215
  ))
216
  return results
217
+
218
+ # Back-compat alias for local sanity testing
219
+ def run(self, frames: list[ndarray]) -> list[TVFrameResult]:
220
+ return self.predict_batch(frames, offset=0, n_keypoints=0)