Japanese BBQ Sauce

This commit is contained in:
firestar5683
2026-07-20 11:58:21 -05:00
parent 11044975c5
commit f265aa3f7b
14 changed files with 270 additions and 29 deletions
@@ -15,8 +15,15 @@ import cv2
from starpilot.system.speed_limit_vision import DETECTOR_CLASSIFIER_EXPANSIONS
if __package__ in (None, ""):
import sys
sys.path.insert(0, str(Path(__file__).resolve().parent))
from common import SUPPORTED_SPEED_VALUES # type: ignore # noqa: TID251
else:
from .common import SUPPORTED_SPEED_VALUES
SPEED_VALUES = frozenset((15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75))
SPEED_VALUES = frozenset(SUPPORTED_SPEED_VALUES)
def parse_args() -> argparse.Namespace:
+5
View File
@@ -23,6 +23,11 @@ DETECTOR_CLASS_NAMES = (
"school_zone_speed_limit",
)
DEFAULT_SPEED_VALUES = (15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75)
# Values the review and dataset tooling can accept. Keep DEFAULT_SPEED_VALUES
# aligned with the currently deployed classifier until an expanded model is
# promoted; adding a class changes every output index after it.
SUPPORTED_SPEED_VALUES = (10, *DEFAULT_SPEED_VALUES, 80, 90, 100)
EXTENDED_CLASSIFIER_SPEED_VALUES = tuple(sorted(SUPPORTED_SPEED_VALUES, key=str))
DETECTOR_EXPORT_NAME = "speed_limit_us_detector.onnx"
CLASSIFIER_EXPORT_NAME = "speed_limit_us_value_classifier.onnx"
@@ -2,7 +2,6 @@
from __future__ import annotations
import argparse
import math
import random
from dataclasses import dataclass
@@ -16,7 +15,7 @@ from PIL import Image, ImageDraw, ImageEnhance, ImageFilter, ImageFont
if __package__ in (None, ""):
import sys
sys.path.insert(0, str(Path(__file__).resolve().parent))
from common import DEFAULT_SPEED_VALUES, DEFAULT_WORKSPACE, ensure_dir, resolve_workspace # type: ignore
from common import DEFAULT_SPEED_VALUES, DEFAULT_WORKSPACE, ensure_dir, resolve_workspace # type: ignore # noqa: TID251
else:
from .common import DEFAULT_SPEED_VALUES, DEFAULT_WORKSPACE, ensure_dir, resolve_workspace
@@ -100,7 +99,6 @@ def render_regulatory_sign(speed_value: int, school_zone: bool, seed: int) -> Im
def render_advisory_sign(speed_value: int, seed: int) -> Image.Image:
rng = random.Random(seed)
size = rng.randint(240, 320)
image = Image.new("RGBA", (size, size), (255, 255, 255, 0))
base = Image.new("RGBA", (size, size), (255, 255, 255, 0))
draw = ImageDraw.Draw(base)
@@ -234,6 +232,7 @@ def main():
parser.add_argument("--train-count", type=int, default=9000, help="Number of synthetic training detector images.")
parser.add_argument("--val-count", type=int, default=1200, help="Number of synthetic validation detector images.")
parser.add_argument("--negative-ratio", type=float, default=0.18, help="Share of detector images with no sign.")
parser.add_argument("--speed-values", nargs="+", type=int, default=list(DEFAULT_SPEED_VALUES), help="Posted values to synthesize.")
parser.add_argument("--seed", type=int, default=20260330, help="Random seed.")
args = parser.parse_args()
@@ -246,7 +245,7 @@ def main():
detector_image_dir = workspace / "detector" / "images"
detector_label_dir = workspace / "detector" / "labels"
classifier_dir = workspace / "classifier"
speed_values = tuple(DEFAULT_SPEED_VALUES)
speed_values = tuple(dict.fromkeys(args.speed_values))
rng = random.Random(args.seed)
for split, count in (("train", max(args.train_count, 0)), ("val", max(args.val_count, 0))):
@@ -13,7 +13,7 @@ import numpy as np
if __package__ in (None, ""):
import sys
sys.path.insert(0, str(Path(__file__).resolve().parent))
from common import DEFAULT_SPEED_VALUES, DEFAULT_WORKSPACE, ensure_dir, resolve_workspace # type: ignore
from common import DEFAULT_SPEED_VALUES, DEFAULT_WORKSPACE, ensure_dir, resolve_workspace # type: ignore # noqa: TID251
from generate_synthetic_us_speed_limits import KNOWN_REAL_CROPS, augment_sign, render_regulatory_sign # type: ignore
else:
from .common import DEFAULT_SPEED_VALUES, DEFAULT_WORKSPACE, ensure_dir, resolve_workspace
@@ -146,6 +146,7 @@ def main():
parser.add_argument("--train-per-class", type=int, default=1800, help="Synthetic training samples per value.")
parser.add_argument("--val-per-class", type=int, default=260, help="Synthetic validation samples per value.")
parser.add_argument("--real-augmentations", type=int, default=28, help="Augmented mask samples to create per known real crop.")
parser.add_argument("--speed-values", nargs="+", type=int, default=list(DEFAULT_SPEED_VALUES), help="Posted values to synthesize.")
parser.add_argument("--seed", type=int, default=20260330, help="Random seed.")
args = parser.parse_args()
@@ -157,7 +158,7 @@ def main():
ensure_dir(classifier_dir / "val")
rng = random.Random(args.seed)
speed_values = tuple(DEFAULT_SPEED_VALUES)
speed_values = tuple(dict.fromkeys(args.speed_values))
for split, per_class in (("train", max(args.train_per_class, 0)), ("val", max(args.val_per_class, 0))):
for speed_value in speed_values:
@@ -15,11 +15,11 @@ if __package__ in (None, ""):
import sys
sys.path.insert(0, str(Path(__file__).resolve().parent))
from build_value_dataset import crop_box, parse_yolo_labels # type: ignore
from common import DEFAULT_SPEED_VALUES, DEFAULT_WORKSPACE, ensure_dir, resolve_workspace # type: ignore
from common import DEFAULT_WORKSPACE, SUPPORTED_SPEED_VALUES, ensure_dir, resolve_workspace # type: ignore # noqa: TID251
from generate_value_roi_classifier_dataset import augment_mask, extract_value_mask # type: ignore
else:
from .build_value_dataset import crop_box, parse_yolo_labels
from .common import DEFAULT_SPEED_VALUES, DEFAULT_WORKSPACE, ensure_dir, resolve_workspace
from .common import DEFAULT_WORKSPACE, SUPPORTED_SPEED_VALUES, ensure_dir, resolve_workspace
from .generate_value_roi_classifier_dataset import augment_mask, extract_value_mask
@@ -60,7 +60,7 @@ def parse_speed_from_read(text: str) -> int:
if not match:
return 0
value = int(match.group(1))
return value if value in DEFAULT_SPEED_VALUES else 0
return value if value in SUPPORTED_SPEED_VALUES else 0
def row_speed(row: dict[str, str]) -> int:
@@ -68,7 +68,7 @@ def row_speed(row: dict[str, str]) -> int:
text = (row.get(field) or "").strip()
if text.isdigit():
value = int(text)
if value in DEFAULT_SPEED_VALUES:
if value in SUPPORTED_SPEED_VALUES:
return value
for field in ("full_detection", "model_read", "ocr_read"):
value = parse_speed_from_read(row.get(field, ""))
@@ -235,12 +235,12 @@ def main() -> int:
if args.max_rows > 0 and attempted >= args.max_rows:
break
print(
"Imported manifest classifier masks: "
f"attempted={attempted} imported={imported} written={written} "
f"skipped_no_speed={skipped_no_speed} skipped_no_crop={skipped_no_crop} skipped_no_mask={skipped_no_mask} "
f"skipped_write_failed={skipped_write_failed}"
)
print("".join((
"Imported manifest classifier masks: ",
f"attempted={attempted} imported={imported} written={written} ",
f"skipped_no_speed={skipped_no_speed} skipped_no_crop={skipped_no_crop} skipped_no_mask={skipped_no_mask} ",
f"skipped_write_failed={skipped_write_failed}",
)))
return 0
@@ -14,9 +14,9 @@ import cv2
if __package__ in (None, ""):
import sys
sys.path.insert(0, str(Path(__file__).resolve().parent))
from common import DEFAULT_SPEED_VALUES, DEFAULT_WORKSPACE, ensure_dir, resolve_workspace # type: ignore # noqa: TID251
from common import DEFAULT_WORKSPACE, SUPPORTED_SPEED_VALUES, ensure_dir, resolve_workspace # type: ignore # noqa: TID251
else:
from .common import DEFAULT_SPEED_VALUES, DEFAULT_WORKSPACE, ensure_dir, resolve_workspace
from .common import DEFAULT_WORKSPACE, SUPPORTED_SPEED_VALUES, ensure_dir, resolve_workspace
CLASSIFIER_FIELDNAMES = [
@@ -149,7 +149,7 @@ def parse_speed(text: str) -> int:
value = int(float(text))
except ValueError:
return 0
return value if value in DEFAULT_SPEED_VALUES else 0
return value if value in SUPPORTED_SPEED_VALUES else 0
def parse_bbox(text: str) -> tuple[int, int, int, int] | None:
@@ -37,6 +37,7 @@ def parse_args() -> argparse.Namespace:
parser.add_argument("routes", nargs="+", help="Route ids like 'dongle/logid'.")
parser.add_argument("--workspace", type=Path, default=DEFAULT_WORKSPACE, help="Training workspace root.")
parser.add_argument("--clip-root", type=Path, default=preferred_clip_root(), help="Downloaded route clip root.")
parser.add_argument("--output-dir", type=Path, help="Output directory. Defaults to <workspace>/review/connect_route_bookmarks.")
parser.add_argument("--models-dir", type=Path, help="Optional directory containing speed_limit_us_detector.onnx and speed_limit_us_value_classifier.onnx.")
parser.add_argument("--lead-in", type=float, default=7.0, help="Seconds before each bookmark to sample into review sheets.")
parser.add_argument("--sample-every", type=float, default=0.5, help="Seconds between sampled lead-in frames.")
@@ -154,7 +155,7 @@ def main() -> int:
args = parse_args()
workspace = resolve_workspace(args.workspace)
clip_root = args.clip_root.expanduser().resolve()
review_root = ensure_dir(workspace / "review" / "connect_route_bookmarks")
review_root = ensure_dir(args.output_dir.expanduser().resolve()) if args.output_dir else ensure_dir(workspace / "review" / "connect_route_bookmarks")
frame_dir = ensure_dir(review_root / "frames")
crop_dir = ensure_dir(review_root / "crops")
contact_sheet_dir = ensure_dir(review_root / "contact_sheets")
@@ -9,7 +9,7 @@ from collections import Counter, defaultdict, deque
from pathlib import Path
PRIORITY_SPEED_ORDER = (60, 65, 55, 50, 45, 40, 35, 30, 25, 20, 70, 15, 75)
PRIORITY_SPEED_ORDER = (60, 65, 55, 50, 45, 40, 35, 30, 25, 20, 70, 75, 80, 90, 100, 15, 10)
COMPARISON_PRIORITY_BONUS = {
"value_changed": 4.0,
"gained_read": 3.0,
@@ -130,7 +130,7 @@ HTML = r"""<!doctype html>
</aside>
</main>
<script>
const speeds = [15,20,25,30,35,40,45,50,55,60,65,70,75];
const speeds = [10,15,20,25,30,35,40,45,50,55,60,65,70,75,80,90,100];
let rows = [];
let index = 0;
let current = null;
@@ -33,6 +33,16 @@ def test_raw_comma_camera_uses_real_frame_rate():
assert common.source_video_fps(Path("clip.mp4"), 0.0) == 20.0
def test_extended_classifier_order_matches_lexical_dataset_classes():
assert common.SUPPORTED_SPEED_VALUES == (10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 90, 100)
assert common.EXTENDED_CLASSIFIER_SPEED_VALUES == (10, 100, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 90)
@pytest.mark.parametrize("speed", (10, 80, 90, 100))
def test_manual_import_accepts_extended_speed_values(speed):
assert import_queue.parse_speed(str(speed)) == speed
def test_localized_bookmark_source_position_normalizes_previous_segment():
previous = {"segment": "26", "relative_time_s": "-18.950"}
current = {"segment": "26", "relative_time_s": "12.500"}