meaculpitt commited on
Commit
44ef5a4
Β·
verified Β·
1 Parent(s): c0918f0

scorevision: push artifact

Browse files
Files changed (1) hide show
  1. miner.py +110 -13
miner.py CHANGED
@@ -173,6 +173,18 @@ import json
173
  import threading
174
  from datetime import datetime, timezone
175
  from concurrent.futures import ThreadPoolExecutor, as_completed
 
 
 
 
 
 
 
 
 
 
 
 
176
 
177
  logger = logging.getLogger(__name__)
178
 
@@ -263,6 +275,14 @@ PER_TILE_CONF = 0.55 # raised from 0.40 to match PER_CONF_LOW
263
  PER_NMS_IOU = 0.50 # NMS IoU for merging across passes (max-conf wins)
264
  PER_MAX_DET = 15 # hard cap on person detections per image
265
 
 
 
 
 
 
 
 
 
266
  # ── Pose FP filter + box refinement config ──────────────────────────────────
267
  POSE_CONF_THRESH = 0.25 # Minimum confidence for pose detection
268
  POSE_NMS_IOU = 0.65 # NMS IoU threshold for pose detections
@@ -1194,12 +1214,20 @@ class Miner:
1194
  return inp, ratio, pl, pt
1195
 
1196
  def _per_enhance(self, img_bgr):
1197
- """CLAHE contrast enhancement (clip=12) on LAB L-channel."""
1198
  lab = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2LAB)
1199
  l, a, b = cv2.split(lab)
1200
- clahe = cv2.createCLAHE(clipLimit=12.0, tileGridSize=(8, 8))
1201
- l = clahe.apply(l)
1202
- return cv2.cvtColor(cv2.merge([l, a, b]), cv2.COLOR_LAB2BGR)
 
 
 
 
 
 
 
 
1203
 
1204
  def _per_decode(self, raw, ratio, pl, pt, oh, ow, conf_thresh):
1205
  pred = raw[0]
@@ -1714,6 +1742,10 @@ class Miner:
1714
  oh, ow = image_bgr.shape[:2]
1715
  t_start = time.monotonic()
1716
 
 
 
 
 
1717
  # Collect all boxes in original pixel coords
1718
  all_boxes = [] # list of [N, 4] arrays
1719
  all_confs = [] # list of [N] arrays
@@ -1754,6 +1786,10 @@ class Miner:
1754
  if len(merged_b) == 0:
1755
  return []
1756
 
 
 
 
 
1757
  # Sanity filters
1758
  img_area = float(oh * ow)
1759
  out = []
@@ -1785,10 +1821,46 @@ class Miner:
1785
 
1786
  return out
1787
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1788
  # ── Unified inference ──────────────────────────────────���────────────────
1789
 
1790
- def _infer_single(self, image_bgr: ndarray) -> list[BoundingBox]:
1791
  self._cached_pose_data = None # reset before each frame
 
 
 
 
 
 
 
 
 
 
1792
  if ENABLE_PARALLEL:
1793
  veh_future = self._executor.submit(self._infer_vehicle, image_bgr)
1794
  per_future = self._executor.submit(self._infer_person, image_bgr)
@@ -1856,21 +1928,46 @@ class Miner:
1856
  ) -> list[TVFrameResult]:
1857
  t_start = time.perf_counter()
1858
 
 
 
 
 
 
 
 
 
 
 
1859
  results: list[TVFrameResult] = []
1860
  for idx, image in enumerate(batch_images):
1861
  t_img = time.perf_counter()
1862
- boxes = self._infer_single(image)
1863
- dt_img = (time.perf_counter() - t_img) * 1000
1864
- logger.info(f"[miner] image {idx}: {len(boxes)} boxes in {dt_img:.0f}ms "
1865
- f"(shape={image.shape}, TTA={ENABLE_TTA}, PAR={ENABLE_PARALLEL})")
1866
  keypoints = [(0, 0) for _ in range(max(0, int(n_keypoints)))]
1867
  results.append(TVFrameResult(
1868
  frame_id=offset + idx, boxes=boxes, keypoints=keypoints,
1869
  ))
1870
-
1871
- dt_total = (time.perf_counter() - t_start) * 1000
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1872
  logger.info(f"[miner] predict_batch: {len(batch_images)} images, "
1873
- f"{sum(len(r.boxes) for r in results)} total boxes, {dt_total:.0f}ms")
1874
 
