feat: HSAP platform v2 — modular navigation, quality review, audit log, world model simulation
Major changes: - New frontend (platform/web/): Vite + React 18 + TypeScript + Tailwind - 4-module navigation: 数据送标 / 模型管理 / 车队管理 / 系统管理 - Data catalog with charts (DMS/ADAS/Lane 3-tab view) - Quality review workflow (标注质检): Good/Fine/Bad scoring with auto-advance - Audit enhancements: batch operations, rejection categories, Feishu notifications - Operation audit log (操作日志) - World model simulation studio (仿真工坊) - Dataset version management with snapshots and diff - ADAS 7-class dataset integration (138K images organized + compressed) - User management with Feishu integration and pagination - CRUD/search/filter on all pages, card layout redesign - PIL-optimized image overlay rendering - Auto-snapshot on build, in_review workflow stage - Removed embedded algorithm code (now in workspace)
This commit is contained in:
91
platform/as_platform/data/ingest/dms_inbox_raw.py
Normal file
91
platform/as_platform/data/ingest/dms_inbox_raw.py
Normal file
@@ -0,0 +1,91 @@
|
||||
"""DMS inbox 原图批次(如 addw/ddaw:仅有 images/,待送标,无 YOLO/COCO 标注)。"""
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from as_platform.data.batch import count_images, dms_has_images
|
||||
from as_platform.data.ingest.base import IngestAdapter, IngestContext, NormalizedDataset
|
||||
|
||||
|
||||
def _count_images(path: Path) -> int:
|
||||
return count_images(path)
|
||||
|
||||
|
||||
def _split_image_counts(root: Path) -> dict[str, int]:
|
||||
train = _count_images(root / "images" / "train")
|
||||
val = _count_images(root / "images" / "val")
|
||||
test = _count_images(root / "images" / "test")
|
||||
if train + val + test == 0:
|
||||
train = _count_images(root / "images")
|
||||
if train + val + test == 0 and root.name == "images":
|
||||
train = _count_images(root / "train")
|
||||
val = _count_images(root / "val")
|
||||
test = _count_images(root / "test")
|
||||
if train + val + test == 0:
|
||||
train = _count_images(root)
|
||||
if train + val + test == 0 and (root / "train").is_dir():
|
||||
train = _count_images(root / "train")
|
||||
val = _count_images(root / "val")
|
||||
test = _count_images(root / "test")
|
||||
return {"train": train, "val": val, "test": test}
|
||||
|
||||
|
||||
def _has_label_txts(root: Path) -> bool:
|
||||
for sub in ("labels", "labels/train"):
|
||||
d = root / sub
|
||||
if d.is_dir() and any(d.rglob("*.txt")):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _is_inbox_raw_layout(root: Path) -> bool:
|
||||
if _has_label_txts(root):
|
||||
return False
|
||||
if dms_has_images(root):
|
||||
return True
|
||||
if root.name == "images" and (_count_images(root / "train") > 0 or _count_images(root) > 0):
|
||||
return True
|
||||
if (root / "train").is_dir() and _count_images(root / "train") > 0:
|
||||
return True
|
||||
if _count_images(root) > 0 and not (root / "images").is_dir() and not (root / "labels").is_dir():
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class DmsInboxRawAdapter(IngestAdapter):
|
||||
format_id = "dms_inbox_raw"
|
||||
projects = ("dms",)
|
||||
|
||||
def can_handle(self, ctx: IngestContext) -> bool:
|
||||
return _is_inbox_raw_layout(ctx.source_path)
|
||||
|
||||
def inspect(self, ctx: IngestContext) -> NormalizedDataset:
|
||||
root = ctx.source_path
|
||||
split_counts = _split_image_counts(root)
|
||||
sample_count = sum(split_counts.values())
|
||||
warnings: list[str] = []
|
||||
if sample_count == 0:
|
||||
warnings.append(
|
||||
"未找到图片;请填批次根目录(含 images/train/)或 images/、train/ 目录"
|
||||
)
|
||||
|
||||
artifacts: list[str] = []
|
||||
if (root / "images").is_dir():
|
||||
artifacts.append("images/")
|
||||
elif (root / "train").is_dir():
|
||||
artifacts.append("train/(将规范为 images/train)")
|
||||
elif root.name == "images":
|
||||
artifacts.append("images/")
|
||||
|
||||
return NormalizedDataset(
|
||||
format_id=self.format_id,
|
||||
project=ctx.project,
|
||||
task=ctx.task,
|
||||
source_path=str(root),
|
||||
split_counts=split_counts,
|
||||
sample_count=sample_count,
|
||||
annotation_count=0,
|
||||
artifacts=artifacts,
|
||||
warnings=warnings,
|
||||
extra={"stage_hint": "raw_pool"},
|
||||
)
|
||||
@@ -5,6 +5,7 @@ from pathlib import Path
|
||||
|
||||
from as_platform.data.ingest.base import IngestAdapter, IngestContext, NormalizedDataset
|
||||
from as_platform.data.ingest.dms_coco import DmsCocoAdapter
|
||||
from as_platform.data.ingest.dms_inbox_raw import DmsInboxRawAdapter
|
||||
from as_platform.data.ingest.dms_yolo import DmsYoloAdapter
|
||||
from as_platform.data.ingest.lane_lines import LaneLinesAdapter
|
||||
from as_platform.data.ingest.lane_mask import LaneMaskAdapter
|
||||
@@ -17,6 +18,7 @@ class UnknownFormatError(ValueError):
|
||||
ADAPTERS: tuple[IngestAdapter, ...] = (
|
||||
DmsYoloAdapter(),
|
||||
DmsCocoAdapter(),
|
||||
DmsInboxRawAdapter(),
|
||||
LaneMaskAdapter(),
|
||||
LaneLinesAdapter(),
|
||||
)
|
||||
@@ -32,9 +34,15 @@ def detect_adapter(ctx: IngestContext) -> IngestAdapter:
|
||||
continue
|
||||
if adapter.can_handle(ctx):
|
||||
return adapter
|
||||
hint = ""
|
||||
if ctx.project == "dms":
|
||||
hint = (
|
||||
";DMS 送标/inbox 请使用批次根目录,且至少包含 images/train/*.jpg"
|
||||
"(或已标注的 images/+labels/、COCO annotations/)"
|
||||
)
|
||||
raise UnknownFormatError(
|
||||
f"unable to detect format for project={ctx.project}, task={ctx.task}, "
|
||||
f"source={ctx.source_path}. supported={available_formats(ctx.project)}"
|
||||
f"source={ctx.source_path}. supported={available_formats(ctx.project)}{hint}"
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user