- 统一标注引擎为 CVAT:客户端/配置/格式转换、iframe 标注页、docker-compose.cvat.yml 与 no_auth 补丁 - 移除 Label Studio 相关配置与构建脚本,清理 embedded.bak 备份与误提交的 node_modules - 新增「我的标注」:跨 Campaign 收件箱、逐张清单、CVAT frame 跳转 - 飞书任务分配:通讯录同步选人、按量分配、分配后 DM 通知(含 my-tasks 链接) - ADAS cuboid_7cls 数据湖接入:workflow 路径、register-batch、开标上传与标注同步 - 数据湖挂载 AS_DATA_LAKE_ROOT、datasets/adas 符号链接、reset_labeling 运维脚本 - 补充 docs/HANDOVER.md 项目交接文档 Co-authored-by: Cursor <cursoragent@cursor.com>
149 lines
4.6 KiB
Python
149 lines
4.6 KiB
Python
"""CVAT 标注配置:为 DMS / ADAS / Lane 生成 label schema(唯一标注引擎)。"""
|
||
from __future__ import annotations
|
||
|
||
from typing import Any
|
||
|
||
from as_platform.config import WORKSPACE
|
||
from as_platform.labeling.scope import labeling_profile_key, load_dms_registry, load_labeling_registry
|
||
|
||
# Lane 车道线 — 折线
|
||
LANE_LABELS: list[dict[str, Any]] = [
|
||
{
|
||
"name": "lane_line",
|
||
"type": "polyline",
|
||
"attributes": [
|
||
{
|
||
"name": "type",
|
||
"mutable": False,
|
||
"input_type": "select",
|
||
"values": ["solid", "dashed", "double_solid", "double_dashed", "solid_dashed"],
|
||
"default_value": "solid",
|
||
},
|
||
{
|
||
"name": "color",
|
||
"mutable": False,
|
||
"input_type": "select",
|
||
"values": ["white", "yellow", "blue", "other"],
|
||
"default_value": "white",
|
||
},
|
||
],
|
||
},
|
||
{
|
||
"name": "curb",
|
||
"type": "polyline",
|
||
"attributes": [
|
||
{
|
||
"name": "type",
|
||
"mutable": False,
|
||
"input_type": "select",
|
||
"values": ["high", "low", "none"],
|
||
"default_value": "low",
|
||
},
|
||
],
|
||
},
|
||
{"name": "stop_line", "type": "polyline"},
|
||
]
|
||
|
||
# ADAS cuboid_7cls — 单目图像 cuboid
|
||
ADAS_CUBOID_7CLS_LABELS: list[dict[str, Any]] = [
|
||
{"name": "car", "type": "cuboid"},
|
||
{"name": "pedestrian", "type": "cuboid"},
|
||
{"name": "truck", "type": "cuboid"},
|
||
{"name": "bus", "type": "cuboid"},
|
||
{"name": "motorcycle", "type": "cuboid"},
|
||
{"name": "tricycle", "type": "cuboid"},
|
||
{"name": "traffic cone", "type": "cuboid"},
|
||
]
|
||
|
||
|
||
def _rect_label(name: str, **attrs: Any) -> dict[str, Any]:
|
||
lb: dict[str, Any] = {"name": name, "type": "rectangle"}
|
||
if attrs:
|
||
lb["attributes"] = attrs
|
||
return lb
|
||
|
||
|
||
def _dms_registry_task_config(task: str, mode: str | None) -> dict[str, Any]:
|
||
import sys
|
||
|
||
scripts = WORKSPACE / "datasets" / "dms" / "scripts"
|
||
if str(scripts) not in sys.path:
|
||
sys.path.insert(0, str(scripts))
|
||
from task_registry import get_mode_config, resolve_task_id
|
||
|
||
reg = load_dms_registry()
|
||
task_r, mode_r = resolve_task_id(task, mode)
|
||
return get_mode_config(task_r, mode_r, reg)
|
||
|
||
|
||
def _labels_from_class_names(names: list[str] | dict[int | str, str]) -> list[dict[str, Any]]:
|
||
if isinstance(names, dict):
|
||
ordered = [names[k] for k in sorted(names, key=lambda x: int(x))]
|
||
else:
|
||
ordered = list(names)
|
||
return [_rect_label(str(n)) for n in ordered]
|
||
|
||
|
||
def _dms_pose_labels() -> list[dict[str, Any]]:
|
||
labels: list[dict[str, Any]] = [_rect_label("face")]
|
||
labels.extend({"name": f"kp_{i:02d}", "type": "points"} for i in range(37))
|
||
return labels
|
||
|
||
|
||
def _dms_labels(task: str, mode: str | None) -> list[dict[str, Any]]:
|
||
tcfg = _dms_registry_task_config(task, mode)
|
||
ttype = tcfg.get("type") or "detect"
|
||
if ttype == "pose":
|
||
return _dms_pose_labels()
|
||
names = tcfg.get("names")
|
||
if names:
|
||
return _labels_from_class_names(names)
|
||
return [_rect_label("object")]
|
||
|
||
|
||
def _labels_from_registry_profile(project: str, task: str, mode: str | None) -> list[dict[str, Any]] | None:
|
||
reg = load_dms_registry() if project == "dms" else None
|
||
pk = labeling_profile_key(project, task, mode, reg)
|
||
prof = (load_labeling_registry().get("profiles") or {}).get(pk) or {}
|
||
cvat_names = prof.get("cvat_labels")
|
||
if cvat_names:
|
||
return [{"name": str(n), "type": "cuboid"} for n in cvat_names]
|
||
return None
|
||
|
||
|
||
def build_cvat_labels(
|
||
project: str,
|
||
task: str | None = None,
|
||
mode: str | None = None,
|
||
annotation_types: list[str] | None = None,
|
||
) -> list[dict[str, Any]]:
|
||
"""根据 HSAP project/task/mode 生成 CVAT Task 的 labels 定义。"""
|
||
task = task or ""
|
||
prof_labels = _labels_from_registry_profile(project, task, mode)
|
||
if prof_labels is not None:
|
||
return prof_labels
|
||
|
||
if project == "adas":
|
||
if task == "cuboid_7cls":
|
||
return ADAS_CUBOID_7CLS_LABELS
|
||
return ADAS_CUBOID_7CLS_LABELS
|
||
|
||
if project == "lane":
|
||
return LANE_LABELS
|
||
|
||
if project == "dms":
|
||
return _dms_labels(task, mode)
|
||
|
||
return [_rect_label("object")]
|
||
|
||
|
||
def resolve_annotation_types(project: str, task: str | None = None, mode: str | None = None) -> list[str]:
|
||
mapping = {
|
||
"dms": ["bbox", "keypoint"],
|
||
"adas": ["cuboid"],
|
||
"lane": ["polyline"],
|
||
}
|
||
if project == "adas" and task == "cuboid_7cls":
|
||
return ["cuboid"]
|
||
return mapping.get(project, ["bbox"])
|