1875
  threading.Thread(
1876
  target=self._replay_save,
@@ -1879,4 +1976,4 @@ class Miner:
1879
  ).start()
1880
 
1881
  return results
1882
- # Miner v3.15 β€” background TRT engine build + CUDA-first fallback 20260402
 
173
  import threading
174
  from datetime import datetime, timezone
175
  from concurrent.futures import ThreadPoolExecutor, as_completed
176
+ import inspect
177
+
178
+ # ── Latency logger (per-request timing to /home/miner/latency.log) ──────
179
+ import logging as _lat_logging
180
+ _lat_logger = _lat_logging.getLogger("sv_latency")
181
+ _lat_logger.setLevel(_lat_logging.INFO)
182
+ _lat_logger.propagate = False
183
+ if not _lat_logger.handlers:
184
+ _lat_fh = _lat_logging.FileHandler("/home/miner/latency.log")
185
+ _lat_fh.setFormatter(_lat_logging.Formatter(
186
+ "%(asctime)s.%(msecs)03d %(message)s", datefmt="%Y-%m-%d %H:%M:%S"))
187
+ _lat_logger.addHandler(_lat_fh)
188
 
189
  logger = logging.getLogger(__name__)
190
 
 
275
  PER_NMS_IOU = 0.50 # NMS IoU for merging across passes (max-conf wins)
276
  PER_MAX_DET = 15 # hard cap on person detections per image
277
 
278
+ # ── Frame quality gating (Laplacian variance) ───────────────────────────────
279
+ PER_BLUR_THRESHOLD = 50.0 # Laplacian variance below this = severely blurry
280
+ PER_BLUR_CONF_PENALTY = 0.85 # multiply confs by this for blurry frames (reduce FP)
281
+
282
+ # ── Adaptive CLAHE config ───────────────────────────────────────────────────
283
+ PER_CLAHE_CLIP = 2.0 # mild CLAHE (was 12.0, too aggressive)
284
+ PER_CLAHE_CONTRAST_THRESH = 40.0 # only apply CLAHE when L-channel std < this
285
+
286
  # ── Pose FP filter + box refinement config ──────────────────────────────────
287
  POSE_CONF_THRESH = 0.25 # Minimum confidence for pose detection
288
  POSE_NMS_IOU = 0.65 # NMS IoU threshold for pose detections
 
1214
  return inp, ratio, pl, pt
1215
 
1216
  def _per_enhance(self, img_bgr):
1217
+ """Adaptive CLAHE: only apply to low-contrast frames, mild clip=2.0."""
1218
  lab = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2LAB)
1219
  l, a, b = cv2.split(lab)
1220
+ if float(l.std()) < PER_CLAHE_CONTRAST_THRESH:
1221
+ clahe = cv2.createCLAHE(clipLimit=PER_CLAHE_CLIP, tileGridSize=(8, 8))
1222
+ l = clahe.apply(l)
1223
+ return cv2.cvtColor(cv2.merge([l, a, b]), cv2.COLOR_LAB2BGR)
1224
+ return img_bgr # skip CLAHE on normal-contrast images
1225
+
1226
+ @staticmethod
1227
+ def _frame_blur_score(img_bgr):
1228
+ """Laplacian variance blur metric. Lower = blurrier."""
1229
+ gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY)
1230
+ return cv2.Laplacian(gray, cv2.CV_64F).var()
1231
 
1232
  def _per_decode(self, raw, ratio, pl, pt, oh, ow, conf_thresh):
1233
  pred = raw[0]
 
1742
  oh, ow = image_bgr.shape[:2]
1743
  t_start = time.monotonic()
1744
 
1745
+ # Frame quality gating β€” detect severely blurry frames
1746
+ blur_score = self._frame_blur_score(image_bgr)
1747
+ is_blurry = blur_score < PER_BLUR_THRESHOLD
1748
+
1749
  # Collect all boxes in original pixel coords
1750
  all_boxes = [] # list of [N, 4] arrays
1751
  all_confs = [] # list of [N] arrays
 
1786
  if len(merged_b) == 0:
1787
  return []
1788
 
1789
+ # Blur confidence penalty β€” reduce FP on severely blurry frames
1790
+ if is_blurry:
1791
+ merged_s = merged_s * PER_BLUR_CONF_PENALTY
1792
+
1793
  # Sanity filters
1794
  img_area = float(oh * ow)
1795
  out = []
 
1821
 
1822
  return out
1823
 
