mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-07-22 17:52:07 +08:00
Japanese BBQ Sauce
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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"}
|
||||
|
||||
Reference in New Issue
Block a user