diff --git a/carla_road_collector_belka_forLowQ.py b/carla_road_collector_belka_forLowQ.py new file mode 100644 index 0000000..2bbc91e --- /dev/null +++ b/carla_road_collector_belka_forLowQ.py @@ -0,0 +1,729 @@ +""" +CARLA Road-Following Map Collector +==================================== +Сбор мультимодальных снимков вдоль дорог карты CARLA. + +Три типа съёмки на каждой точке: + • satellite/ — вид сверху (pitch = -90°) + • drone/ — вид под углом с орбиты + • street/ — вид с уровня человеческого роста (~1.7 м) + +Структура выходных файлов: + carla_road_dataset/Town10HD/ + ├── wp000000_r5_l-1/ + │ ├── satellite/ + │ │ ├── rgb.png + │ │ ├── semantic_label.png + │ │ ├── semantic_color.png + │ │ ├── depth_uint16.png + │ │ └── depth_color.png + │ ├── drone/ + │ │ ├── yaw000_rgb.png + │ │ ├── yaw000_semantic_label.png + │ │ ├── yaw000_semantic_color.png + │ │ ├── yaw000_depth_uint16.png + │ │ ├── yaw000_depth_color.png + │ │ └── yaw090_… (и т.д.) + │ └── street/ + │ ├── forward_rgb.png + │ ├── forward_semantic_label.png + │ ├── forward_semantic_color.png + │ ├── forward_depth_uint16.png + │ ├── forward_depth_color.png + │ ├── left_rgb.png + │ ├── right_rgb.png + │ └── backward_rgb.png + ├── wp000001_r5_l-1/ + │ └── … + └── metadata.json + +Usage: + python carla_road_collector.py --host localhost --port 2000 \\ + --output ./dataset --route dense --step 20 + +Requirements: + pip install carla numpy opencv-python tqdm +""" + +import carla +import numpy as np +import cv2 +import math +import argparse +import json +import threading +from pathlib import Path +from dataclasses import dataclass, field +from typing import Optional, List, Dict, Tuple +from tqdm import tqdm +import os + + +# --------------------------------------------------------------------------- +# Semantic palette +# --------------------------------------------------------------------------- + +SEMANTIC_PALETTE: Dict[int, Tuple[int, int, int]] = { + 0: (0, 0, 0), 1: (70, 70, 70), 2: (100, 40, 40), + 3: (55, 90, 80), 4: (220, 20, 60), 5: (153, 153, 153), + 6: (157, 234, 50), 7: (128, 64, 128), 8: (244, 35, 232), + 9: (107, 142, 35), 10: (0, 0, 142), 11: (102, 102, 156), + 12: (220, 220, 0), 13: (70, 130, 180), 14: (81, 0, 81), + 15: (150, 100, 100), 16: (230, 150, 140), 17: (180, 165, 180), + 18: (250, 170, 30), 19: (110, 190, 160), 20: (170, 120, 50), + 21: (45, 60, 150), 22: (145, 170, 100), +} +_SEM_NAMES = { + 0:"Unlabeled", 1:"Building", 2:"Fence", 3:"Other", 4:"Pedestrian", + 5:"Pole", 6:"RoadLine", 7:"Road", 8:"SideWalk", 9:"Vegetation", + 10:"Vehicle", 11:"Wall", 12:"TrafficSign", 13:"Sky", 14:"Ground", + 15:"Bridge", 16:"RailTrack", 17:"GuardRail", 18:"TrafficLight", + 19:"Static", 20:"Dynamic", 21:"Water", 22:"Terrain", +} +_PALETTE_LUT: Optional[np.ndarray] = None + +def _get_lut() -> np.ndarray: + global _PALETTE_LUT + if _PALETTE_LUT is None: + lut = np.zeros((256, 3), dtype=np.uint8) + for tag, bgr in SEMANTIC_PALETTE.items(): + lut[tag] = bgr + _PALETTE_LUT = lut + return _PALETTE_LUT + +def semantic_to_color(label: np.ndarray) -> np.ndarray: + return _get_lut()[label] + + +# --------------------------------------------------------------------------- +# Depth helpers +# --------------------------------------------------------------------------- + +def decode_depth_meters(img: carla.Image) -> np.ndarray: + arr = np.frombuffer(img.raw_data, dtype=np.uint8).reshape( + (img.height, img.width, 4)) + R = arr[:, :, 2].astype(np.float32) + G = arr[:, :, 1].astype(np.float32) + B = arr[:, :, 0].astype(np.float32) + return (R + G * 256.0 + B * 65536.0) / (256.0 ** 3 - 1) * 1000.0 + +def depth_to_uint16(depth_m: np.ndarray, max_d: float) -> np.ndarray: + return (np.clip(depth_m, 0, max_d) / max_d * 65535).astype(np.uint16) + +def depth_to_colormap(depth_m: np.ndarray, max_d: float) -> np.ndarray: + norm = (np.clip(depth_m, 0, max_d) / max_d * 255).astype(np.uint8) + return cv2.applyColorMap(norm, cv2.COLORMAP_TURBO) + + +# --------------------------------------------------------------------------- +# Config +# --------------------------------------------------------------------------- + +@dataclass +class RoadCollectorConfig: + # Маршрут + route_mode: str = "dense" # "topology" | "dense" + step_meters: float = 20.0 + min_dist_between_captures: float = 10.0 + + # Спутник + sat_altitude: float = 60.0 + sat_fov: float = 90.0 + sat_image_w: int = 1024 + sat_image_h: int = 1024 + + # Дрон + drone_altitude: float = 40.0 + drone_pitch: float = -35.0 + drone_yaw_offsets: List[float] = field( + default_factory=lambda: [0.0, 90.0, 180.0, 270.0]) + drone_fov: float = 90.0 + drone_image_w: int = 1024 + drone_image_h: int = 1024 + + # Улица (вид с уровня человека) + street_height: float = 1.9 # высота камеры над землёй, метры + street_fov: float = 90.0 + street_image_w: int = 1024 + street_image_h: int = 1024 + # Стороны съёмки: forward=вперёд по дороге, остальные — относительно него + street_yaw_offsets: List[float] = field( + default_factory=lambda: [0.0, 90.0, 180.0, 270.0]) + street_yaw_labels: List[str] = field( + default_factory=lambda: ["forward", "right", "backward", "left"]) + + # Модальности + capture_rgb: bool = True + capture_semantic: bool = True + capture_depth: bool = True + depth_max_meters: float = 100.0 + + # Виды + capture_satellite: bool = True + capture_drone: bool = True + capture_street: bool = True + + # Вывод + output_dir: str = "./carla_road_dataset" + save_metadata: bool = True + + # Синхронизация + settle_ticks: int = 5 + + +# --------------------------------------------------------------------------- +# Sensor infrastructure +# --------------------------------------------------------------------------- + +class _SensorWrapper: + def __init__(self): + self._data = None + self._event = threading.Event() + + def callback(self, data): + self._data = data + self._event.set() + + def wait(self, timeout: float = 10.0): + if not self._event.wait(timeout) or self._data is None: + raise RuntimeError("Sensor timed out.") + return self._data + + +class MultiSensorCapture: + """Спавним RGB + Semantic + Depth в одной точке, снимаем за один тик.""" + + _BP = { + "rgb": "sensor.camera.rgb", + "semantic": "sensor.camera.semantic_segmentation", + "depth": "sensor.camera.depth", + } + + def __init__(self, world: carla.World, transform: carla.Transform, + w: int, h: int, fov: float, modalities: List[str]): + self._wrappers: Dict[str, _SensorWrapper] = {} + self._actors: List[carla.Actor] = [] + bp_lib = world.get_blueprint_library() + for name in modalities: + bp = bp_lib.find(self._BP[name]) + bp.set_attribute("image_size_x", str(w)) + bp.set_attribute("image_size_y", str(h)) + bp.set_attribute("fov", str(fov)) + actor = world.spawn_actor(bp, transform) + wrapper = _SensorWrapper() + actor.listen(wrapper.callback) + self._wrappers[name] = wrapper + self._actors.append(actor) + + def capture(self, tick_fn, settle_ticks: int = 5) -> Dict[str, carla.Image]: + for _ in range(settle_ticks): + tick_fn() + return {name: w.wait() for name, w in self._wrappers.items()} + + def destroy(self): + for a in self._actors: + try: + if a and a.is_alive: + a.stop() # отписываем callback ДО destroy + a.destroy() + except Exception: + pass # при Low quality иногда уже мёртв — не страшно + self._actors.clear() + + +# --------------------------------------------------------------------------- +# Сохранение одного набора изображений (все модальности) в папку +# --------------------------------------------------------------------------- + +def save_shot(images: Dict[str, carla.Image], + folder: Path, + prefix: str, + cfg: RoadCollectorConfig) -> Dict: + """ + Сохраняет все активные модальности в `folder`. + Имена файлов: _rgb.png, _semantic_label.png и т.д. + Если prefix пустой — просто rgb.png, semantic_label.png и т.д. + + Возвращает словарь с путями файлов и статистикой глубины. + """ + folder.mkdir(parents=True, exist_ok=True) + + def fname(tag: str) -> Path: + name = f"{prefix}_{tag}.png" if prefix else f"{tag}.png" + return folder / name + + saved: Dict = {} + + # ── RGB ────────────────────────────────────────────────────────────────── + if "rgb" in images: + img = images["rgb"] + arr = np.frombuffer(img.raw_data, dtype=np.uint8).reshape( + (img.height, img.width, 4)) + p = fname("rgb") + cv2.imwrite(str(p), arr[:, :, :3]) + saved["rgb"] = p.name + + # ── Semantic ───────────────────────────────────────────────────────────── + if "semantic" in images: + img = images["semantic"] + arr = np.frombuffer(img.raw_data, dtype=np.uint8).reshape( + (img.height, img.width, 4)) + label = arr[:, :, 2] # тег хранится в R-канале + + p_lbl = fname("semantic_label") + cv2.imwrite(str(p_lbl), label) + saved["semantic_label"] = p_lbl.name + + p_col = fname("semantic_color") + cv2.imwrite(str(p_col), semantic_to_color(label)) + saved["semantic_color"] = p_col.name + + # ── Depth ───────────────────────────────────────────────────────────────── + if "depth" in images: + depth_m = decode_depth_meters(images["depth"]) + + p_u16 = fname("depth_uint16") + cv2.imwrite(str(p_u16), depth_to_uint16(depth_m, cfg.depth_max_meters)) + saved["depth_uint16"] = p_u16.name + + p_vis = fname("depth_color") + cv2.imwrite(str(p_vis), depth_to_colormap(depth_m, cfg.depth_max_meters)) + saved["depth_color"] = p_vis.name + + saved["depth_stats"] = { + "min_m": float(np.nanmin(depth_m)), + "max_m": float(np.nanmax(depth_m)), + "mean_m": float(np.nanmean(depth_m)), + } + + return saved + + +def _active_modalities(cfg: RoadCollectorConfig) -> List[str]: + m = [k for k, v in [("rgb", cfg.capture_rgb), + ("semantic", cfg.capture_semantic), + ("depth", cfg.capture_depth)] if v] + if not m: + raise ValueError("Включите хотя бы одну модальность.") + return m + + +# --------------------------------------------------------------------------- +# Route builders +# --------------------------------------------------------------------------- + +def build_route_topology(carla_map: carla.Map) -> List[carla.Waypoint]: + topology = carla_map.get_topology() + seen = set() + waypoints = [] + for wp_s, wp_e in topology: + key = (wp_s.road_id, wp_s.lane_id) + if key in seen: + continue + seen.add(key) + ls, le = wp_s.transform.location, wp_e.transform.location + mid = carla.Location(x=(ls.x+le.x)/2, y=(ls.y+le.y)/2, z=(ls.z+le.z)/2) + wp = carla_map.get_waypoint(mid, project_to_road=True, + lane_type=carla.LaneType.Driving) + if wp: + waypoints.append(wp) + print(f"[Route] topology: {len(waypoints)} сегментов") + return waypoints + + +def build_route_dense(carla_map: carla.Map, + step: float, min_dist: float) -> List[carla.Waypoint]: + all_wps = [wp for wp in carla_map.generate_waypoints(step) + if wp.lane_type == carla.LaneType.Driving] + kept = [] + for wp in all_wps: + loc = wp.transform.location + if not any(loc.distance(k.transform.location) < min_dist for k in kept): + kept.append(wp) + + # ! reverse + kept = kept[::-1] + + print(f"[Route] dense: {len(all_wps)} → {len(kept)} точек " + f"(шаг={step}м, min_dist={min_dist}м)") + return kept + + +# --------------------------------------------------------------------------- +# Capture at one waypoint — satellite / drone / street +# --------------------------------------------------------------------------- + +def capture_at_waypoint(world: carla.World, + wp: carla.Waypoint, + idx: int, + cfg: RoadCollectorConfig, + map_dir: Path) -> dict: + """ + Создаёт папку для одной точки маршрута и снимает все три вида. + + Структура папки: + wp000042_r12_l-1/ + ├── satellite/ + │ ├── rgb.png + │ ├── semantic_label.png + │ ├── semantic_color.png + │ ├── depth_uint16.png + │ └── depth_color.png + ├── drone/ + │ ├── yaw000_rgb.png … yaw000_depth_color.png + │ └── yaw090_rgb.png … + └── street/ + ├── forward_rgb.png … forward_depth_color.png + ├── right_rgb.png … + ├── backward_rgb.png … + └── left_rgb.png … + """ + loc = wp.transform.location + road_yaw = wp.transform.rotation.yaw + modalities = _active_modalities(cfg) + + # Папка этой точки + cell_name = f"wp{idx:06d}_r{wp.road_id}_l{wp.lane_id}" + cell_dir = map_dir / cell_name + cell_dir.mkdir(parents=True, exist_ok=True) + + meta: dict = { + "cell": cell_name, + "waypoint_idx": idx, + "road_id": wp.road_id, + "lane_id": wp.lane_id, + "road_yaw_deg": road_yaw, + "location": {"x": loc.x, "y": loc.y, "z": loc.z}, + "satellite": None, + "drone": [], + "street": [], + } + + # ── 1. SATELLITE ────────────────────────────────────────────────────────── + if cfg.capture_satellite: + cam_z = loc.z + cfg.sat_altitude + #! foward + # sat_yaw = road_yaw+180 + #! reverse + sat_yaw = road_yaw + + transform = carla.Transform( + carla.Location(x=loc.x, y=loc.y, z=cam_z), + carla.Rotation(pitch=-90.0, yaw=sat_yaw, roll=0.0) + ) + sensor = MultiSensorCapture(world, transform, + cfg.sat_image_w, cfg.sat_image_h, + cfg.sat_fov, modalities) + try: + images = sensor.capture(world.tick, cfg.settle_ticks) + finally: + sensor.destroy() + for _ in range(3): # Low quality needs extra tick after destroy + world.tick() + + sat_dir = cell_dir / "satellite" + saved = save_shot(images, sat_dir, prefix="", cfg=cfg) + meta["satellite"] = { + "camera": {"x": loc.x, "y": loc.y, "z": cam_z, + "pitch": -90.0, "yaw": sat_yaw} + } + + # ── 2. DRONE ────────────────────────────────────────────────────────────── + if cfg.capture_drone: + cam_z = loc.z + cfg.drone_altitude + pitch_rad = math.radians(abs(cfg.drone_pitch)) + orbit_r = (cfg.drone_altitude / math.tan(pitch_rad) + if pitch_rad > 1e-6 else 0.0) + drone_dir = cell_dir / "drone" + + for yaw_off in cfg.drone_yaw_offsets: + abs_yaw = road_yaw + yaw_off # выравниваем по дороге + + yaw_rad = math.radians(abs_yaw) + + #! foward + # cam_x = loc.x - (orbit_r * math.cos(yaw_rad))/4 + # cam_y = loc.y - (orbit_r * math.sin(yaw_rad))/4 + # look_yaw = abs_yaw + + #! reverse + cam_x = loc.x + (orbit_r * math.cos(yaw_rad))/4 + cam_y = loc.y + (orbit_r * math.sin(yaw_rad))/4 + look_yaw = abs_yaw + 180 + + transform = carla.Transform( + carla.Location(x=cam_x, y=cam_y, z=cam_z), + carla.Rotation(pitch=cfg.drone_pitch, yaw=look_yaw, roll=0.0) + ) + sensor = MultiSensorCapture(world, transform, + cfg.drone_image_w, cfg.drone_image_h, + cfg.drone_fov, modalities) + try: + images = sensor.capture(world.tick, cfg.settle_ticks) + finally: + sensor.destroy() + for _ in range(3): # Low quality needs extra tick after destroy + world.tick() + + prefix = f"yaw{int(yaw_off):03d}" + saved = save_shot(images, drone_dir, prefix=prefix, cfg=cfg) + meta["drone"].append({ + "yaw_offset_from_road": yaw_off, + "camera": {"x": cam_x, "y": cam_y, "z": cam_z, + "pitch": cfg.drone_pitch, "yaw": look_yaw}, + "orbit_radius": orbit_r, + "altitude": cfg.drone_altitude, + "fov": cfg.drone_fov, + "files": saved, + }) + + # ── 3. STREET ───────────────────────────────────────────────────────────── + if cfg.capture_street: + cam_z = loc.z + cfg.street_height + street_dir = cell_dir / "street" + labels = cfg.street_yaw_labels + + for i, yaw_off in enumerate(cfg.street_yaw_offsets): + #! foward + # abs_yaw = road_yaw + yaw_off # 0° = смотрим вперёд по дороге + #! reverse + abs_yaw += 180 + + label = labels[i] if i < len(labels) else f"yaw{int(yaw_off):03d}" + cam_x = loc.x - orbit_r * math.cos(yaw_rad) + cam_y = loc.y - orbit_r * math.sin(yaw_rad) + transform = carla.Transform( + carla.Location(x=loc.x, y=loc.y, z=cam_z), # old v + carla.Rotation(pitch=0.0, yaw=abs_yaw, roll=0.0) + ) + sensor = MultiSensorCapture(world, transform, + cfg.street_image_w, cfg.street_image_h, + cfg.street_fov, modalities) + try: + images = sensor.capture(world.tick, cfg.settle_ticks) + finally: + sensor.destroy() + for _ in range(3): # Low quality needs extra tick after destroy + world.tick() + + saved = save_shot(images, street_dir, prefix=label, cfg=cfg) + meta["street"].append({ + "direction": label, + "yaw_offset_from_road": yaw_off, + "camera": {"x": cam_x, "y": cam_y, "z": cam_z, + "pitch": 0.0, "yaw": abs_yaw}, + "height": cfg.street_height, + "fov": cfg.street_fov, + "files": saved, + }) + + return meta + + +# --------------------------------------------------------------------------- +# Main collection loop +# --------------------------------------------------------------------------- + +def collect_along_roads(cfg: RoadCollectorConfig, + host: str = "localhost", + port: int = 2000): + + print(f"[CARLA] Подключение к {host}:{port} …") + client = carla.Client(host, port) + client.set_timeout(30.0) + world = client.get_world() + + settings = world.get_settings() + settings.synchronous_mode = True + settings.fixed_delta_seconds = 0.05 + settings.no_rendering_mode = False + world.apply_settings(settings) + + # weather = carla.WeatherParameters( + # cloudiness=100.0, + # precipitation=0.0, + # precipitation_deposits=0.0, + # wind_intensity=0.0, + # sun_altitude_angle=60.0, + # sun_azimuth_angle=0.0, + # fog_density=0.0, + # wetness=0.0 + # ) + # world.set_weather(weather) + # world.tick() + + carla_map = world.get_map() + map_name = carla_map.name + modalities = _active_modalities(cfg) + + views = [v for v, on in [("satellite", cfg.capture_satellite), + ("drone", cfg.capture_drone), + ("street", cfg.capture_street)] if on] + + print(f"[CARLA] Карта: {map_name}") + print(f"[Config] Виды: {views} | Модальности: {modalities}") + + map_dir = Path(cfg.output_dir) / map_name.replace("/", "_") + map_dir.mkdir(parents=True, exist_ok=True) + + # Маршрут + if cfg.route_mode == "topology": + waypoints = build_route_topology(carla_map) + elif cfg.route_mode == "dense": + waypoints = build_route_dense(carla_map, cfg.step_meters, + cfg.min_dist_between_captures) + else: + raise ValueError(f"Неизвестный route_mode: {cfg.route_mode!r}") + + print(f"[Route] Точек для съёмки: {len(waypoints)}") + + all_meta = [] + try: + for idx, wp in enumerate(tqdm(waypoints, desc="Capture", unit="wp")): + try: + meta = capture_at_waypoint(world, wp, idx, cfg, map_dir) + all_meta.append(meta) + except Exception as exc: + loc = wp.transform.location + print(f"\n [!] wp{idx:06d} ({loc.x:.1f}, {loc.y:.1f}) → {exc}") + finally: + settings.synchronous_mode = False + world.apply_settings(settings) + + if cfg.save_metadata: + meta_path = map_dir / "metadata.json" + with open(meta_path, "w") as f: + json.dump({ + "map": map_name, + "config": { + "route_mode": cfg.route_mode, + "step_meters": cfg.step_meters, + "views": views, + "modalities": modalities, + "sat_altitude": cfg.sat_altitude, + "drone_altitude": cfg.drone_altitude, + "drone_pitch": cfg.drone_pitch, + "drone_yaw_offsets": cfg.drone_yaw_offsets, + "street_height": cfg.street_height, + "street_yaw_offsets": cfg.street_yaw_offsets, + "street_yaw_labels": cfg.street_yaw_labels, + "depth_max_meters": cfg.depth_max_meters, + }, + "semantic_palette": { + str(k): {"bgr": list(v), "name": _SEM_NAMES.get(k, "?")} + for k, v in SEMANTIC_PALETTE.items() + }, + "total_cells": len(all_meta), + "cells": all_meta, + }, f, indent=2) + print(f"\n[Meta] Сохранено → {meta_path}") + + total_imgs = sum( + len([f for f in (m.get("satellite") or {}).get("files", {}) + if not f.endswith("_stats")]) + + sum(len(d["files"]) for d in m.get("drone", [])) + + sum(len(s["files"]) for s in m.get("street", [])) + for m in all_meta + ) + print(f"\n✓ Готово. {len(all_meta)} ячеек | ~{total_imgs} файлов → {map_dir}") + return all_meta + + +# --------------------------------------------------------------------------- +# CLI +# --------------------------------------------------------------------------- + +def parse_args(): + p = argparse.ArgumentParser( + description="CARLA road collector — satellite + drone + street", + formatter_class=argparse.ArgumentDefaultsHelpFormatter + ) + p.add_argument("--host", default="localhost") + p.add_argument("--port", type=int, default=2000) + # p.add_argument("--output", default="./carla_road_map2_LOWQ_FULL") + p.add_argument("--output", default="./carla_road_map2rew_LOWQ_FULL") + + g = p.add_argument_group("Маршрут") + g.add_argument("--route", default="dense", + choices=["topology", "dense"]) + g.add_argument("--step", type=float, default=20.0, metavar="M", # Было 1000 + help="Шаг между точками (dense mode, метры)") + g.add_argument("--min-dist", type=float, default=50.0, metavar="M")# Было 100 + + g2 = p.add_argument_group("Камеры") + g2.add_argument("--sat-altitude", type=float, default=60.0) + g2.add_argument("--sat-fov", type=float, default=90.0) # было 90 + g2.add_argument("--drone-altitude", type=float, default=40.0) # было 40 + g2.add_argument("--drone-pitch", type=float, default=-35.0) + g2.add_argument("--drone-yaws", nargs="+", type=float, + #default=[0.0, 90.0, 180.0, 270.0], + default=[180.0], metavar="YAW") + g2.add_argument("--street-height", type=float, default=1.7, + help="Высота уличной камеры над землёй (м)") + g2.add_argument("--street-fov", type=float, default=90.0) + g2.add_argument("--street-yaws", nargs="+", type=float, + #default=[0.0, 90.0, 180.0, 270.0] + default=[0.0], metavar="YAW", + help="Направления улицы: 0=вперёд по дороге") + g2.add_argument("--image-size", type=int, default=1024) + g2.add_argument("--depth-max", type=float, default=100.0) + g2.add_argument("--settle-ticks", type=int, default=5) + + g3 = p.add_argument_group("Модальности") + g3.add_argument("--no-rgb", action="store_true") + g3.add_argument("--no-semantic", action="store_false") + g3.add_argument("--no-depth", action="store_false") + + p.set_defaults(no_rgb=False, no_semantic=False, no_depth=False) + + g4 = p.add_argument_group("Виды") + g4.add_argument("--no-satellite", action="store_true") + g4.add_argument("--no-drone", action="store_true") + g4.add_argument("--no-street", action="store_true") + + return p.parse_args() + + +if __name__ == "__main__": + args = parse_args() + + cfg = RoadCollectorConfig( + route_mode=args.route, + step_meters=args.step, + min_dist_between_captures=args.min_dist, + + sat_altitude=args.sat_altitude, + sat_fov=args.sat_fov, + sat_image_w=args.image_size, + sat_image_h=args.image_size, + + drone_altitude=args.drone_altitude, + drone_pitch=args.drone_pitch, + drone_yaw_offsets=args.drone_yaws, + drone_fov=90.0, + drone_image_w=args.image_size, + drone_image_h=args.image_size, + + street_height=args.street_height, + street_fov=args.street_fov, + street_yaw_offsets=args.street_yaws, + street_yaw_labels=["forward", "right", "backward", "left"], + street_image_w=args.image_size, + street_image_h=args.image_size, + + capture_rgb=not args.no_rgb, + capture_semantic=not args.no_semantic, + capture_depth=not args.no_depth, + depth_max_meters=args.depth_max, + + capture_satellite=not args.no_satellite, + capture_drone=not args.no_drone, + capture_street=not args.no_street, + + output_dir=args.output, + settle_ticks=args.settle_ticks + ) + + collect_along_roads(cfg=cfg, host=args.host, port=args.port) + + \ No newline at end of file