1824
+ # ── Element detection (stack frame inspection) ──────────────────────────
1825
+ _CHALLENGE_TYPE_MAP = {2: 'person', 12: 'vehicle'}
1826
+
1827
+ def _detect_element_hint(self) -> str:
1828
+ """Detect whether this request is for person or vehicle.
1829
+
1830
+ Reads challenge_type_id from the chute template predict() metadata
1831
+ via stack frame inspection. Returns 'person', 'vehicle', or 'both'.
1832
+ """
1833
+ frame = None
1834
+ try:
1835
+ frame = inspect.currentframe()
1836
+ for _ in range(10):
1837
+ frame = frame.f_back
1838
+ if frame is None:
1839
+ break
1840
+ meta = frame.f_locals.get('metadata')
1841
+ if isinstance(meta, dict) and 'challenge_type_id' in meta:
1842
+ ct_id = meta['challenge_type_id']
1843
+ return self._CHALLENGE_TYPE_MAP.get(ct_id, 'both')
1844
+ except Exception:
1845
+ pass
1846
+ finally:
1847
+ del frame
1848
+ return 'both'
1849
+
1850
  # ── Unified inference ──────────────────────────────────���────────────────
1851
 
1852
+ def _infer_single(self, image_bgr: ndarray, element_hint: str = 'both') -> list[BoundingBox]:
1853
  self._cached_pose_data = None # reset before each frame
1854
+
1855
+ if element_hint == 'person':
1856
+ return self._infer_person(image_bgr)
1857
+
1858
+ if element_hint == 'vehicle':
1859
+ vehicle_boxes = self._infer_vehicle(image_bgr)
1860
+ vehicle_boxes = self._vehicle_parts_confirm(vehicle_boxes, [], image_bgr)
1861
+ return vehicle_boxes
1862
+
1863
+ # Fallback: run both (original behavior)
1864
  if ENABLE_PARALLEL:
1865
  veh_future = self._executor.submit(self._infer_vehicle, image_bgr)
1866
  per_future = self._executor.submit(self._infer_person, image_bgr)
 
1928
  ) -> list[TVFrameResult]:
1929
  t_start = time.perf_counter()
1930
 
1931
+ # Detect element type from caller metadata
1932
+ element_hint = self._detect_element_hint()
1933
+ t_setup = time.perf_counter()
1934
+ dt_setup = (t_setup - t_start) * 1000
1935
+
1936
+ _lat_logger.info(
1937
+ "REQUEST batch=%d hint=%s setup=%.1fms",
1938
+ len(batch_images), element_hint, dt_setup,
1939
+ )
1940
+
1941
  results: list[TVFrameResult] = []
1942
  for idx, image in enumerate(batch_images):
1943
  t_img = time.perf_counter()
1944
+ boxes = self._infer_single(image, element_hint=element_hint)
1945
+ t_post = time.perf_counter()
1946
+ dt_infer = (t_post - t_img) * 1000
1947
+
1948
  keypoints = [(0, 0) for _ in range(max(0, int(n_keypoints)))]
1949
  results.append(TVFrameResult(
1950
  frame_id=offset + idx, boxes=boxes, keypoints=keypoints,
1951
  ))
1952
+ dt_post = (time.perf_counter() - t_post) * 1000
1953
+
1954
+ if idx < 3 or idx == len(batch_images) - 1:
1955
+ _lat_logger.info(
1956
+ " IMG %d/%d boxes=%d infer=%.1fms post=%.1fms shape=%s",
1957
+ idx, len(batch_images), len(boxes), dt_infer, dt_post,
1958
+ image.shape,
1959
+ )
1960
+
1961
+ t_done = time.perf_counter()
1962
+ dt_total = (t_done - t_start) * 1000
1963
+ total_boxes = sum(len(r.boxes) for r in results)
1964
+
1965
+ _lat_logger.info(
1966
+ "DONE batch=%d boxes=%d total=%.1fms setup=%.1fms hint=%s",
1967
+ len(batch_images), total_boxes, dt_total, dt_setup, element_hint,
1968
+ )
1969
  logger.info(f"[miner] predict_batch: {len(batch_images)} images, "
1970
+ f"{total_boxes} total boxes, {dt_total:.0f}ms (hint={element_hint})")
1971
 
1972
  threading.Thread(
1973
  target=self._replay_save,
 
1976
  ).start()
1977
 
1978
  return results
1979
+ # Miner v3.18 β€” element detection + per-step timing β€” background TRT engine build + CUDA-first fallback 20260402