Autonomous Vehicle Traffic Scenarios

View as Markdown
1# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2# SPDX-License-Identifier: Apache-2.0
3# /// script
4# requires-python = ">=3.10"
5# dependencies = [
6# "data-designer",
7# ]
8# ///
9"""Autonomous Vehicle Traffic Scenario Image Generation Recipe
10
11Generate synthetic autonomous-vehicle ego-camera images with controlled
12variation over region, road type, weather, time of day, traffic density,
13surface condition, traffic controls, and long-tail scenario elements. Use the
14generated images for perception review sets, visual QA, or
15simulator-validation prompts.
16
17Synthetic images are not a replacement for real sensor logs, simulator runs, or
18safety validation. They are useful for rapidly creating controlled visual
19examples around rare or hazardous conditions.
20
21Prerequisites:
22 - An image-generation provider key for the selected model. The defaults use
23 OpenRouter, so set OPENROUTER_API_KEY before running.
24
25Run:
26 uv run traffic_scenarios.py --num-records 10
27"""
28
29from __future__ import annotations
30
31import argparse
32from pathlib import Path
33
34import data_designer.config as dd
35from data_designer.interface import DataDesigner, DatasetCreationResults
36
37DEFAULT_MODEL_PROVIDER = "openrouter"
38DEFAULT_MODEL_ID = "google/gemini-3.1-flash-image-preview"
39DEFAULT_MODEL_ALIAS = "traffic-scene-model"
40
41
42def build_model_configs(
43 *,
44 model_provider: str,
45 model_id: str,
46 model_alias: str,
47 image_size: str,
48 aspect_ratio: str,
49 max_parallel_requests: int,
50) -> list[dd.ModelConfig]:
51 """Build a provider-agnostic image-generation model config."""
52 return [
53 dd.ModelConfig(
54 alias=model_alias,
55 model=model_id,
56 provider=model_provider,
57 inference_parameters=dd.ImageInferenceParams(
58 extra_body={
59 "modalities": ["image", "text"],
60 "image_config": {
61 "aspect_ratio": aspect_ratio,
62 "image_size": image_size,
63 },
64 },
65 max_parallel_requests=max_parallel_requests,
66 ),
67 skip_health_check=True,
68 )
69 ]
70
71
72def add_category(config_builder: dd.DataDesignerConfigBuilder, name: str, values: list[str]) -> None:
73 """Add a categorical sampler column."""
74 config_builder.add_column(
75 dd.SamplerColumnConfig(
76 name=name,
77 sampler_type=dd.SamplerType.CATEGORY,
78 params=dd.CategorySamplerParams(values=values),
79 )
80 )
81
82
83def build_config(
84 *,
85 model_provider: str = DEFAULT_MODEL_PROVIDER,
86 model_id: str = DEFAULT_MODEL_ID,
87 model_alias: str = DEFAULT_MODEL_ALIAS,
88 image_size: str = "1K",
89 aspect_ratio: str = "16:9",
90 max_parallel_requests: int = 10,
91) -> dd.DataDesignerConfigBuilder:
92 """Build an autonomous-vehicle ego-camera image-generation pipeline."""
93 model_configs = build_model_configs(
94 model_provider=model_provider,
95 model_id=model_id,
96 model_alias=model_alias,
97 image_size=image_size,
98 aspect_ratio=aspect_ratio,
99 max_parallel_requests=max_parallel_requests,
100 )
101 config_builder = dd.DataDesignerConfigBuilder(model_configs=model_configs)
102
103 add_category(
104 config_builder,
105 "geographic_region",
106 [
107 "US - dense urban (NYC-style)",
108 "US - sprawling suburban (Los Angeles-style)",
109 "US - rural Midwest",
110 "Europe - narrow streets (Italian/French town)",
111 "Europe - orderly infrastructure (German autobahn)",
112 "Asia - mixed traffic (India/Thailand)",
113 "Asia - modern cityscape (Singapore/Tokyo)",
114 ],
115 )
116
117 add_category(
118 config_builder,
119 "road_type",
120 [
121 "urban city street with tall buildings",
122 "urban street with mixed retail/residential",
123 "suburban residential street with trees",
124 "suburban commercial strip with parking lots",
125 "highway - 3 lanes each direction",
126 "highway - 5 lanes each direction with HOV",
127 "rural two-lane country road",
128 "rural highway with sparse markings",
129 "mountain road with curves and guardrails",
130 "coastal road with scenic views",
131 "bridge - suspension or arch style",
132 "bridge - concrete overpass",
133 "tunnel - well-lit with ceiling lights",
134 "tunnel - dim lighting",
135 "parking lot - shopping center",
136 "parking garage - multi-level",
137 "intersection - 4-way with traffic lights",
138 "intersection - complex 6-way",
139 "roundabout - single lane",
140 "roundabout - multi-lane",
141 "construction zone with detour",
142 "school zone with crossing",
143 ],
144 )
145
146 add_category(
147 config_builder,
148 "weather",
149 [
150 "clear sunny day",
151 "partly cloudy",
152 "overcast with gray skies",
153 "light rain - misty windshield",
154 "moderate rain - active wipers",
155 "heavy rain - reduced visibility under 100ft",
156 "light fog - moderate visibility",
157 "dense fog - visibility under 50ft",
158 "light snow - flurries",
159 "moderate snow - accumulating on road",
160 "heavy snow - whiteout conditions",
161 "sleet/freezing rain",
162 "dust storm - desert conditions",
163 "high winds - debris visible",
164 ],
165 )
166
167 add_category(
168 config_builder,
169 "time_of_day",
170 [
171 "dawn - pre-sunrise twilight",
172 "early morning - golden hour light",
173 "mid-morning - bright sun, long shadows",
174 "midday - overhead sun, minimal shadows",
175 "afternoon - sun starting to lower",
176 "late afternoon - golden hour",
177 "dusk - post-sunset twilight",
178 "night - well-lit with streetlights",
179 "night - moderately lit urban",
180 "night - poorly lit rural",
181 "night - headlights only, no street lighting",
182 ],
183 )
184
185 add_category(
186 config_builder,
187 "traffic_density",
188 [
189 "empty - no other vehicles visible",
190 "sparse - 1-2 vehicles in distance",
191 "light - 3-5 vehicles visible",
192 "moderate - steady flow of traffic",
193 "heavy - congested, slow-moving",
194 "stop-and-go - bumper-to-bumper",
195 ],
196 )
197
198 sparse_vehicle_mix = [
199 "sedans and compact cars",
200 "mix of cars and SUVs",
201 "includes large trucks/semi-trailers",
202 "includes buses",
203 "includes motorcycles and scooters",
204 "includes bicycles and e-bikes",
205 "includes delivery vans/box trucks",
206 ]
207 flowing_vehicle_mix = [*sparse_vehicle_mix, "mixed vehicle types - diverse traffic"]
208 config_builder.add_column(
209 dd.SamplerColumnConfig(
210 name="vehicle_mix",
211 sampler_type=dd.SamplerType.SUBCATEGORY,
212 params=dd.SubcategorySamplerParams(
213 category="traffic_density",
214 values={
215 "empty - no other vehicles visible": ["no other vehicles visible"],
216 "sparse - 1-2 vehicles in distance": sparse_vehicle_mix,
217 "light - 3-5 vehicles visible": flowing_vehicle_mix,
218 "moderate - steady flow of traffic": flowing_vehicle_mix,
219 "heavy - congested, slow-moving": flowing_vehicle_mix,
220 "stop-and-go - bumper-to-bumper": flowing_vehicle_mix,
221 },
222 ),
223 )
224 )
225
226 add_category(
227 config_builder,
228 "scenario_element",
229 [
230 "pedestrian crossing at marked crosswalk",
231 "pedestrian jaywalking mid-block",
232 "pedestrian with stroller/wheelchair",
233 "group of pedestrians crossing",
234 "child chasing ball toward street",
235 "jogger/runner on shoulder",
236 "pedestrian wearing dark clothing at night",
237 "pedestrian with umbrella obscuring face in rain",
238 "elderly pedestrian crossing slowly with walker",
239 "pedestrian distracted by phone while crossing",
240 "pedestrians exiting parked bus on roadside",
241 "crowd spilling onto road from sidewalk event",
242 "cyclist in dedicated bike lane",
243 "cyclist merging into traffic lane",
244 "cyclist making left turn",
245 "cyclist riding against traffic on wrong side",
246 "e-scooter rider weaving between cars",
247 "e-scooter rider on sidewalk entering crosswalk",
248 "group of cyclists in paceline on shoulder",
249 "cyclist with cargo trailer taking full lane",
250 "motorcycle lane splitting",
251 "motorcycle filtering through stopped traffic",
252 "motorcycle approaching from blind spot",
253 "school bus with stop sign extended and flashing",
254 "ambulance approaching with lights and sirens",
255 "police vehicle with lights activated",
256 "fire truck in oncoming lane",
257 "emergency vehicle approaching from behind in mirror",
258 "tow truck loading vehicle on roadside",
259 "construction zone - workers present with cones",
260 "construction zone - lane closure with signs",
261 "road crew filling potholes with equipment in lane",
262 "utility workers with cherry picker blocking lane",
263 "temporary steel plates covering road excavation",
264 "stopped vehicle - hazard lights on shoulder",
265 "vehicle broken down in lane",
266 "disabled vehicle in lane with warning triangle",
267 "vehicle stalled in intersection",
268 "vehicle with open hood - person inspecting engine",
269 "vehicle suddenly braking ahead",
270 "vehicle making unexpected lane change without signal",
271 "vehicle backing out of parking spot",
272 "vehicle running red light from cross street",
273 "vehicle driving wrong way on one-way street",
274 "vehicle making illegal U-turn",
275 "vehicle swerving to avoid pothole",
276 "vehicle drifting out of lane (distracted driver)",
277 "vehicle cutting in from merging lane aggressively",
278 "slow-moving vehicle (farm equipment/golf cart) on road",
279 "delivery truck double-parked with flashers",
280 "garbage truck with crew working",
281 "parked car door opening into traffic",
282 "semi-truck jackknifed across lanes",
283 "wide-load vehicle with escort car",
284 "ice cream truck stopped with children nearby",
285 "ride-share vehicle stopped abruptly for pickup",
286 "moving truck partially blocking lane while loading",
287 "food truck parked on street with customer queue",
288 "animal (deer) on roadside",
289 "animal (dog) loose on road",
290 "flock of birds on road surface",
291 "animal (coyote/fox) darting across road",
292 "fallen tree branch partially blocking lane",
293 "debris/cargo on road surface",
294 "large pothole in driving lane",
295 "standing water/flooded section of road",
296 "oil spill or fluid on road surface",
297 "tire tread/retread debris on highway",
298 "mattress or furniture fallen from truck on road",
299 "manhole cover missing or displaced",
300 "traffic cone or barrel knocked into lane",
301 "sun glare directly ahead through windshield",
302 "headlight glare from oncoming vehicle at night",
303 "spray/mist from vehicle ahead on wet road",
304 "shadow from overpass creating sudden darkness",
305 "smoke from nearby fire drifting across road",
306 "reflection of wet road creating mirror effect",
307 "traffic light malfunctioning - flashing red",
308 "obscured traffic sign by overgrown vegetation",
309 "contradictory road signs at intersection",
310 "pedestrian signal countdown with people still crossing",
311 "railroad crossing with gates descending and lights flashing",
312 "toll booth approach with lanes merging",
313 ],
314 )
315
316 add_category(
317 config_builder,
318 "road_surface",
319 [
320 "dry asphalt - good condition",
321 "dry asphalt - faded lane markings",
322 "wet reflective surface",
323 "wet with puddles",
324 "icy patches visible",
325 "black ice conditions",
326 "snow-covered - lane markings obscured",
327 "gravel surface",
328 "unpaved dirt road",
329 "potholes and road damage visible",
330 "recent patching - uneven surface",
331 "construction - temporary markings",
332 ],
333 )
334
335 add_category(
336 config_builder,
337 "traffic_control",
338 [
339 "traffic light - green",
340 "traffic light - yellow/amber",
341 "traffic light - red",
342 "stop sign clearly visible",
343 "yield sign",
344 "speed limit sign - 25 mph",
345 "speed limit sign - 55 mph",
346 "no traffic control - uncontrolled intersection",
347 "construction signage and cones",
348 "temporary traffic lights",
349 ],
350 )
351
352 config_builder.add_column(
353 dd.ImageColumnConfig(
354 name="traffic_scene",
355 prompt=TRAFFIC_SCENE_PROMPT,
356 model_alias=model_alias,
357 )
358 )
359
360 return config_builder
361
362
363def create_dataset(
364 config_builder: dd.DataDesignerConfigBuilder,
365 *,
366 num_records: int,
367 dataset_name: str,
368 artifact_path: Path | str | None = None,
369) -> DatasetCreationResults:
370 data_designer = DataDesigner(artifact_path=artifact_path)
371 data_designer.validate(config_builder)
372 return data_designer.create(config_builder, num_records=num_records, dataset_name=dataset_name)
373
374
375TRAFFIC_SCENE_PROMPT = """\
376Create a photorealistic autonomous-vehicle ego-camera perception scene.
377
378The image must look like it was captured by a camera mounted on the self-driving
379ego vehicle, not by a roadside camera, drone, or cinematic photographer. Keep
380the viewpoint physically plausible for an AV sensor. When appropriate, show a
381subtle hood edge, windshield edge, or bumper edge, but do not show a full car
382interior or dashboard UI.
383
384Scene requirements:
385- Geographic region: {{ geographic_region }}
386- Road type: {{ road_type }}
387- Weather: {{ weather }}
388- Time of day: {{ time_of_day }}
389- Traffic density: {{ traffic_density }}
390- Vehicle mix: {{ vehicle_mix }}
391- Key scenario element: {{ scenario_element }}
392- Road surface: {{ road_surface }}
393- Traffic control: {{ traffic_control }}
394
395The scene should clearly show road geometry, lane markings, traffic signs,
396traffic control devices, surrounding vehicles, vulnerable road users when
397requested, and the key scenario element from the ego vehicle's camera. Preserve
398regional driving characteristics such as road width, side of road, sign style,
399and lane markings. Use realistic lighting, lens geometry, motion perspective,
400weather effects, and visibility. Generate exactly one final ego-camera image
401for this row. Do not return alternate versions, a grid, a pair of examples,
402before/after panels, or multiple camera frames. Do not include text overlays,
403labels, watermarks, dashcam timestamps, bounding boxes, sensor UI, or navigation
404UI.
405"""
406
407
408def parse_args() -> argparse.Namespace:
409 parser = argparse.ArgumentParser(description="Generate synthetic autonomous-vehicle traffic scenes.")
410 parser.add_argument("--num-records", type=int, default=10, help="Number of traffic scenes to generate.")
411 parser.add_argument("--dataset-name", default="synthetic-traffic-scenarios", help="Output dataset name.")
412 parser.add_argument("--artifact-path", type=Path, default=None, help="Optional Data Designer artifact directory.")
413 parser.add_argument("--model-provider", default=DEFAULT_MODEL_PROVIDER, help="Image model provider name.")
414 parser.add_argument("--model-id", default=DEFAULT_MODEL_ID, help="Provider model ID.")
415 parser.add_argument("--model-alias", default=DEFAULT_MODEL_ALIAS, help="Alias used by image columns.")
416 parser.add_argument("--image-size", default="1K", help="OpenRouter image size tier, such as 1K, 2K, or 4K.")
417 parser.add_argument("--aspect-ratio", default="16:9", help="Provider-specific aspect ratio value.")
418 parser.add_argument("--max-parallel-requests", type=int, default=10, help="Maximum parallel image requests.")
419 return parser.parse_args()
420
421
422def main() -> None:
423 args = parse_args()
424 config_builder = build_config(
425 model_provider=args.model_provider,
426 model_id=args.model_id,
427 model_alias=args.model_alias,
428 image_size=args.image_size,
429 aspect_ratio=args.aspect_ratio,
430 max_parallel_requests=args.max_parallel_requests,
431 )
432 results = create_dataset(
433 config_builder,
434 num_records=args.num_records,
435 dataset_name=args.dataset_name,
436 artifact_path=args.artifact_path,
437 )
438 dataset = results.load_dataset()
439 print(f"Generated {len(dataset)} synthetic traffic-scene rows.")
440 print(f"Dataset artifacts: {results.artifact_storage.base_dataset_path}")
441
442
443if __name__ == "__main__":
444 main()