Buckets:
| """cwm3/baselines_dms/extractors/ov_gaze.py — baseline I1 "ov_gaze": the Intel OpenVINO Open Model Zoo gaze / eye-state pipeline on the driver crop (plan §3, 2026-09-14). | |
| Pipeline (the OMZ gaze_estimation_demo chain, re-implemented in Python on top of openvino.Core, CPU, 4 inference threads; every IR from the registry | |
| after its sha256 check): | |
| driver crop (video_io.driver_crop, +10 %; frame decoded at NATIVE size) | |
| -> face-detection-adas-0001 BGR f32 0..255, cv2.resize INTER_LINEAR, no mean / scale (model.yml has none). fd_mode "reshape" (DEFAULT; the demo's | |
| -fd_reshape option): the network input [1,3,384,672] is reshaped to [1,3,384,w], w = round(384 * W / H) rounded to a | |
| multiple of 32 in [64, 672], so the ~1:2 portrait driver crop is resized (almost) isotropically — one compiled model per | |
| distinct w, cached. fd_mode "default" (the demo's default): anisotropic resize of the crop to 672x384; on the phase-1 | |
| frames this squashes the ~60 px faces ~3.8x and loses 14 % of the frames to conf < 0.5 (fd_input_variants.json). | |
| detection_out [1,1,200,7] = (image_id, label, conf, x0, y0, x1, y1) normalised; keep the highest-confidence box with | |
| conf >= 0.5 (FACE_CONF_MIN); none -> NaN row (fail 3) | |
| -> demo adjustBoundingBox x -= 0.067 w, y -= 0.028 h, w *= 1.15, h *= 1.13, then squared on the larger side (gaze_estimation_demo face_detector.cpp), | |
| clipped to the crop | |
| -> head-pose-estimation-adas-0001 face square -> 60x60 BGR f32 0..255 -> angle_y_fc (yaw), angle_p_fc (pitch), angle_r_fc (roll), degrees | |
| -> facial-landmarks-35-adas-0002 same 60x60 face -> align_fc3 [1,70] = 35 normalised (x, y) pairs; points 0-1 = corners of the eye on the IMAGE-LEFT | |
| (the demo's "left eye"), 2-3 = the image-right eye | |
| -> eye squares side = 1.8 x |corner distance| (demo createEyeBoundingBox), centred on the corner midpoint, min side EYE_MIN_PX, border | |
| replicated when the square leaves the crop; both squares rotated by the head roll about their centre (demo roll align, | |
| cv2.getRotationMatrix2D(center, roll, 1) + BORDER_REPLICATE) | |
| -> open-closed-eye-0001 each eye square -> 32x32 BGR f32 0..255 (mean 127 / scale 255 of model.yml are baked into the converted IR) -> '19' | |
| [1,2,1,1] softmax; CLOSED = index 0 (verified: open-eyed sample portrait gives [~0, 1.0]; on driver frames index 1 | |
| dominates; see README "eye class order") | |
| -> gaze-estimation-adas-0002 left_eye_image / right_eye_image [1,3,60,60] BGR f32 0..255 + head_pose_angles ('138') [1,3] = (yaw, pitch, roll) deg | |
| -> gaze_vector [1,3]; normalised, rotated back by the roll (demo), yaw = 90 + atan2(z, x) deg, pitch = asin(y) deg | |
| (demo gazeVectorToGazeAngles): yaw > 0 = gaze toward the IMAGE RIGHT, pitch > 0 = up. | |
| Channels (C = 11; f16 in the cache): ov_face_conf [0,1] | ov_head_yaw, ov_head_pitch, ov_head_roll deg | ov_gaze_x, ov_gaze_y, ov_gaze_z unit vector | |
| (camera frame after roll-back; z < 0 = toward the camera) | ov_gaze_yaw, ov_gaze_pitch deg | ov_eye_closed_l, ov_eye_closed_r closed probability of the | |
| image-left / image-right eye [0,1]. Roles (summarise vocabulary): see ROLES. A row is valid iff a face was found (conf >= 0.5) and every channel is finite. | |
| No label is read here; the phase-1 `y` column is only used for a descriptive distribution in measurements.json. No openpilot field is read. | |
| Spans: the manifest windows [win_start, win_end] are snapped to the 10 Hz grid and pulled inwards by GRID_EPS_S = 0.02 s (grid_spans, the same helper | |
| as mp_face.py) before run_route: run_route stores spans as float32 and video_io.frames_in_spans floors / ceils them without tolerance, which | |
| otherwise reads one frame AFTER win_end (19 / 45 routes) or BEFORE win_start (19 / 45) — found by the 2026-09-14 verification and fixed; exactly | |
| the frames k0 .. k1 = round(10 win_start) .. round(10 win_end) are decoded now, none later than the requested window end. | |
| python baselines_dms/extractors/ov_gaze.py --self-test [--uid UID] # 1 local TRAIN route, 30 frames, temp dir; + sample-portrait check | |
| python baselines_dms/extractors/ov_gaze.py --phase1 [--workers 3] [--max-minutes 60] [--fd-mode reshape|default] [--force] | |
| # 50 frozen manifest windows -> results/baselines_dms/phase1/ov_gaze/ | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import math | |
| import os | |
| import resource | |
| import sys | |
| import time | |
| from pathlib import Path | |
| import numpy as np | |
| os.environ.setdefault("OMP_NUM_THREADS", "4") | |
| os.environ.setdefault("MKL_NUM_THREADS", "4") | |
| HERE = Path(__file__).resolve().parent | |
| PKG = HERE.parent | |
| CWM3 = PKG.parent | |
| for p in (str(PKG), str(HERE)): | |
| if p not in sys.path: | |
| sys.path.insert(0, p) | |
| import cache_schema as cs # noqa: E402 | |
| import video_io as vio # noqa: E402 | |
| from base import EXIT_DONE, Extractor, run_route, run_routes # noqa: E402 | |
| BASELINE = "ov_gaze" | |
| PHASE1_DIR = cs.RESULTS_ROOT / "phase1" | |
| OUT_DIR = PHASE1_DIR / BASELINE | |
| MANIFEST = PHASE1_DIR / "manifest_v1.csv" | |
| FACE_PACK_DIR = CWM3 / "results" / "targets_face_causal_routes_v2" | |
| N_THREADS = 4 | |
| FACE_CONF_MIN = 0.5 | |
| EYE_SCALE = 1.8 | |
| EYE_MIN_PX = 8 | |
| FACE_MIN_PX = 12 | |
| FD_H, FD_W_DEFAULT, FD_W_STEP, FD_W_MIN = 384, 672, 32, 64 | |
| MODELS = {"fd": ("omz_face_detection_adas_0001", "face-detection-adas-0001.xml"), | |
| "hp": ("omz_head_pose_estimation_adas_0001", "head-pose-estimation-adas-0001.xml"), | |
| "lm": ("omz_facial_landmarks_35_adas_0002", "facial-landmarks-35-adas-0002.xml"), | |
| "oc": ("omz_open_closed_eye_0001", "open-closed-eye-0001.xml"), | |
| "gz": ("omz_gaze_estimation_adas_0002", "gaze-estimation-adas-0002.xml")} | |
| CHANNELS = ["ov_face_conf", "ov_head_yaw", "ov_head_pitch", "ov_head_roll", "ov_gaze_x", "ov_gaze_y", "ov_gaze_z", "ov_gaze_yaw", "ov_gaze_pitch", | |
| "ov_eye_closed_l", "ov_eye_closed_r"] | |
| ROLES = {"ov_face_conf": "conf", "ov_head_yaw": "angle_deg", "ov_head_pitch": "angle_deg", "ov_head_roll": "angle_deg", "ov_gaze_x": "none", "ov_gaze_y": "none", | |
| "ov_gaze_z": "none", "ov_gaze_yaw": "angle_deg", "ov_gaze_pitch": "angle_deg", "ov_eye_closed_l": "eye_closed_prob", "ov_eye_closed_r": "eye_closed_prob"} | |
| UNITS = {"ov_face_conf": "probability [0,1]", "ov_head_yaw": "deg", "ov_head_pitch": "deg", "ov_head_roll": "deg", "ov_gaze_x": "unit-vector component [-1,1]", | |
| "ov_gaze_y": "unit-vector component [-1,1]", "ov_gaze_z": "unit-vector component [-1,1]", "ov_gaze_yaw": "deg", "ov_gaze_pitch": "deg", | |
| "ov_eye_closed_l": "probability [0,1]", "ov_eye_closed_r": "probability [0,1]"} | |
| EYE_CLOSED_INDEX = 0 # open-closed-eye-0001 output '19': [p_closed, p_open] (empirical; see module docstring) | |
| GRID_EPS_S = 0.02 # span ends are snapped to the 10 Hz grid and pulled inwards by this much (see grid_spans) | |
| def grid_spans(spans, hz=cs.HZ, eps=GRID_EPS_S): | |
| """[[start, end]] -> f64[S,2] with both ends snapped to the hz grid and pulled INWARDS by eps: [k0/hz + eps, k1/hz - eps] (same as mp_face.grid_spans). | |
| run_route stores spans as float32 and video_io.frames_in_spans floors / ceils them without tolerance, so a raw end such as 834.2 (f32 834.20001) | |
| would pull in the frame at 834.3 = one step AFTER the requested window end (and 1336.2 -> f32 1336.19995 one step before the start). With | |
| eps = 0.02 s (>> the f32 spacing, 0.002 s at 5 h) floor / ceil land exactly on k0 .. k1; cache_schema.covers (tol 0.051) still accepts the raw | |
| windows against the stored spans.""" | |
| a = np.asarray(spans, np.float64).reshape(-1, 2) | |
| k0, k1 = np.round(a[:, 0] * hz), np.round(a[:, 1] * hz) | |
| return np.stack([k0 / hz + eps, k1 / hz - eps], 1) | |
| def _nchw(img_bgr_u8, wh): | |
| """u8 BGR HxWx3 -> f32 [1,3,h,w] in 0..255 (cv2.resize INTER_LINEAR as the demo's ie_wrapper does; no mean / scale for the Intel models).""" | |
| import cv2 | |
| r = cv2.resize(img_bgr_u8, wh, interpolation=cv2.INTER_LINEAR) if (img_bgr_u8.shape[1], img_bgr_u8.shape[0]) != tuple(wh) else img_bgr_u8 | |
| return np.ascontiguousarray(r.transpose(2, 0, 1)[None]).astype(np.float32) | |
| def adjust_face_box(x0, y0, x1, y1, W, H): | |
| """gaze_estimation_demo face_detector.cpp adjustBoundingBox: loosen (-6.7 % x, -2.8 % y, +15 % w, +13 % h) then square on the larger side; clipped.""" | |
| w, h = x1 - x0, y1 - y0 | |
| x0 -= 0.067 * w | |
| y0 -= 0.028 * h | |
| w *= 1.15 | |
| h *= 1.13 | |
| if w < h: | |
| x0 -= (h - w) / 2 | |
| w = h | |
| else: | |
| y0 -= (w - h) / 2 | |
| h = w | |
| X0, Y0 = int(max(0, round(x0))), int(max(0, round(y0))) | |
| X1, Y1 = int(min(W, round(x0 + w))), int(min(H, round(y0 + h))) | |
| return X0, Y0, X1, Y1 | |
| def eye_square(p1, p2, scale=EYE_SCALE, min_px=EYE_MIN_PX): | |
| """demo createEyeBoundingBox: side = scale x |p1 - p2| (at least min_px), centred on the midpoint -> (x0, y0, side).""" | |
| side = max(int(scale * float(np.hypot(*(p1 - p2)))), int(min_px)) | |
| mid = (p1 + p2) / 2.0 | |
| return int(round(mid[0] - side / 2.0)), int(round(mid[1] - side / 2.0)), side | |
| def crop_pad(img, x0, y0, side): | |
| """img[y0:y0+side, x0:x0+side] with BORDER_REPLICATE padding where the square leaves the image -> (u8[side,side,3], padded: bool).""" | |
| import cv2 | |
| H, W = img.shape[:2] | |
| cx0, cy0, cx1, cy1 = max(0, x0), max(0, y0), min(W, x0 + side), min(H, y0 + side) | |
| if cx1 <= cx0 or cy1 <= cy0: | |
| return None, True | |
| sub = img[cy0:cy1, cx0:cx1] | |
| pads = (cy0 - y0, (y0 + side) - cy1, cx0 - x0, (x0 + side) - cx1) | |
| if any(p > 0 for p in pads): | |
| return cv2.copyMakeBorder(sub, *pads, cv2.BORDER_REPLICATE), True | |
| return sub, False | |
| def rotate_about_center(img, angle_deg): | |
| import cv2 | |
| h, w = img.shape[:2] | |
| M = cv2.getRotationMatrix2D((w / 2.0, h / 2.0), float(angle_deg), 1.0) | |
| return cv2.warpAffine(img, M, (w, h), flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REPLICATE) | |
| def gaze_angles(g): | |
| """demo gazeVectorToGazeAngles: (yaw, pitch) deg from a gaze vector (normalised inside).""" | |
| r = float(np.linalg.norm(g)) | |
| if not np.isfinite(r) or r <= 0: | |
| return float("nan"), float("nan") | |
| yaw = math.degrees(math.pi / 2 + math.atan2(float(g[2]), float(g[0]))) | |
| pitch = math.degrees(math.pi / 2 - math.acos(max(-1.0, min(1.0, float(g[1]) / r)))) | |
| return yaw, pitch | |
| class OVGazeExtractor(Extractor): | |
| baseline = BASELINE | |
| channels = list(CHANNELS) | |
| roles = dict(ROLES) | |
| units = dict(UNITS) | |
| model_ids = [MODELS[k][0] for k in ("fd", "hp", "lm", "oc", "gz")] | |
| needs_crop = True | |
| decode_wh = None # native frames: the eye squares (~30-60 px) need the full resolution | |
| batch = 32 | |
| roll_align = True | |
| fd_mode = "reshape" # "reshape" (demo -fd_reshape; default here) | "default" (demo default anisotropic 672x384) | |
| def describe(self): | |
| d = super().describe() | |
| d.update(fd_mode=self.fd_mode, roll_align=bool(self.roll_align), face_conf_min=FACE_CONF_MIN, eye_scale=EYE_SCALE, eye_min_px=EYE_MIN_PX) | |
| return d | |
| def spec(self): | |
| """{channel: role} in the summarise vocabulary (angle_deg | eye_closed_prob | eye_open_ratio | prob | logit | embedding | conf | none).""" | |
| return dict(self.roles) | |
| def summarise_spec(self): | |
| """the summarise.SPECS-style role dict for this baseline (thresholds are the frozen decisions.md constants held by summarise.py).""" | |
| import summarise as SM | |
| return SM.spec_for(self.baseline, self.channels) | |
| def setup(self, registry_entry=None): | |
| import cv2 | |
| import openvino as ov | |
| import registry as REG | |
| cv2.setNumThreads(N_THREADS) | |
| self.core = ov.Core() | |
| cfg = {"INFERENCE_NUM_THREADS": N_THREADS, "PERFORMANCE_HINT": "LATENCY"} | |
| self.compiled, self.req, self.weight_sha256, entries = {}, {}, {}, {} | |
| for key, (rid, xml) in MODELS.items(): | |
| d = REG.resolve(rid) # asserts the sha256 of every file of the entry | |
| e = REG.get(rid) | |
| entries[rid] = {k: e.get(k) for k in ("id", "name", "version", "sha256", "local_path")} | |
| self.weight_sha256[rid] = e["sha256"] | |
| for f in e.get("files", []): | |
| if f["name"].endswith((".xml", ".bin")): | |
| self.weight_sha256[f"{rid}/{f['name']}"] = f["sha256"] | |
| m = self.core.read_model(str(d / xml)) | |
| self.compiled[key] = self.core.compile_model(m, "CPU", cfg) | |
| self.req[key] = self.compiled[key].create_infer_request() | |
| if key == "fd": | |
| self.fd_xml, self.fd_cfg = str(d / xml), cfg | |
| self.fd_models = {FD_W_DEFAULT: (self.compiled[key], self.req[key])} | |
| self.in_names = {k: [i.get_any_name() for i in c.inputs] for k, c in self.compiled.items()} | |
| self.out_names = {k: [o.get_any_name() for o in c.outputs] for k, c in self.compiled.items()} | |
| assert self.in_names["fd"] == ["data"] and self.out_names["fd"] == ["detection_out"], (self.in_names["fd"], self.out_names["fd"]) | |
| assert set(self.out_names["hp"]) == {"angle_y_fc", "angle_p_fc", "angle_r_fc"}, self.out_names["hp"] | |
| assert self.out_names["lm"] == ["align_fc3"] and self.in_names["oc"] == ["input.1"], (self.out_names["lm"], self.in_names["oc"]) | |
| assert set(self.in_names["gz"]) == {"left_eye_image", "right_eye_image", "138"} and self.out_names["gz"] == ["gaze_vector"], (self.in_names["gz"], self.out_names["gz"]) | |
| self.gz_hp_name = "138" | |
| self.lib_versions = {"openvino": ov.__version__, "opencv-python": cv2.__version__, "numpy": np.__version__, "python": sys.version.split()[0]} | |
| self.registry_entry = registry_entry if registry_entry is not None else entries | |
| self.stats = {"n_crops": 0, "n_face": 0, "n_eye_padded": 0, "n_eye_min_side": 0, "n_face_small": 0, "eye_p_open_sum": 0.0, "eye_p_open_n": 0, "fd_mode": self.fd_mode, "fd_widths": {}} | |
| self.infer_ms = [] # per-crop wall ms of the 5-model chain (measurement only) | |
| self.last_debug = None | |
| return self | |
| # ------------------------------------------------------------------ one crop | |
| def _fd_for(self, W, H): | |
| """(compiled, request, (w, h)) of the face detector for a crop of size W x H: reshaped to the crop aspect (fd_mode reshape) or the stock 672x384.""" | |
| if self.fd_mode != "reshape": | |
| return (*self.fd_models[FD_W_DEFAULT], (FD_W_DEFAULT, FD_H)) | |
| w = int(round(FD_H * W / max(H, 1))) | |
| w = int(min(FD_W_DEFAULT, max(FD_W_MIN, (w // FD_W_STEP) * FD_W_STEP))) | |
| if w not in self.fd_models: | |
| m = self.core.read_model(self.fd_xml) | |
| m.reshape([1, 3, FD_H, w]) | |
| c = self.core.compile_model(m, "CPU", self.fd_cfg) | |
| self.fd_models[w] = (c, c.create_infer_request()) | |
| self.stats["fd_widths"][str(w)] = self.stats["fd_widths"].get(str(w), 0) + 1 | |
| return (*self.fd_models[w], (w, FD_H)) | |
| def _face(self, crop): | |
| H, W = crop.shape[:2] | |
| fd, req, wh = self._fd_for(W, H) | |
| det = req.infer({"data": _nchw(crop, wh)})[fd.output(0)][0, 0] | |
| det = det[(det[:, 0] >= 0) & (det[:, 2] >= FACE_CONF_MIN)] | |
| if len(det) == 0: | |
| return None | |
| b = det[np.argmax(det[:, 2])] | |
| conf = float(b[2]) | |
| x0, y0, x1, y1 = np.clip(b[3:7], 0.0, 1.0) * np.array([W, H, W, H], np.float64) | |
| if x1 - x0 < 2 or y1 - y0 < 2: | |
| return None | |
| return conf, (x0, y0, x1, y1) | |
| def infer_one(self, crop, debug=False): | |
| """u8 BGR driver crop -> f32[C] (NaN row when no face).""" | |
| import cv2 | |
| row = np.full(self.C, np.nan, np.float32) | |
| if crop is None or crop.size == 0 or crop.ndim != 3: | |
| return row | |
| H, W = crop.shape[:2] | |
| f = self._face(crop) | |
| if f is None: | |
| return row | |
| conf, raw_box = f | |
| X0, Y0, X1, Y1 = adjust_face_box(*raw_box, W, H) | |
| if X1 - X0 < FACE_MIN_PX or Y1 - Y0 < FACE_MIN_PX: | |
| self.stats["n_face_small"] += 1 | |
| return row | |
| self.stats["n_face"] += 1 | |
| face = crop[Y0:Y1, X0:X1] | |
| f60 = _nchw(face, (60, 60)) | |
| r = self.req["hp"].infer({"data": f60}) | |
| ang = {o.get_any_name(): float(r[o].ravel()[0]) for o in self.compiled["hp"].outputs} | |
| yaw, pitch, roll = ang["angle_y_fc"], ang["angle_p_fc"], ang["angle_r_fc"] | |
| pts = self.req["lm"].infer({"data": f60})[self.compiled["lm"].output(0)].reshape(35, 2).astype(np.float64) | |
| P = np.stack([X0 + pts[:, 0] * (X1 - X0), Y0 + pts[:, 1] * (Y1 - Y0)], 1) | |
| eyes, eye_boxes = [], [] | |
| for a, b in ((0, 1), (2, 3)): | |
| ex, ey, side = eye_square(P[a], P[b]) | |
| if side == EYE_MIN_PX: | |
| self.stats["n_eye_min_side"] += 1 | |
| e, padded = crop_pad(crop, ex, ey, side) | |
| if e is None: | |
| return row # eye square entirely outside the crop: treat as "nothing found" | |
| self.stats["n_eye_padded"] += int(padded) | |
| if self.roll_align: | |
| e = rotate_about_center(e, roll) | |
| eyes.append(e) | |
| eye_boxes.append((ex, ey, side)) | |
| closed = [] | |
| for e in eyes: | |
| p = self.req["oc"].infer({"input.1": _nchw(e, (32, 32))})[self.compiled["oc"].output(0)].ravel() | |
| closed.append(float(p[EYE_CLOSED_INDEX])) | |
| self.stats["eye_p_open_sum"] += float(p[1 - EYE_CLOSED_INDEX]) | |
| self.stats["eye_p_open_n"] += 1 | |
| g = self.req["gz"].infer({"left_eye_image": _nchw(eyes[0], (60, 60)), "right_eye_image": _nchw(eyes[1], (60, 60)), | |
| self.gz_hp_name: np.array([[yaw, pitch, roll]], np.float32)})[self.compiled["gz"].output(0)].ravel().astype(np.float64) | |
| n = float(np.linalg.norm(g)) | |
| if n > 0 and np.isfinite(n): | |
| g = g / n | |
| if self.roll_align: # demo: undo the eye-image roll rotation on the estimated vector | |
| cs_, sn_ = math.cos(math.radians(roll)), math.sin(math.radians(roll)) | |
| g = np.array([g[0] * cs_ + g[1] * sn_, -g[0] * sn_ + g[1] * cs_, g[2]]) | |
| gyaw, gpitch = gaze_angles(g) | |
| row[:] = [conf, yaw, pitch, roll, g[0], g[1], g[2], gyaw, gpitch, closed[0], closed[1]] | |
| if debug: | |
| self.last_debug = {"face_box": (X0, Y0, X1, Y1), "raw_box": tuple(float(v) for v in raw_box), "eye_boxes": eye_boxes, "landmarks": P, "row": row.copy()} | |
| return row | |
| def infer(self, crops): | |
| out = np.full((len(crops), self.C), np.nan, np.float32) | |
| for i, c in enumerate(crops): | |
| if c is None: | |
| continue | |
| t0 = time.perf_counter() | |
| out[i] = self.infer_one(c) | |
| self.infer_ms.append(1000.0 * (time.perf_counter() - t0)) | |
| self.stats["n_crops"] += 1 | |
| return out | |
| EXTRACTOR = OVGazeExtractor | |
| # ---------------------------------------------------------------------------------------------------------------- helpers (measurements) | |
| def _pq(v): | |
| v = np.asarray([x for x in v if np.isfinite(x)], np.float64) | |
| return {"p50": float(np.percentile(v, 50)), "p95": float(np.percentile(v, 95)), "n": int(len(v))} if len(v) else {"p50": None, "p95": None, "n": 0} | |
| def _rss_mb(): | |
| ru_self = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1024.0 | |
| ru_child = resource.getrusage(resource.RUSAGE_CHILDREN).ru_maxrss / 1024.0 | |
| return {"self_peak_mb": round(ru_self, 1), "children_peak_mb_max_single": round(ru_child, 1)} | |
| def _vram_mb(): | |
| """CPU-only baseline: no CUDA context is created; report the GPU memory used by THIS process as seen by nvidia-smi (expected 0).""" | |
| import subprocess | |
| try: | |
| out = subprocess.run(["nvidia-smi", "--query-compute-apps=pid,used_memory", "--format=csv,noheader,nounits"], capture_output=True, text=True, timeout=10).stdout | |
| mine = [int(l.split(",")[1]) for l in out.splitlines() if l.strip() and int(l.split(",")[0]) == os.getpid()] | |
| return float(sum(mine)) | |
| except Exception: | |
| return 0.0 | |
| def _read_manifest(path=MANIFEST): | |
| import csv | |
| rows = [] | |
| with open(path) as f: | |
| for r in csv.DictReader(f): | |
| rows.append({"idx": int(r["idx"]), "uid": r["uid"], "regime": r["regime"], "y": int(r["y"]), "t_anchor": float(r["t_anchor"]), "row": int(r["row"]), | |
| "win_start": float(r["win_start"]), "win_end": float(r["win_end"]), "dongle": r["dongle"], "video": r["video"]}) | |
| return rows | |
| def _spearman(a, b): | |
| from scipy.stats import spearmanr | |
| a, b = np.asarray(a, np.float64), np.asarray(b, np.float64) | |
| m = np.isfinite(a) & np.isfinite(b) | |
| if m.sum() < 5: | |
| return None, int(m.sum()) | |
| r = spearmanr(a[m], b[m]).correlation | |
| return (None if not np.isfinite(r) else float(r)), int(m.sum()) | |
| def _face_pack_hist(regime, rows_idx): | |
| """face pack rows -> (t_hist f64[n,80], lm_yaw_dev f32[n,80], eye_open f32[n,80], t_anchor f64[n], uid[n]) for the given anchor rows.""" | |
| p = FACE_PACK_DIR / f"facehist_causal_{regime}_train.npz" | |
| with np.load(p, allow_pickle=True) as z: | |
| ch = [str(c) for c in z["channels"]] | |
| j_yaw, j_eye = ch.index("lm_yaw_dev"), ch.index("eye_open") | |
| idx = np.asarray(rows_idx, np.int64) | |
| t = z["t"][idx].astype(np.float64) | |
| Fh = z["Fh8_c"][idx].astype(np.float32) | |
| Fv = z["Fh8valid"][idx].astype(bool) | |
| uid = z["uid"][idx].astype(str) | |
| steps = np.arange(80) - 79 # Fh8_c[:, j] is the value at t + (j - 79) / 10 (t-7.9 .. t) | |
| T = t[:, None] + steps[None, :] / 10.0 | |
| yaw, eye = Fh[:, :, j_yaw], Fh[:, :, j_eye] | |
| yaw[~Fv] = np.nan | |
| eye[~Fv] = np.nan | |
| return T, yaw, eye, t, uid | |
| def _lookup(cache, times, tol=0.02): | |
| """cache rows at the given times (10 Hz grid) -> f32[n, C] (NaN when absent / invalid).""" | |
| k = np.round(cache.t.astype(np.float64) * 10).astype(np.int64) | |
| want = np.round(np.asarray(times, np.float64) * 10).astype(np.int64) | |
| pos = {int(kk): i for i, kk in enumerate(k)} | |
| out = np.full((len(want), cache.C), np.nan, np.float32) | |
| F = cache.feat.astype(np.float32) | |
| for i, w in enumerate(want): | |
| j = pos.get(int(w)) | |
| if j is not None: | |
| out[i] = F[j] | |
| return out | |
| def _decode_frames(uid, frame_idx_list, vpath=None): | |
| """seek + retrieve a handful of native frames (contact sheet only); -> {frame_idx: u8 BGR}.""" | |
| import cv2 | |
| vp = Path(vpath) if vpath else vio.video_path(uid) | |
| cap = cv2.VideoCapture(str(vp)) | |
| out = {} | |
| for fi in sorted(set(int(f) for f in frame_idx_list)): | |
| cap.set(cv2.CAP_PROP_POS_FRAMES, fi) | |
| ok, fr = cap.read() | |
| if ok: | |
| out[fi] = fr | |
| cap.release() | |
| return out | |
| def contact_sheet(ex, picks, out_png, tile=(300, 300)): | |
| """picks: list of dict(uid, frame_idx, t, box(native), yaw, gyaw, tag) -> PNG grid (3 x 4) of driver crops with the face / eye boxes, the gaze arrow and the yaw printed.""" | |
| import cv2 | |
| tiles = [] | |
| by_uid = {} | |
| for p in picks: | |
| by_uid.setdefault(p["uid"], []).append(p["frame_idx"]) | |
| frames = {u: _decode_frames(u, fis) for u, fis in by_uid.items()} | |
| for p in picks: | |
| fr = frames[p["uid"]].get(p["frame_idx"]) | |
| crop = vio.driver_crop(fr, np.asarray(p["box"], np.float32)) if fr is not None else None | |
| canvas = np.zeros((tile[1], tile[0], 3), np.uint8) | |
| if crop is not None: | |
| crop = np.ascontiguousarray(crop) | |
| row = ex.infer_one(crop, debug=True) | |
| dbg = ex.last_debug if np.isfinite(row).all() else None | |
| vis = crop.copy() | |
| if dbg is not None: | |
| X0, Y0, X1, Y1 = dbg["face_box"] | |
| cv2.rectangle(vis, (X0, Y0), (X1, Y1), (0, 255, 0), 2) | |
| for (ex_, ey_, side) in dbg["eye_boxes"]: | |
| cv2.rectangle(vis, (ex_, ey_), (ex_ + side, ey_ + side), (255, 200, 0), 2) | |
| P = dbg["landmarks"] | |
| mid = ((P[0] + P[1]) / 2 + (P[2] + P[3]) / 2) / 2 | |
| g = row[4:7] | |
| L = 0.6 * (X1 - X0) | |
| cv2.arrowedLine(vis, (int(mid[0]), int(mid[1])), (int(mid[0] + g[0] * L), int(mid[1] - g[1] * L)), (0, 0, 255), 3, tipLength=0.2) | |
| s = min(tile[0] / vis.shape[1], tile[1] / vis.shape[0]) | |
| small = cv2.resize(vis, (max(1, int(vis.shape[1] * s)), max(1, int(vis.shape[0] * s))), interpolation=cv2.INTER_AREA) | |
| canvas[:small.shape[0], :small.shape[1]] = small | |
| yaw_now = float(row[1]) if np.isfinite(row[1]) else float("nan") | |
| gyaw_now = float(row[7]) if np.isfinite(row[7]) else float("nan") | |
| else: | |
| yaw_now = gyaw_now = float("nan") | |
| txt1 = f"{p['tag']} head_yaw {yaw_now:+.1f} (cache {p['yaw']:+.1f})" | |
| txt2 = f"gaze_yaw {gyaw_now:+.1f} t={p['t']:.1f}s" | |
| for k, txt in enumerate((txt1, txt2, p["uid"][:34])): | |
| cv2.putText(canvas, txt, (4, tile[1] - 40 + 16 * k), cv2.FONT_HERSHEY_SIMPLEX, 0.42, (0, 0, 0), 3, cv2.LINE_AA) | |
| cv2.putText(canvas, txt, (4, tile[1] - 40 + 16 * k), cv2.FONT_HERSHEY_SIMPLEX, 0.42, (255, 255, 255), 1, cv2.LINE_AA) | |
| tiles.append(canvas) | |
| while len(tiles) % 4: | |
| tiles.append(np.zeros((tile[1], tile[0], 3), np.uint8)) | |
| rows = [np.hstack(tiles[i:i + 4]) for i in range(0, len(tiles), 4)] | |
| sheet = np.vstack(rows) | |
| header = np.full((28, sheet.shape[1], 3), 255, np.uint8) | |
| cv2.putText(header, "ov_gaze sign convention: top row = 6 most NEGATIVE ov_head_yaw, bottom rows = 6 most POSITIVE (green face box, cyan eye squares, red gaze arrow = (gaze_x, -gaze_y))", | |
| (6, 19), cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 0, 0), 1, cv2.LINE_AA) | |
| sheet = np.vstack([header, sheet]) | |
| Path(out_png).parent.mkdir(parents=True, exist_ok=True) | |
| cv2.imwrite(str(out_png), sheet) | |
| return out_png | |
| # ---------------------------------------------------------------------------------------------------------------- CLI modes | |
| def make_extractor(fd_mode="reshape"): | |
| ex = OVGazeExtractor() | |
| ex.fd_mode = fd_mode | |
| return ex.setup(None) | |
| def self_test(uid=None, workers=2, fd_mode="reshape"): | |
| import tempfile | |
| import cv2 | |
| t0 = time.time() | |
| ex = make_extractor(fd_mode) | |
| rep = {"self_test": "extractors.ov_gaze", "describe": ex.describe(), "roles": ex.spec()} | |
| # (1) the sample portrait: exactly one face, eyes open, gaze near the camera | |
| im = cv2.imread(str(PKG / "weights" / "_samples" / "portrait.jpg")) | |
| row = ex.infer_one(im, debug=True) | |
| assert np.isfinite(row).all() and row[0] >= 0.9, row | |
| assert row[9] < 0.5 and row[10] < 0.5, ("sample portrait eyes must classify as open", row[9:11]) | |
| rep["portrait"] = {c: round(float(v), 4) for c, v in zip(ex.channels, row)} | |
| # (2) an empty / no-face crop -> NaN row | |
| assert np.isnan(ex.infer_one(np.zeros((200, 200, 3), np.uint8))).all() | |
| assert np.isnan(ex.infer([None])).all() | |
| # (3) one local TRAIN route, 30 frames through run_route | |
| if uid is None: | |
| cands = [(vio.video_path(u).stat().st_size, u) for u in vio.anchors_by_uid(("manual_train", "engaged_train")) if vio.has_video(u) and vio.pose_path(u) is not None] | |
| if not cands: | |
| rep["route"] = "none local; skipped" | |
| print(json.dumps(rep, indent=1, default=str)) | |
| return rep | |
| uid = min(cands)[1] | |
| ts = vio.anchors_of(uid) | |
| W, H, n = vio.video_info(vio.video_path(uid)) | |
| t_a = float(ts[len(ts) // 2]) if len(ts) else min(30.0, n / vio.FPS - 3.1) | |
| span = grid_spans([[max(0.0, t_a - 2.9), t_a]]) # exactly 30 frames at 10 Hz (grid-snapped; none after t_a) | |
| ex.infer_ms.clear() | |
| with tempfile.TemporaryDirectory() as d: | |
| code, path, info = run_route(ex, uid, span, d, max_minutes=10, workers=workers, block=15) | |
| rc = cs.RouteCache.load(path) | |
| assert code == EXIT_DONE and rc.n == 30 and rc.C == ex.C and rc.channels == ex.channels, (code, rc.n, rc.C) | |
| code2, _, info2 = run_route(ex, uid, span, d, max_minutes=10, workers=workers) | |
| assert code2 == EXIT_DONE and info2["status"] == "exists" | |
| F = rc.feat.astype(np.float32) | |
| rep["route"] = {"uid": uid, "native_wh": [W, H], "span": span.tolist(), "n_frames": rc.n, "fail_hist": info["fail_hist"], "valid_rate": info["valid_rate"], | |
| "runtime": rc.runtime, "decode_worker_fps": info["decode_worker_fps"], "wall_s": info["wall_s"], "rerun": info2["status"], | |
| "infer_ms_per_crop": _pq(ex.infer_ms), "stats": dict(ex.stats), | |
| "head_yaw_valid": [round(float(v), 1) for v in F[rc.valid, 1][:10]], | |
| "eye_p_open_mean": (ex.stats["eye_p_open_sum"] / ex.stats["eye_p_open_n"] if ex.stats["eye_p_open_n"] else None), | |
| "meta_keys": sorted(rc.meta), "summarise_spec": ex.summarise_spec()} | |
| rep["wall_s"] = round(time.time() - t0, 1) | |
| rep["rss"] = _rss_mb() | |
| print(json.dumps(rep, indent=1, default=str)) | |
| return rep | |
| def phase1(workers=3, max_minutes=90.0, out_dir=OUT_DIR, manifest=MANIFEST, force=False, fd_mode="reshape"): | |
| import cv2 | |
| t_start = time.time() | |
| rows = _read_manifest(manifest) | |
| assert len(rows) == 50, len(rows) | |
| uids = sorted({r["uid"] for r in rows}) | |
| spans = {u: cs.merge_spans(grid_spans([[r["win_start"], r["win_end"]] for r in rows if r["uid"] == u])) for u in uids} # exactly frames k0..k1, none after win_end | |
| ex = make_extractor(fd_mode) | |
| cache_dir = Path(out_dir) / "cache" | |
| cache_dir.mkdir(parents=True, exist_ok=True) | |
| log_lines = [] | |
| def log(m): | |
| print(m, flush=True) | |
| log_lines.append(m) | |
| vram_before = _vram_mb() | |
| pre_exist = {u for u in uids if (cache_dir / f"{u}.npz").exists()} if not force else set() | |
| code, summ = run_routes(ex, uids, spans, cache_dir, max_minutes=max_minutes, workers=workers, log=log, block=40, force=force) | |
| wall_run = time.time() - t_start | |
| vram_after = _vram_mb() | |
| # ------------------------------------------------------------------ measurements over the caches | |
| caches = {u: cs.RouteCache.load(cache_dir / f"{u}.npz") for u in uids if (cache_dir / f"{u}.npz").exists()} | |
| n_frames = int(sum(c.n for c in caches.values())) | |
| n_written = int(sum(c.runtime.get("n_frames", 0) for u, c in caches.items() if u not in pre_exist)) # frames computed by THIS run | |
| fail_hist = {} | |
| fail_all = np.concatenate([c.fail for c in caches.values()]) if caches else np.zeros(0, np.uint8) | |
| for k, v in zip(*np.unique(fail_all, return_counts=True)): | |
| fail_hist[cs.FAIL_CODES[int(k)]] = int(v) | |
| valid_all = np.concatenate([c.valid for c in caches.values()]) if caches else np.zeros(0, bool) | |
| box_all = np.concatenate([np.isfinite(c.box).all(1) for c in caches.values()]) if caches else np.zeros(0, bool) | |
| frame_all = fail_all != 2 | |
| valid_share = float(valid_all.mean()) if n_frames else None | |
| valid_share_box = float(valid_all[box_all & frame_all].mean()) if (box_all & frame_all).any() else None | |
| # decode ms: frame-weighted percentiles of the per-route p50 / p95 (each route's runtime holds per-block per-frame ms percentiles) | |
| dec50 = [c.runtime.get("decode_ms_p50") for c in caches.values()] | |
| dec95 = [c.runtime.get("decode_ms_p95") for c in caches.values()] | |
| wts = [c.n for c in caches.values()] | |
| def wmed(v, w): | |
| v = np.asarray([x if x is not None else np.nan for x in v], np.float64) | |
| w = np.asarray(w, np.float64) | |
| m = np.isfinite(v) | |
| if not m.any(): | |
| return None | |
| o = np.argsort(v[m]) | |
| cw = np.cumsum(w[m][o]) | |
| return float(v[m][o][np.searchsorted(cw, cw[-1] / 2)]) | |
| # ------------------------------------------------------------------ per-anchor values + face-pack correlation | |
| F_all = {} | |
| per_anchor = [] | |
| yaw_pairs, gyaw_pairs, eye_pairs, per_anchor_rho = [], [], [], [] | |
| yaw_dev_pairs = [] | |
| for regime in ("manual", "engaged"): | |
| rr = [r for r in rows if r["regime"] == regime] | |
| T, lm_yaw_dev, eye_open, t_pack, uid_pack = _face_pack_hist(regime, [r["row"] for r in rr]) | |
| for i, r in enumerate(rr): | |
| assert abs(t_pack[i] - r["t_anchor"]) < 0.06 and uid_pack[i] == r["uid"], (r["idx"], t_pack[i], r["t_anchor"], uid_pack[i]) | |
| c = caches.get(r["uid"]) | |
| if c is None: | |
| per_anchor.append({"idx": r["idx"], "uid": r["uid"], "regime": regime, "y": r["y"], "t_anchor": r["t_anchor"], "cache": "missing"}) | |
| continue | |
| Fh = _lookup(c, T[i]) # [80, C] over the pack's history grid (t-7.9 .. t) | |
| at = _lookup(c, [r["t_anchor"]])[0] | |
| m3 = slice(50, 80) # last 3 s | |
| win_valid = np.isfinite(Fh[:, 1]) | |
| yaw_pairs.append((Fh[:, 1], lm_yaw_dev[i])) | |
| gyaw_pairs.append((Fh[:, 7], lm_yaw_dev[i])) | |
| eye_pairs.append((np.nanmean(Fh[:, 9:11], 1) if win_valid.any() else Fh[:, 9], eye_open[i])) | |
| yaw_dev_pairs.append((Fh[:, 1] - np.nanmedian(Fh[:, 1]) if win_valid.any() else Fh[:, 1], lm_yaw_dev[i] - np.nanmedian(lm_yaw_dev[i]))) | |
| rho_i, n_i = _spearman(Fh[:, 1], lm_yaw_dev[i]) | |
| per_anchor_rho.append(rho_i) | |
| rec = {"idx": r["idx"], "uid": r["uid"], "regime": regime, "y": r["y"], "t_anchor": r["t_anchor"], "valid_at_anchor": bool(np.isfinite(at).all()), | |
| "valid_share_8s": float(win_valid.mean()), "rho_head_yaw_vs_lm_yaw_dev_8s": rho_i, "n_pairs_8s": n_i, | |
| "abs_head_yaw_max_3s": (float(np.nanmax(np.abs(Fh[m3, 1]))) if np.isfinite(Fh[m3, 1]).any() else None), | |
| "share_abs_head_yaw_gt20_3s": (float(np.nanmean(np.abs(Fh[m3, 1]) > 20)) if np.isfinite(Fh[m3, 1]).any() else None)} | |
| for j, ch in enumerate(ex.channels): | |
| rec[f"{ch}_at_anchor"] = (float(at[j]) if np.isfinite(at[j]) else None) | |
| per_anchor.append(rec) | |
| def pooled(pairs): | |
| a = np.concatenate([p[0] for p in pairs]) if pairs else np.zeros(0) | |
| b = np.concatenate([p[1] for p in pairs]) if pairs else np.zeros(0) | |
| return _spearman(a, b) | |
| rho_yaw, n_yaw = pooled(yaw_pairs) | |
| rho_gyaw, n_gyaw = pooled(gyaw_pairs) | |
| rho_eye, n_eye = pooled(eye_pairs) | |
| rho_yaw_dev, n_yaw_dev = pooled(yaw_dev_pairs) | |
| rhos = [r for r in per_anchor_rho if r is not None] | |
| # descriptive score distribution by y (no tuning; y is not used anywhere else) | |
| desc = {} | |
| for y in (0, 1): | |
| v = [a["abs_head_yaw_max_3s"] for a in per_anchor if a.get("y") == y and a.get("abs_head_yaw_max_3s") is not None] | |
| s = [a["share_abs_head_yaw_gt20_3s"] for a in per_anchor if a.get("y") == y and a.get("share_abs_head_yaw_gt20_3s") is not None] | |
| desc[f"y{y}"] = {"n": len(v), "abs_head_yaw_max_3s_median": (float(np.median(v)) if v else None), "abs_head_yaw_max_3s_mean": (float(np.mean(v)) if v else None), | |
| "share_abs_head_yaw_gt20_3s_mean": (float(np.mean(s)) if s else None)} | |
| # ------------------------------------------------------------------ contact sheet: 6 most negative / 6 most positive head yaw (distinct routes, conf >= 0.9) | |
| cands = [] | |
| for u, c in caches.items(): | |
| F = c.feat.astype(np.float32) | |
| ok = c.valid & (F[:, 0] >= 0.9) | |
| for j in np.flatnonzero(ok): | |
| cands.append((float(F[j, 1]), u, int(c.frame_idx[j]), float(c.t[j]), c.box[j].tolist(), float(F[j, 7]))) | |
| cands.sort(key=lambda z: z[0]) | |
| def pick(seq, k): | |
| out, seen = [], set() | |
| for z in seq: | |
| if z[1] in seen: | |
| continue | |
| seen.add(z[1]) | |
| out.append(z) | |
| if len(out) == k: | |
| break | |
| return out | |
| neg = pick(cands, 6) | |
| pos = pick(cands[::-1], 6) | |
| picks = [{"uid": u, "frame_idx": fi, "t": t, "box": b, "yaw": yw, "gyaw": g, "tag": "NEG"} for (yw, u, fi, t, b, g) in neg] + \ | |
| [{"uid": u, "frame_idx": fi, "t": t, "box": b, "yaw": yw, "gyaw": g, "tag": "POS"} for (yw, u, fi, t, b, g) in pos[::-1]] | |
| sheet = Path(out_dir) / "sign_convention_contact_sheet.png" | |
| if picks: | |
| contact_sheet(ex, picks, sheet) | |
| wall_total = time.time() - t_start | |
| meas = { | |
| "baseline": BASELINE, "written_utc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), "manifest": str(manifest), "n_anchors": len(rows), "n_routes": len(uids), | |
| "n_routes_cached": len(caches), "run_summary": summ, "exit_code": int(code), "workers": int(workers), "decode_wh": "native", "extractor": ex.describe(), "channels": ex.channels, "roles": ex.spec(), | |
| "units": ex.units, "model_ids": ex.model_ids, "weight_sha256": ex.weight_sha256, "lib_versions": ex.lib_versions, | |
| "n_frames": n_frames, "n_frames_written_this_run": n_written, "n_routes_written_this_run": len([u for u in caches if u not in pre_exist]), | |
| "valid_share": valid_share, "valid_share_with_box": valid_share_box, "fail_hist": fail_hist, | |
| "frames_with_box": int((box_all & frame_all).sum()), "frames_no_frame": int((~frame_all).sum()), | |
| "decode_ms_per_frame": {"p50_frame_weighted_median_of_route_p50": wmed(dec50, wts), "p95_frame_weighted_median_of_route_p95": wmed(dec95, wts), | |
| "route_p50_list": [round(v, 2) if v is not None else None for v in dec50]}, | |
| "infer_ms_per_frame": _pq(ex.infer_ms), "infer_ms_note": "wall ms of the 5-model chain per crop (face detector + head pose + landmarks + 2 x eye state + gaze), CPU 4 threads; only frames with a crop", | |
| "wall_s_run_routes": round(wall_run, 1), "wall_s_total_incl_measurements": round(wall_total, 1), | |
| "fps_end_to_end": (round(n_written / wall_run, 2) if wall_run > 0 and n_written else None), | |
| "fps_note": "frames written this run / wall seconds of run_routes (decode + crop + infer + cache write; None when every cache already existed)", | |
| "peak_rss_mb": _rss_mb(), "vram_mb_peak": max(vram_before, vram_after), "vram_note": "CPU-only OpenVINO; no CUDA context; nvidia-smi compute-apps for this pid", | |
| "extractor_stats": dict(ex.stats), "eye_class_order": {"closed_index": EYE_CLOSED_INDEX, "mean_p_open_over_eyes": (ex.stats["eye_p_open_sum"] / ex.stats["eye_p_open_n"] if ex.stats["eye_p_open_n"] else None), | |
| "rho_mean_eye_closed_vs_facepack_eye_open": rho_eye, "n_pairs": n_eye}, | |
| "spearman_vs_face_pack": {"rho_head_yaw_vs_lm_yaw_dev_pooled": rho_yaw, "n_pairs": n_yaw, "rho_gaze_yaw_vs_lm_yaw_dev_pooled": rho_gyaw, "n_pairs_gaze": n_gyaw, | |
| "rho_head_yaw_minus_window_median_vs_lm_yaw_dev_minus_window_median_pooled": rho_yaw_dev, "n_pairs_dev": n_yaw_dev, | |
| "per_anchor_rho_head_yaw_median": (float(np.median(rhos)) if rhos else None), "per_anchor_rho_head_yaw_iqr": ([float(np.percentile(rhos, 25)), float(np.percentile(rhos, 75))] if rhos else None), | |
| "n_anchors_with_rho": len(rhos), "face_pack": str(FACE_PACK_DIR), "alignment": "Fh8_c[:, j] at t_anchor + (j - 79) / 10 s; cache rows matched on the 10 Hz grid"}, | |
| "sign_convention": {"lm_yaw_def": "(nose_x - eye_mid_x) / IOD: > 0 = nose displaced to the IMAGE RIGHT = head turned toward the image right", | |
| "ov_head_yaw": ("same sign as lm_yaw_dev (> 0 = head turned toward the IMAGE RIGHT)" if (rho_yaw or 0) > 0 else "OPPOSITE sign to lm_yaw_dev (> 0 = head turned toward the image left)") if rho_yaw is not None else "undetermined", | |
| "ov_gaze_yaw": "demo gazeVectorToGazeAngles: 90 + atan2(z, x) deg; > 0 = gaze toward the IMAGE RIGHT; ov_gaze_pitch = asin(y) deg, > 0 = up", | |
| "contact_sheet": str(sheet), "contact_sheet_picks": picks}, | |
| "descriptive_by_y": desc, "per_anchor": per_anchor, "log": log_lines[-60:], | |
| } | |
| Path(out_dir).mkdir(parents=True, exist_ok=True) | |
| (Path(out_dir) / "measurements.json").write_text(json.dumps(meas, indent=1, default=str)) | |
| (Path(out_dir) / "run_log.txt").write_text("\n".join(log_lines)) | |
| print(json.dumps({k: meas[k] for k in ("n_frames", "valid_share", "valid_share_with_box", "fail_hist", "infer_ms_per_frame", "fps_end_to_end", "wall_s_run_routes", "peak_rss_mb", | |
| "spearman_vs_face_pack", "eye_class_order", "exit_code")}, indent=1, default=str)) | |
| return code, meas | |
| def main(): | |
| import argparse | |
| ap = argparse.ArgumentParser(description=__doc__.splitlines()[0]) | |
| ap.add_argument("--self-test", action="store_true") | |
| ap.add_argument("--phase1", action="store_true") | |
| ap.add_argument("--uid", default="") | |
| ap.add_argument("--workers", type=int, default=3) | |
| ap.add_argument("--max-minutes", type=float, default=90.0) | |
| ap.add_argument("--out-dir", default=str(OUT_DIR)) | |
| ap.add_argument("--manifest", default=str(MANIFEST)) | |
| ap.add_argument("--force", action="store_true", help="recompute caches that already exist under --out-dir/cache") | |
| ap.add_argument("--fd-mode", default="reshape", choices=["reshape", "default"], help="face-detector input handling (see module docstring)") | |
| a = ap.parse_args() | |
| if a.phase1: | |
| code, _ = phase1(workers=min(a.workers, 3), max_minutes=a.max_minutes, out_dir=Path(a.out_dir), manifest=Path(a.manifest), force=a.force, fd_mode=a.fd_mode) | |
| return code | |
| self_test(a.uid or None, workers=min(a.workers, 2), fd_mode=a.fd_mode) | |
| return 0 | |
| if __name__ == "__main__": | |
| sys.exit(main()) | |
Xet Storage Details
- Size:
- 42.6 kB
- Xet hash:
- 67b02807e01c6712bacea8052b4d094c88ee26487977f3af81d66643e6dce95a
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.