feat: Add support for ADAS project in labeling and export processes

- Implemented class name loading for ADAS campaigns in `_class_names_for_campaign`.
- Enhanced annotation parsing to support cuboid and 2D rectangle labels in `_parse_ls_annotations`.
- Updated catalog building to include ADAS packs and batches in `build_catalog_signature`.
- Added ADAS cuboid promotion logic in `adas_cuboid.py`, supporting both quaternion_json and YOLO HBB formats.
- Introduced validation for ADAS batches to check for YOLO HBB labels in `adas_cuboid.py`.
- Modified delivery scanning to include ADAS project deliveries in `scan.py`.
- Extended job execution to handle ADAS exports for YOLO format in `runner.py`.
- Updated labeling export logic to accommodate ADAS project in `export_cuboid_batch.py`.
- Enhanced format conversion to support rectangle labels in `format_converter.py`.
- Improved CVAT task handling in `service.py` to ensure task ID updates on each labeling job.
- Updated web API endpoints to reflect ADAS project changes in `hsap-api.ts`.
- Added ADAS catalog types and UI components for displaying ADAS packs and batches in `dmsCatalog.ts`, `CatalogPage.tsx`, and `ExportPage.tsx`.
- Adjusted workflow registry to align with new ADAS project structure.
This commit is contained in:
2026-07-16 16:03:04 +08:00
parent be8a885b07
commit 1bde0fe430
23 changed files with 472 additions and 83 deletions

View File

@@ -68,7 +68,13 @@ def _class_names_for_campaign(camp) -> dict[int, str]:
import yaml
from as_platform.data.core import load_wf, proj_root
if not camp or camp.project != "dms":
if not camp:
return {}
if camp.project == "adas":
from as_platform.labeling.class_map import load_adas_class_names
names = load_adas_class_names()
return {i: n for i, n in enumerate(names)}
if camp.project != "dms":
return {}
wf = load_wf()
root = proj_root(wf, "dms")
@@ -114,21 +120,42 @@ def _parse_ls_annotations(path: Path, class_names: dict[int, str]) -> list[dict[
return []
out: list[dict[str, Any]] = []
for item in data.get("result") or []:
if item.get("type") not in ("rectanglelabels", "rectangle"):
continue
val = item.get("value") or {}
w_pct = float(val.get("width") or 0)
h_pct = float(val.get("height") or 0)
if w_pct <= 0 or h_pct <= 0:
continue
x_pct = float(val.get("x") or 0)
y_pct = float(val.get("y") or 0)
labels = val.get("rectanglelabels") or val.get("labels") or []
label = labels[0] if labels else "unknown"
cid = _name_to_class_id(str(label), class_names)
cx = (x_pct + w_pct / 2) / 100.0
cy = (y_pct + h_pct / 2) / 100.0
out.append({"class_id": cid, "bbox": (cx, cy, w_pct / 100.0, h_pct / 100.0)})
item_type = item.get("type")
if item_type == "cuboid":
# Cuboid: 8 vertices in pixel coords → compute 2D bbox
points = item.get("points") or []
if len(points) < 16:
continue
label = item.get("label") or "unknown"
cid = _name_to_class_id(str(label), class_names)
# Compute min/max from 8 (x,y) pairs
xs = [points[i] for i in range(0, len(points), 2)]
ys = [points[i] for i in range(1, len(points), 2)]
min_x, max_x = min(xs), max(xs)
min_y, max_y = min(ys), max(ys)
if max_x <= min_x or max_y <= min_y:
continue
orig_w = float(item.get("original_width") or 1920)
orig_h = float(item.get("original_height") or 1080)
bw = (max_x - min_x) / orig_w
bh = (max_y - min_y) / orig_h
cx = (min_x + max_x) / 2.0 / orig_w
cy = (min_y + max_y) / 2.0 / orig_h
out.append({"class_id": cid, "bbox": (cx, cy, bw, bh)})
elif item_type in ("rectanglelabels", "rectangle"):
val = item.get("value") or {}
w_pct = float(val.get("width") or 0)
h_pct = float(val.get("height") or 0)
if w_pct <= 0 or h_pct <= 0:
continue
x_pct = float(val.get("x") or 0)
y_pct = float(val.get("y") or 0)
labels = val.get("rectanglelabels") or val.get("labels") or []
label = labels[0] if labels else "unknown"
cid = _name_to_class_id(str(label), class_names)
cx = (x_pct + w_pct / 2) / 100.0
cy = (y_pct + h_pct / 2) / 100.0
out.append({"class_id": cid, "bbox": (cx, cy, w_pct / 100.0, h_pct / 100.0)})
return out