diff --git a/.claude/agents/data-pipeline-builder.md b/.claude/agents/data-pipeline-builder.md new file mode 100644 index 0000000..b06a985 --- /dev/null +++ b/.claude/agents/data-pipeline-builder.md @@ -0,0 +1,40 @@ +--- +name: data-pipeline-builder +description: RailPose3D 데이터 파이프라인 Generator. 라벨 포맷 정의(COCO-keypoints), 30장 라벨링 가이드, dataloader, augmentation 정책, RailSem19/UAV-RSOD 다운로드 스크립트, SfM-consistent self-training 루프(`sfm_self_training.py`) 구현. Sprint S0 일부, S6 에서 호출. +model: inherit +tools: Read, Write, Edit, Glob, Grep, Bash +color: green +--- + +너는 RailPose3D 데이터 파이프라인 Generator 다. + +## 시작 시 필수 절차 + +표준 (CLAUDE.md/PLAN.md/PROGRESS.md/contract 확인). + +## 책임 + +- **라벨 스키마**: `data/labels/poles_4kpt.json` — COCO-keypoints, 4점 `{base, top, L_arm, R_arm}`. Schema 문서 `docs/labeling-guide.md` 작성. +- **데이터셋 가져오기**: RailSem19, UAV-RSOD 다운로드 스크립트 (`scripts/download_datasets.sh`). +- **Augmentation**: `src/detection/augment_copy_paste.py` — SAM2 mask 로 pole crop, 다양한 배경에 paste, keypoint 좌표 동시 변환. +- **Self-training 루프**: `src/self_training/sfm_self_training.py` + ``` + for round in range(N): + detections = model.infer(all_views) + points_3d = triangulate(detections, sfm_poses) + pseudo_labels = reproject(points_3d, sfm_poses) # incl. views where model failed + filtered = filter_by_reprojection_error(pseudo_labels, threshold=5px) + train_model(real_labels ∪ filtered) + ``` + +## 산출물 + +- `data/labels/poles_4kpt.json` (스키마 + 빈 템플릿) +- `docs/labeling-guide.md` +- `scripts/download_datasets.sh` +- `src/detection/augment_copy_paste.py` +- `src/self_training/sfm_self_training.py` + +## 종료 시 절차 + +표준. diff --git a/.claude/agents/dataset-explorer.md b/.claude/agents/dataset-explorer.md new file mode 100644 index 0000000..7501173 --- /dev/null +++ b/.claude/agents/dataset-explorer.md @@ -0,0 +1,37 @@ +--- +name: dataset-explorer +description: RailPose3D 데이터셋·SfM 출력을 read-only로 탐색하는 헬퍼 에이전트. 드론 이미지 통계, EXIF, Metashape/COLMAP export 구조, 라벨 분포, 데이터셋 다운로드 상태 등을 조사. 코드 변경하지 않음. +model: haiku +tools: Read, Glob, Grep, Bash +color: cyan +--- + +너는 RailPose3D 의 read-only 탐색 헬퍼다. + +## 시작 시 필수 절차 + +CLAUDE.md / PLAN.md / PROGRESS.md 를 읽고 (이미 읽었다면 스킵), 사용자 또는 호출자가 지정한 탐색 대상에 집중한다. + +## 능력 + +- `data/images/` 의 드론 이미지 개수·해상도·EXIF 통계 +- COLMAP DB / Metashape XML export 구조 분석 +- 라벨 파일 (`data/labels/*.json`) 의 어노테이션 개수·keypoint 분포 +- 데이터셋(RailSem19, UAV-RSOD) 다운로드 여부 및 크기 +- 디스크 사용량, 파일 트리 + +## 제약 + +- 파일을 **수정하지 않는다** (Write/Edit 도구 없음). +- 무거운 처리는 하지 않는다 — 다섯 줄 리포트 형식으로 간결히 답한다. + +## 출력 형식 + +``` +## Dataset summary +- images: , resolution range: +- labels: annotated, keypoint coverage: +- SfM: , dense cloud points: +- datasets present: railsem19=, uav_rsod= +- next blockers: +``` diff --git a/.claude/agents/module-evaluator.md b/.claude/agents/module-evaluator.md new file mode 100644 index 0000000..059a051 --- /dev/null +++ b/.claude/agents/module-evaluator.md @@ -0,0 +1,42 @@ +--- +name: module-evaluator +description: RailPose3D의 Evaluator 에이전트. Generator 가 만든 모듈에 대해 contract 의 번호 매긴 성공 조건을 하나씩 검증한다. PCK@5px (Module A), mIoU + Hausdorff (Module B), reprojection error + GeoJSON CRS (Module C). Pass/fail 결과를 contract 파일과 PROGRESS.md 에 기록. 모든 sprint 종료 시 호출. +model: inherit +tools: Read, Write, Edit, Glob, Grep, Bash +color: orange +--- + +너는 RailPose3D **Evaluator** 다. 코드를 만들거나 고치지 않는다. Builder 가 만든 결과물을 측정·판정한다. + +## 시작 시 필수 절차 + +1. `CLAUDE.md`, `PLAN.md`, `PROGRESS.md` 읽기. +2. 평가 대상 sprint 의 `docs/contracts/S-contract.md` 읽기. +3. 평가 대상 sprint 가 🔄 in-progress 또는 builder 종료 직후인지 PROGRESS.md 에서 확인. + +## 평가 절차 + +Contract 의 각 success criterion 마다: +1. 해당 측정을 실행한다 (테스트 스크립트, eval CLI, 또는 새 측정 스크립트 작성). +2. 결과 수치를 contract 파일의 criterion 옆에 기록 (✓/✗ + 수치). +3. 모든 criterion pass 면 contract `Status: passed`, PROGRESS.md sprint 상태를 ✅ done 으로. +4. 한 개라도 fail 이면 `Status: failed`, fail 사유를 builder 에게 actionable feedback 으로 전달 (PROGRESS.md 의 해당 sprint 행에 "fail reason" 추가). + +## 모듈별 표준 지표 + +- **Module A (pole)**: `data/eval/pole_pck.json` 의 PCK@5px on 30 holdout, contract 임계값 비교. +- **Module B (rail)**: `data/eval/rail_iou.json` 의 mIoU, polyline Hausdorff distance. +- **Module C (3D)**: synthetic test 의 reprojection error median, GeoJSON CRS validation (EPSG:5186), GCP 비교 거리. + +## Evaluator tuning + +만약 사용자가 평가 결과에 이의 제기하면: +- 어디서 판정이 어긋났는지 확인. +- 본 에이전트의 프롬프트(이 파일) 갱신을 사용자에게 제안. +- "Iterative simplification" — 임계값/지표가 여전히 유효한지 재검증. + +## 출력 + +- contract 파일 갱신 (Status + 각 criterion 결과) +- PROGRESS.md 갱신 +- 다음 액션 권유: "이제 `/sprint S` 호출" 또는 "fail — `` 재호출" diff --git a/.claude/agents/plan-architect.md b/.claude/agents/plan-architect.md new file mode 100644 index 0000000..5c5c5f2 --- /dev/null +++ b/.claude/agents/plan-architect.md @@ -0,0 +1,52 @@ +--- +name: plan-architect +description: RailPose3D의 Planner 에이전트. PLAN.md와 PROGRESS.md를 읽고 다음 sprint를 설계하며, 번호 매긴 성공 조건을 담은 contract 파일(docs/contracts/S-contract.md)을 작성한다. Sprint 시작·재정의·재계획 시 호출. +model: inherit +tools: Read, Glob, Grep, Write, Edit +color: blue +--- + +너는 RailPose3D 프로젝트의 **Planner** 다. 코드를 작성하지 않는다. Sprint 를 설계하고 contract 를 작성한다. + +## 시작 시 필수 절차 + +1. `PLAN.md` 를 읽고 불변 기준선·sprint 분할표·검증 지표를 파악한다. +2. `PROGRESS.md` 를 읽고 현재 sprint·다음 액션·blocker 를 파악한다. +3. `docs/plan.md`, `docs/research.md` 를 필요 시 참조한다. +4. 위 3개 파일이 없거나 비어있으면 사용자에게 알리고 멈춘다 (스스로 만들지 않는다). + +## 책임 + +- 사용자나 상위 에이전트가 sprint id 를 지정하면, 해당 sprint 의 **contract 파일** `docs/contracts/S-contract.md` 를 작성한다. +- Contract 는 다음 구조를 따른다: + ```markdown + # Sprint S Contract + Status: pending | in-progress | passed | failed + Module: A|B|C|... + Dependencies: + ## Success criteria (numbered, testable) + 1. ... + 2. ... + ## Verification method + - 어떤 스크립트/명령으로 측정하는지 + - 어떤 데이터셋·라벨로 측정하는지 + ## Out of scope + - ... + ## Required artifacts + - 코드 경로, 산출 파일 경로 + ``` +- Contract 작성 후 PROGRESS.md 의 해당 sprint 행에 contract 링크를 갱신한다. +- Sprint 간 의존성·병렬화 가능성을 평가하고 PROGRESS.md `Next Action` 을 업데이트한다. + +## 하지 말 것 + +- 코드를 작성하지 않는다. +- Generator 의 구현 방법을 micro-manage 하지 않는다 (성공 조건만 명시, "어떻게" 는 builder 가 결정). +- PLAN.md 의 불변 기준선(아키텍처 결정)을 임의로 수정하지 않는다 — 수정 필요 시 사용자에게 명시 승인을 받고서만. + +## 출력 포맷 + +작업이 끝나면 다음을 보고한다: +- 생성한 contract 파일 경로 +- 변경한 PROGRESS.md 항목 +- 다음 호출 권장 (예: "이제 `pole-detector-builder` 를 호출하시오") diff --git a/.claude/agents/pole-detector-builder.md b/.claude/agents/pole-detector-builder.md new file mode 100644 index 0000000..2fb16fd --- /dev/null +++ b/.claude/agents/pole-detector-builder.md @@ -0,0 +1,46 @@ +--- +name: pole-detector-builder +description: RailPose3D Module A (전철주 4-keypoint pose detection)의 Generator 에이전트. RTMPose-m/ViTPose 학습·추론·copy-paste augmentation·Grounding-DINO+SAM2 zero-shot 부트스트랩을 구현. Module A 관련 sprint(S1 부분, S2, S6의 A 부분, S8) 에서 호출. +model: inherit +tools: Read, Write, Edit, Glob, Grep, Bash +color: green +--- + +너는 RailPose3D **Module A (전철주 keypoint detection)** 의 Generator 다. + +## 시작 시 필수 절차 + +1. `CLAUDE.md`, `PLAN.md`, `PROGRESS.md` 를 읽는다. +2. 현재 sprint 가 Module A 관련인지 확인한다. 아니면 사용자에게 확인한다. +3. 해당 sprint 의 contract 파일 `docs/contracts/S-contract.md` 를 읽고 성공 조건을 파악한다. +4. 작업 시작 시 PROGRESS.md 의 sprint 상태를 🔄 in-progress 로 갱신, Activity Log 에 한 줄 추가. + +## 기술 스택 (CLAUDE.md 와 일치 필수) + +- **모델**: RTMPose-m (primary) → ViTPose-Base (fallback). YOLO11-pose 도 옵션. +- **Keypoint schema**: COCO-keypoints 포맷, 4점 `{base, top, left_crossarm_tip, right_crossarm_tip}` per pole. +- **Bootstrap**: Grounding-DINO 1.5 (`"utility pole"`, `"전봇대"`) → SAM 2.1 box prompt → 마스크 PCA → base 후보. +- **Augmentation**: Albumentations + copy-paste (SAM2 mask 기반, keypoint 보존 변환). +- **Self-training**: SfM 카메라 포즈 reprojection 으로 pseudo-label 생성, reprojection error > 5px outlier 제거. + +## 절대 하지 말 것 + +- bbox 기반 detector 로 base 추정 (CLAUDE.md §2 위반). +- 30장으로 backbone full retrain (frozen backbone 우선). +- BlenderProc 합성 데이터만으로 학습. + +## 작업 산출물 + +- `src/detection/pole_keypoint.py` — 추론 wrapper +- `src/detection/mvp_groundingdino_sam2.py` — zero-label MVP +- `src/detection/augment_copy_paste.py` — keypoint-preserving aug +- `configs/rtmpose_pole_4kpt.py` — MMPose 설정 +- `tests/test_pole_keypoint.py` — unit + golden image +- 평가 결과: `data/eval/pole_pck.json` (PCK@5px on holdout) + +## 종료 시 필수 절차 + +1. 모든 contract 성공 조건에 대해 측정 결과를 contract 파일에 채운다 (✓/✗ + 수치). +2. PROGRESS.md sprint 상태판 갱신 (✅ done 또는 ⛔ blocked). +3. Activity Log 에 결과 한 줄 추가. +4. 평가는 **수행하지 않는다** — `module-evaluator` 호출을 사용자에게 권유한다. diff --git a/.claude/agents/rail-detector-builder.md b/.claude/agents/rail-detector-builder.md new file mode 100644 index 0000000..3590fac --- /dev/null +++ b/.claude/agents/rail-detector-builder.md @@ -0,0 +1,39 @@ +--- +name: rail-detector-builder +description: RailPose3D Module B (레일 segmentation + polyline)의 Generator 에이전트. SegFormer-B2 3-stage transfer (RailSem19 → UAV-RSOD → 30장), skeletonize, RDP 단순화, DeepLSD sub-pixel refinement 구현. Module B 관련 sprint (S1 부분, S3, S6의 B 부분) 에서 호출. +model: inherit +tools: Read, Write, Edit, Glob, Grep, Bash +color: green +--- + +너는 RailPose3D **Module B (레일 segmentation)** 의 Generator 다. + +## 시작 시 필수 절차 + +1. `CLAUDE.md`, `PLAN.md`, `PROGRESS.md`, 해당 sprint 의 contract 를 읽는다. +2. PROGRESS.md 의 sprint 상태를 🔄 in-progress 로 갱신. + +## 기술 스택 + +- **모델**: SegFormer-B2 (primary). 대안: NL-LinkNet-SSR (Drones MDPI 2024 published). +- **3-stage transfer**: RailSem19 사전학습 → UAV-RSOD fine-tune → 사용자 30장 fine-tune. +- **Bootstrap**: Grounded-SAM 2 (텍스트: `"railway track"`, `"steel rail"`) → 30장 수작업 보정 → 학습 시드. +- **Polyline 추출**: `skimage.morphology.skeletonize` → connected component split → RDP 단순화 (eps 1–2px). +- **선택적 sub-pixel refine**: DeepLSD attraction field. + +## 절대 하지 말 것 + +- LaneATT/CLRNet 등 lane detector 직접 적용 (전방 차량 시점 가정). +- Sat2Graph/RoadTracer 그래프 모델 사용 (이 규모 부적합). +- HAWP/LETR/M-LSD wireframe (실내 Manhattan-world). + +## 산출물 + +- `src/detection/rail_segment.py` +- `configs/segformer_rail.yaml` +- `tests/test_rail_segment.py` +- 평가 결과: `data/eval/rail_iou.json` (mIoU + Hausdorff) + +## 종료 시 필수 절차 + +위 `pole-detector-builder` 와 동일. 평가는 module-evaluator 위임. diff --git a/.claude/agents/triangulation-builder.md b/.claude/agents/triangulation-builder.md new file mode 100644 index 0000000..d33a7fc --- /dev/null +++ b/.claude/agents/triangulation-builder.md @@ -0,0 +1,44 @@ +--- +name: triangulation-builder +description: RailPose3D Module C (2D→3D triangulation)의 Generator 에이전트. SfM 카메라 포즈 로드, DBSCAN-in-world-XY pole association, pycolmap triangulate, pyceres bundle adjustment, PDAL CSF ground 추출, Open3D raycasting, scipy splprep B-spline rail fitting 구현. Sprint S4·S5·S7 에서 호출. +model: inherit +tools: Read, Write, Edit, Glob, Grep, Bash +color: green +--- + +너는 RailPose3D **Module C (2D→3D)** 의 Generator 다. + +## 시작 시 필수 절차 + +표준 절차 (CLAUDE.md/PLAN.md/PROGRESS.md/contract 읽기, sprint 상태 갱신). + +## 기술 스택 + +| 단계 | 도구 | +|---|---| +| SfM I/O | `pycolmap` (Metashape XML 은 `sfm_io.py` 에서 변환) | +| Pole association | sklearn `DBSCAN` (eps≈0.5m, min_samples=3) in world XY | +| Pole triangulation | `pycolmap.triangulate_point` + RANSAC | +| Pole BA | `pyceres` (Huber loss, **pose-fixed**) | +| Ground 분류 | PDAL `filters.csf` | +| Ray-ground 교차 | Open3D `RaycastingScene` | +| Rail curve fit | `scipy.interpolate.splprep` 3D B-spline | +| NURBS export | `geomdl` | +| CAD export | `ezdxf` | +| GeoJSON CRS | EPSG:5186 (Korea Central Belt 2010); 로컬 frame 시 GCP 3점 7-param Helmert | + +## 핵심 알고리즘 (CLAUDE.md 준수) + +1. **Pole association (어려운 부분)**: appearance descriptor 사용 금지 (포철주는 동일 모양 50m 간격). 대신 ray ↔ ground 교차로 월드좌표 후보를 만들고 DBSCAN 클러스터. +2. **Pole BA**: pose 고정 (joint pose+point BA 는 detection noise 가 pose 를 오염). +3. **Rail**: per-view polyline 을 3–5px 샘플 → ground surface 에 raycast → 호장 정렬 → spline fit → (선택) pyceres control-point refinement. + +## 산출물 + +- `src/triangulation/sfm_io.py`, `ground.py`, `poles.py`, `rails.py` +- `src/export/geojson_dxf.py` +- `tests/test_triangulation.py` (synthetic 데이터로 검증) + +## 종료 시 절차 + +표준 (PROGRESS 갱신, evaluator 위임). diff --git a/.claude/hooks/session_briefing.sh b/.claude/hooks/session_briefing.sh new file mode 100644 index 0000000..2e61d54 --- /dev/null +++ b/.claude/hooks/session_briefing.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +# RailPose3D SessionStart hook: PLAN.md / PROGRESS.md 핵심을 컨텍스트에 주입. +# stdin = 이벤트 JSON, stdout = JSON {"hookSpecificOutput": {"additionalContext": "..."}} + +set -e + +PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}" +PLAN="$PROJECT_DIR/PLAN.md" +PROG="$PROJECT_DIR/PROGRESS.md" + +# 파일 부재 시 안전 종료 +if [ ! -f "$PLAN" ] || [ ! -f "$PROG" ]; then + printf '{"hookSpecificOutput":{"additionalContext":"[RailPose3D] PLAN.md 또는 PROGRESS.md 누락. /start 가 없으므로 사용자에게 셋업 진행 여부를 확인할 것."}}' + exit 0 +fi + +# Current Sprint + Next Action + Blockers 섹션만 추출 (간단한 grep 기반) +SUMMARY=$(awk ' + /^## Current Sprint/ {f=1; print; next} + /^## Next Action/ {f=1; print; next} + /^## Blockers/ {f=1; print; next} + /^## / {f=0} + f {print} +' "$PROG" | head -60) + +# 다이렉트 echo 가 한국어/유니코드 안전하지 않으므로 jq 가 있으면 사용, 없으면 escape +if command -v jq >/dev/null 2>&1; then + jq -nc --arg ctx "[RailPose3D] 세션 시작. 첫 작업 전 PLAN.md + PROGRESS.md 를 읽고 작업 위치를 명시할 것. + +현재 PROGRESS 요약: +$SUMMARY + +권장 첫 명령: /start" '{hookSpecificOutput:{additionalContext:$ctx}}' +else + # jq 없을 때 fallback — 큰따옴표/개행 escape + ESCAPED=$(printf '%s' "$SUMMARY" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' | awk '{printf "%s\\n", $0}') + printf '{"hookSpecificOutput":{"additionalContext":"[RailPose3D] 세션 시작. PLAN.md + PROGRESS.md 우선 확인. 권장 첫 명령: /start\\n\\n%s"}}' "$ESCAPED" +fi diff --git a/.claude/hooks/stop_progress_reminder.sh b/.claude/hooks/stop_progress_reminder.sh new file mode 100644 index 0000000..88978c2 --- /dev/null +++ b/.claude/hooks/stop_progress_reminder.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# RailPose3D Stop hook: 세션 종료 시 PROGRESS.md 갱신 여부 점검. +# PROGRESS.md 의 mtime 이 세션 시작 이전이면 사용자에게 한 줄 알림. + +set -e + +PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}" +PROG="$PROJECT_DIR/PROGRESS.md" + +[ ! -f "$PROG" ] && exit 0 + +# 최근 1시간 안에 변경되었는지 검사 (간단 판정). +NOW=$(date +%s) +MTIME=$(stat -c %Y "$PROG" 2>/dev/null || stat -f %m "$PROG" 2>/dev/null || echo 0) +DIFF=$(( NOW - MTIME )) + +if [ "$DIFF" -gt 3600 ]; then + printf '{"systemMessage":"[RailPose3D] PROGRESS.md 가 1시간 이상 갱신되지 않았습니다. 세션 종료 전 /progress 또는 /handoff 호출을 고려하세요."}' +fi + +exit 0 diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..2b43613 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,60 @@ +{ + "permissions": { + "allow": [ + "mcp__gitea__get_me", + "mcp__gitea__issue_write" + ] + }, + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "bash ./.claude/hooks/session_briefing.sh", + "timeout": 5 + } + ] + } + ], + "UserPromptSubmit": [ + { + "hooks": [ + { + "type": "command", + "command": "t=$(mktemp);cat>\"$t\";e=./.claude/hooks/token-usage/claude-hook.exe;[ -x \"$e\" ] && \"$e\" session-context \"$t\";rm -f \"$t\"", + "timeout": 5 + } + ] + } + ], + "Stop": [ + { + "hooks": [ + { + "type": "command", + "command": "t=$(mktemp);cat>\"$t\";e=./.claude/hooks/token-usage/claude-hook.exe;[ -x \"$e\" ] && \"$e\" stop-record \"$t\";rm -f \"$t\"", + "timeout": 5 + }, + { + "type": "command", + "command": "bash ./.claude/hooks/stop_progress_reminder.sh", + "timeout": 3 + } + ] + } + ], + "PostToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "t=$(mktemp);cat>\"$t\";e=./.claude/hooks/token-usage/claude-hook.exe;[ -x \"$e\" ] && \"$e\" aptabase-commit \"$t\";rm -f \"$t\"", + "timeout": 15 + } + ] + } + ] + } +} diff --git a/.claude/skills/contract/SKILL.md b/.claude/skills/contract/SKILL.md new file mode 100644 index 0000000..dace111 --- /dev/null +++ b/.claude/skills/contract/SKILL.md @@ -0,0 +1,18 @@ +--- +name: contract +description: 특정 sprint의 contract 파일만 새로 작성하거나 갱신. plan-architect 에이전트에 위임. /sprint와 달리 PROGRESS.md 의 sprint 상태는 변경하지 않음(planning-only). +argument-hint: +allowed-tools: Read, Write, Glob, Agent +--- + +대상 sprint: `$ARGUMENTS` + +1. `PLAN.md` 의 sprint 분할표에 `$ARGUMENTS` 가 있는지 확인. +2. 기존 `docs/contracts/$ARGUMENTS-contract.md` 가 있으면 사용자에게 덮어쓸지 묻는다. +3. **`plan-architect`** Agent 도구로 호출: + ``` + Sprint $ARGUMENTS 의 contract 만 docs/contracts/$ARGUMENTS-contract.md 에 + 작성/갱신하라. PROGRESS.md sprint 상태판은 건드리지 말고, contract 링크만 + 해당 행의 Contract 칸에 갱신하라. + ``` +4. 결과 (생성/갱신된 contract 경로) 보고. diff --git a/.claude/skills/eval/SKILL.md b/.claude/skills/eval/SKILL.md new file mode 100644 index 0000000..f1af588 --- /dev/null +++ b/.claude/skills/eval/SKILL.md @@ -0,0 +1,22 @@ +--- +name: eval +description: RailPose3D 모듈 평가. module-evaluator 에이전트에 위임해 sprint contract의 성공 조건을 정량 측정. argument로 sprint id 또는 module letter (A|B|C) 를 받는다. +argument-hint: +allowed-tools: Read, Glob, Agent +--- + +평가 대상: `$ARGUMENTS` + +1. 입력이 sprint id (S0~S8) 면 해당 sprint 의 contract 파일을 평가 대상으로 한다. +2. 입력이 module letter (A|B|C) 면 PROGRESS.md 에서 해당 모듈의 가장 최근 in-progress 또는 막 완료된 sprint 의 contract 를 평가 대상으로 한다. +3. **`module-evaluator` 서브에이전트** 를 Agent 도구로 호출한다. 프롬프트: + + ``` + 대상 sprint: <확정한 sprint id> + contract: docs/contracts/-contract.md + + 각 success criterion 을 측정하고 contract 파일과 PROGRESS.md 를 갱신하라. + ``` + +4. evaluator 결과를 사용자에게 요약 (passed criteria 수 / 전체, fail 사유) 으로 보여준다. +5. fail 시 → 어떤 builder 를 재호출할지 권장. pass 시 → 다음 sprint 진입 권장. diff --git a/.claude/skills/handoff/SKILL.md b/.claude/skills/handoff/SKILL.md new file mode 100644 index 0000000..06835c5 --- /dev/null +++ b/.claude/skills/handoff/SKILL.md @@ -0,0 +1,41 @@ +--- +name: handoff +description: RailPose3D context-reset 핸드오프 스냅샷 생성. 컨텍스트 윈도우 압박 또는 세션 종료 직전에 호출. docs/handoffs/YYYYMMDD-HHMM.md 에 현재 sprint·열린 의문·미완 작업·다음 행동을 기록해 새 세션이 파일 한 개만 읽고 이어갈 수 있게 한다. +argument-hint: [optional summary] +allowed-tools: Read, Write, Bash +--- + +선택 메모: `$ARGUMENTS` + +1. `PLAN.md`, `PROGRESS.md`, 현재 in-progress sprint 의 contract 파일을 읽는다. +2. 현재 시각으로 파일명 결정: `docs/handoffs/.md` (Bash `date +%Y%m%d-%H%M` 사용). +3. 다음 템플릿으로 핸드오프 파일 작성: + + ```markdown + # Handoff — + + ## 한 줄 요약 + <현재 sprint, 마지막에 한 일, 다음 한 줄> + + ## 진행 중 sprint + - id, contract 경로, 현재 상태 + - 끝낸 criterion 번호 / 남은 번호 + - 마지막 측정 수치 (있으면) + + ## 열린 의문 / blocker + - … + + ## 다음 세션이 가장 먼저 할 일 + 1. /start + 2. <구체적 다음 호출> + 3. … + + ## 환경 메모 + - 활성 conda env, GPU 가용성, 현재 데이터셋 위치 등 (알면 기록) + + ## 사용자 메모 + $ARGUMENTS + ``` + +4. PROGRESS.md Activity Log 에 "handoff written: <파일명>" 한 줄 append. +5. 사용자에게 파일 경로를 보여주고 "다음 세션 첫 명령은 `/start` 후 이 핸드오프 파일을 읽도록 하세요" 안내. diff --git a/.claude/skills/progress/SKILL.md b/.claude/skills/progress/SKILL.md new file mode 100644 index 0000000..ffb0c5f --- /dev/null +++ b/.claude/skills/progress/SKILL.md @@ -0,0 +1,23 @@ +--- +name: progress +description: RailPose3D PROGRESS.md를 갱신. 현재 sprint 상태 변경, Activity Log 한 줄 추가, 다음 액션 갱신. 인자 없이 실행하면 현재 상태와 갱신 후보를 제안. +argument-hint: [optional note] +allowed-tools: Read, Edit, Write +--- + +요청 메시지: `$ARGUMENTS` (없으면 현재 상태 점검) + +1. `PROGRESS.md` 를 읽는다. +2. 인자가 있으면 Activity Log 에 한 줄 추가: + ``` + | <오늘 날짜 YYYY-MM-DD> | <에이전트/사용자 이름> | $ARGUMENTS | <결과 요약> | + ``` +3. PLAN.md 의 sprint 표와 PROGRESS.md 의 sprint 상태판을 비교해 누락/불일치를 찾는다. +4. 사용자에게 다음을 묻는다 (한 번에 한 sprint 씩): + - 현재 in-progress sprint 가 완료되었나? + - 새 sprint 로 진입할 준비가 되었나? + - blocker 가 해결되었나? +5. 응답에 따라 PROGRESS.md 를 Edit 으로 갱신. +6. 갱신 후 `Last updated` 날짜를 오늘로 변경. + +**규칙**: PLAN.md 는 절대 수정하지 않는다. PLAN.md 변경이 필요하면 사용자에게 명시 승인을 요구. diff --git a/.claude/skills/sprint/SKILL.md b/.claude/skills/sprint/SKILL.md new file mode 100644 index 0000000..2304fa2 --- /dev/null +++ b/.claude/skills/sprint/SKILL.md @@ -0,0 +1,24 @@ +--- +name: sprint +description: RailPose3D 새 sprint를 시작한다. plan-architect 에이전트에 위임해 contract 파일을 생성하고 PROGRESS.md를 갱신. argument로 sprint id (S0~S8) 를 받는다. +argument-hint: +allowed-tools: Read, Write, Edit, Glob, Agent +--- + +요청된 sprint id: `$ARGUMENTS` + +다음 절차를 수행한다. + +1. `PLAN.md` 의 Sprint 분할표에서 해당 id 행이 존재하는지 확인한다. 없으면 사용자에게 알리고 멈춘다. +2. `PROGRESS.md` 를 읽고 의존성 sprint 가 완료되었는지(✅) 확인한다. 미완 의존성 발견 시 경고하고 진행 여부를 사용자에게 묻는다. +3. **`plan-architect` 서브에이전트** 를 Agent 도구로 호출한다. 프롬프트는: + + ``` + Sprint $ARGUMENTS 의 contract 파일을 docs/contracts/$ARGUMENTS-contract.md 에 + 작성하라. PLAN.md 의 해당 sprint 행과 검증 기준 섹션을 근거로 번호 매긴 + testable success criteria 를 작성하고, verification method 와 required + artifacts 를 명시하라. 작업 후 PROGRESS.md 의 sprint 행을 갱신하라. + ``` + +4. plan-architect 가 보고한 contract 파일 경로와 갱신된 PROGRESS.md 항목을 사용자에게 보여준다. +5. 다음 단계 권장 (어떤 builder 를 호출할지) 을 한 줄 출력한다. diff --git a/.claude/skills/start/SKILL.md b/.claude/skills/start/SKILL.md new file mode 100644 index 0000000..355fb68 --- /dev/null +++ b/.claude/skills/start/SKILL.md @@ -0,0 +1,37 @@ +--- +name: start +description: RailPose3D 세션 진입점. PLAN.md와 PROGRESS.md를 읽어 현재 sprint·다음 액션·blocker를 한 화면 요약으로 보여준다. 새 세션이나 복귀 직후 항상 가장 먼저 실행한다. +allowed-tools: Read, Glob +--- + +당신은 지금 RailPose3D 프로젝트 세션을 시작했다. 다음 절차를 그대로 수행한다. + +1. `CLAUDE.md`, `PLAN.md`, `PROGRESS.md` 를 차례로 읽는다. +2. 다음 형식으로 한 화면 브리핑을 출력한다 (섹션 헤더 한국어 유지): + +``` +# RailPose3D — 현재 상태 + +## 현재 sprint + + +## Sprint 보드 (요약) +| Sprint | 상태 | +| --- | --- | +| ...PLAN.md + PROGRESS.md 머지... | + +## 다음 액션 + + +## Blockers + + +## 권장 다음 호출 +- /sprint ← 현재 sprint 시작 시 +- /contract ← contract 만 새로 만들 때 +- ← in-progress sprint 의 generator 직접 호출 +- /eval ← builder 작업 직후 +- /progress ← 작업 끝나고 PROGRESS 갱신 +``` + +3. 사용자 입력을 기다린다. 자의적으로 작업을 시작하지 않는다. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b3478b0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# Data and outputs (large, gitignored per CLAUDE.md §5) +data/ +*.pth +*.ckpt +*.onnx + +# Local usage tracking +.usage/ + +# Python +__pycache__/ +*.py[cod] +*.egg-info/ +.venv/ +.uv/ +build/ +dist/ + +# Editor / OS +.idea/ +.vscode/ +.DS_Store +Thumbs.db + +# Claude Code local +.claude/CLAUDE.local.md +.claude/settings.local.json + +# Large prebuilt binary in this repo (if accidentally added) +claude_token_usage(1).exe diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..ed77ce2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,114 @@ +# CLAUDE.md — RailPose3D + +> **프로젝트 명**: **RailPose3D** — 드론 영상에서 전철주·레일 등 **긴 물체를 검출**하고 SfM 카메라 포즈로 **3D 좌표화**하는 R&D 프로젝트. + +## 0. 모든 에이전트 첫 행동 (필수) + +세션이나 서브에이전트가 시작되면 **반드시** 다음을 먼저 수행한다: + +1. [PLAN.md](PLAN.md) 를 읽는다 — 불변 기준선·sprint 분할·검증 지표. +2. [PROGRESS.md](PROGRESS.md) 를 읽는다 — 현재 sprint, 다음 액션, blocker. +3. 자신의 역할이 어느 sprint·module 에 속하는지 PROGRESS.md 의 *Current Sprint* 섹션과 대조해 명시한 뒤 작업을 시작한다. + +작업이 끝나면 **반드시** PROGRESS.md 의 Sprint 상태판 + Activity Log 를 갱신한다. + +## 1. Goal (요약) + +- **입력**: 드론 nadir + oblique 영상, 그리고 SfM(COLMAP/Metashape)이 산출한 카메라 포즈와 sparse/dense cloud (사용자 보유). +- **출력**: 전철주 base 의 3D point + 레일의 3D polyline (EPSG:5186 GeoJSON / DXF). +- **제약**: 라벨 30장만 보유 (zero-shot/transfer/self-training 우선), Windows 환경, 한국 catenary 도메인. + +## 2. Architecture (3-모듈, 불변) + +| Module | 책임 | 채택 | +|---|---|---| +| **A** Pole keypoint | 전철주 4-keypoint `{base, top, L_arm, R_arm}` | RTMPose-m / ViTPose | +| **B** Rail seg | 레일 binary mask → skeleton → polyline | SegFormer-B2 (3-stage transfer) | +| **C** 2D→3D | 다중뷰 삼각측량으로 3D 좌표 산출 | DBSCAN + pycolmap + pyceres / PDAL CSF + Open3D + scipy | + +자세한 근거는 [docs/plan.md](docs/plan.md) 참조. + +### 절대 하지 않는 것 + +- bbox 기반 detector 로 base 추정 (oblique view 에서 완금이 base 로 오인됨 — 기하 문제) +- SfM dense mesh 에서 직접 객체 추출 (가는 물체에서 fragment) +- 30장으로 큰 모델 from-scratch 학습 +- Lane detector(CLRNet 등) 를 nadir 드론에 그대로 적용 +- BlenderProc 합성 데이터에만 의존 + +## 3. Harness 설계 원칙 (Anthropic engineering blog 기반) + +이 프로젝트는 **Planner / Generator / Evaluator** 3-역할 분리 하네스로 운영한다. + +- **Planner = `plan-architect` 에이전트** — sprint 단위로 분해, contract(번호 매긴 성공 조건) 작성. +- **Generator = 모듈별 builder 에이전트** — `pole-detector-builder`, `rail-detector-builder`, `triangulation-builder`, `data-pipeline-builder`. +- **Evaluator = `module-evaluator` 에이전트** — PCK / mIoU / reprojection error 등 정량 지표로 contract pass/fail 판정. + +원칙: +- **Decomposition** — 한 sprint 에 한 모듈·한 feature 만. +- **File-based handoff** — 대화 턴이 아니라 PLAN.md / PROGRESS.md / `docs/contracts/*.md` 로 인계. +- **Context reset over compaction** — 컨텍스트 압박 시 `/handoff` 로 상태 스냅샷 후 새 세션 시작. +- **Evaluator tuning loop** — evaluator 의 판단이 사용자와 어긋나면 evaluator 프롬프트를 갱신. +- **Iterative simplification** — 모델 개선 시 하네스 컴포넌트가 여전히 load-bearing 인지 재검증. + +## 4. 운영 워크플로 + +``` +[user] → /start # PLAN+PROGRESS 자동 로드·요약 + → /sprint # plan-architect → contract 생성 + → builder agent 작업 # generator + → /eval # module-evaluator → pass/fail + → /progress # PROGRESS.md 갱신 + ↳ context 부족 시 /handoff 후 새 세션 +``` + +## 5. 디렉터리 규약 + +``` +detectelectronpole/ +├── CLAUDE.md ← 이 파일 +├── PLAN.md ← 불변 기준선 +├── PROGRESS.md ← 현재 상태 (자주 갱신) +├── README.md +├── docs/ +│ ├── plan.md ← 사용자 승인 상세 plan +│ ├── research.md +│ ├── contracts/ ← sprint contract 파일 (S-contract.md) +│ └── handoffs/ ← context 핸드오프 스냅샷 +├── data/ ← 드론 원본·SfM·라벨 (gitignored) +├── configs/ ← MMPose, SegFormer 등 설정 +├── src/ +│ ├── detection/ ← 모듈 A, B +│ ├── triangulation/ ← 모듈 C +│ ├── self_training/ +│ └── export/ ← GeoJSON/DXF +├── pipeline/ +│ └── run_full.py ← end-to-end orchestrator +├── tests/ +└── .claude/ + ├── agents/ ← 서브에이전트 + ├── skills/ ← /start, /sprint, /eval, /progress, /handoff, /contract + ├── commands/ ← (legacy mirror, skills 와 동일 내용) + └── settings.json ← hooks +``` + +## 6. 코딩·환경 규약 + +- Python 3.11+, `uv` 또는 conda 환경. +- 라이브러리: `pycolmap`, `pyceres`, `open3d`, `pdal`, `mmpose`, `transformers`(SegFormer), `albumentations`, `scipy`, `geomdl`, `ezdxf`. +- 타입 힌트 필수, `ruff` + `pytest`. +- 모든 detection 출력은 **COCO-keypoints** (pole) / **GeoJSON-like polyline** (rail) 통합 포맷으로 직렬화. +- 좌표계: 기본 EPSG:5186 (Korea Central Belt 2010), 로컬 frame 인 경우 GCP 3점으로 변환. + +## 7. Memory & Imports + +- 자동 메모리: `~/.claude/projects/d--MYCLAUDE-PROJECT-detectelectronpole/memory/` +- 사용자 글로벌 지침: `@~/.claude/CLAUDE.md` + +## 8. 응답 언어 + +기본 한국어. 기술 용어·라이브러리·식별자는 영어 그대로. 코드 주석은 영어. + +--- + +이 파일을 읽은 에이전트는 **반드시** PLAN.md + PROGRESS.md 로 이어서 진행할 것. diff --git a/PLAN.md b/PLAN.md new file mode 100644 index 0000000..832d280 --- /dev/null +++ b/PLAN.md @@ -0,0 +1,59 @@ +# RailPose3D — PLAN + +> 이 문서는 **불변 기준선(baseline)** 이다. 일상 진행 상태는 [PROGRESS.md](PROGRESS.md) 에 기록한다. +> 자세한 배경·근거는 [docs/plan.md](docs/plan.md) (사용자 승인 plan), [docs/research.md](docs/research.md) 참조. + +## Goal + +드론 영상(nadir + oblique) 으로부터 **전철주(catenary pole)** 와 **레일(rail)** 등 가늘고 긴 구조물을 검출하고, 기존 SfM 카메라 포즈를 활용해 **3D 좌표(EPSG:5186)** 로 추출한다. + +## 핵심 설계 결정 (불변) + +1. **메시는 버린다.** SfM dense mesh 의 fragmented 출력에 의존하지 않는다. 카메라 포즈 + ground 분류 dense cloud 만 사용. +2. **2D 정확 검출 → 3D 삼각측량.** 가는 물체는 detection-then-triangulate 가 mesh 기반보다 정확. +3. **Pose/keypoint 모델 (bbox 아님).** 4-keypoint `{base, top, L_arm, R_arm}` 로 base/완금 구조적 구분. +4. **Segmentation + skeletonize (lane detector 아님).** 레일은 nadir/oblique view-invariant. +5. **Self-training over SfM.** 30장 라벨을 카메라 포즈로 5–20× 증식. + +## 3-모듈 분해 + +- **Module A — Pole Keypoint Detection** (RTMPose-m, 4-keypoint) +- **Module B — Rail Segmentation** (SegFormer-B2, 3-stage transfer + skeletonize) +- **Module C — 2D→3D Triangulation** (DBSCAN association + pycolmap + pyceres / PDAL CSF + Open3D + scipy splprep) + +## Sprint 분할 (마일스톤) + +| Sprint | 모듈 | 목표 | 성공 조건 (contract) | +|---|---|---|---| +| **S0** Bootstrap | Infra | 디렉터리·환경·CI 골격 | `pipeline/run_full.py --version` 동작, lint/format pass | +| **S1** MVP-Zero | A+B | Grounding-DINO + SAM2 zero-label baseline | 30장 검증, pole base 픽셀 오차·rail mIoU 정량화 | +| **S2** Pole-A1 | A | RTMPose-m fine-tune (30장 → 300장 copy-paste aug) | PCK@5px ≥ MVP baseline + 20% | +| **S3** Rail-B1 | B | SegFormer 3-stage transfer | rail mIoU ≥ 0.70 on 30 holdout | +| **S4** Triangulate | C | DBSCAN + pycolmap + pyceres pole pipeline | 3D pole point reprojection error < 5 px median | +| **S5** Rail-3D | C | Open3D raycasting + B-spline rail pipeline | 3D rail Hausdorff vs GCP < 10 cm | +| **S6** Self-train | A,B | SfM-consistent self-training loop | 라운드 N+1 PCK > 라운드 N | +| **S7** E2E | All | 100m trial section end-to-end | GeoJSON + DXF export, QGIS 시각화 일치 | +| **S8** Korean catenary 분기 | A | 단주 vs 문형 가구 분류 + 4kpt 변형 | 두 타입에서 PCK@5px ≥ 0.85 | + +각 sprint 는 시작 시 `/contract ` 로 contract 파일을 생성하고, 완료 시 `/eval ` 로 검증한다. + +## 검증 기준 (정량 지표) + +- **Module A**: PCK@5px (base keypoint), 30장 holdout +- **Module B**: mIoU (rail mask), Hausdorff (polyline) +- **Module C**: reprojection error (pole), Hausdorff vs GCP (rail), GeoJSON CRS=EPSG:5186 통과 + +## 위험 관리 + +| 위험 | 대응 | +|---|---| +| 전철주 base 가림 | self-training 으로 다중뷰 일관성 검증 | +| Windows pycolmap/pyceres 빌드 | conda-forge wheel 우선, 실패 시 WSL2 fallback | +| 좌표계 mismatch | GCP 3점 이상으로 7-param Helmert | +| 한국 catenary 다양성 (단주/문형) | S8에서 분기 처리 | + +## 참고 산출물 + +- [docs/plan.md](docs/plan.md) — 사용자 승인 plan (한국어, 자세한 근거) +- [docs/research.md](docs/research.md) — 초기 기술 조사 +- 메모리: `~/.claude/projects/d--MYCLAUDE-PROJECT-detectelectronpole/memory/` diff --git a/PROGRESS.md b/PROGRESS.md new file mode 100644 index 0000000..f0f66b1 --- /dev/null +++ b/PROGRESS.md @@ -0,0 +1,57 @@ +# RailPose3D — PROGRESS + +> 이 문서는 **현재 상태**를 추적한다. 모든 에이전트는 작업 시작 시 [PLAN.md](PLAN.md) 와 이 파일을 읽고, 종료 시 이 파일을 갱신한다. + +마지막 업데이트: 2026-04-27 (초기 셋업) + +## Current Sprint + +`S0 — Bootstrap (infra)` — **NOT STARTED** + +## Sprint 상태판 + +| Sprint | 상태 | Contract | 시작일 | 완료일 | 결과 | +|---|---|---|---|---|---| +| S0 Bootstrap | 🔲 todo | — | — | — | — | +| S1 MVP-Zero | 🔲 todo | — | — | — | — | +| S2 Pole-A1 | 🔲 todo | — | — | — | — | +| S3 Rail-B1 | 🔲 todo | — | — | — | — | +| S4 Triangulate | 🔲 todo | — | — | — | — | +| S5 Rail-3D | 🔲 todo | — | — | — | — | +| S6 Self-train | 🔲 todo | — | — | — | — | +| S7 E2E | 🔲 todo | — | — | — | — | +| S8 Korean variants | 🔲 todo | — | — | — | — | + +상태 표기: 🔲 todo / 🔄 in-progress / ✅ done / ⛔ blocked + +## Next Action + +**Sprint S0 Bootstrap** 시작. 작업 항목: + +1. 프로젝트 디렉터리 골격 생성 (`src/`, `configs/`, `data/`, `pipeline/`) +2. `pyproject.toml` + 의존성 (uv 또는 conda env) +3. pre-commit / ruff / pytest 셋업 +4. `pipeline/run_full.py` placeholder +5. README 갱신 (RailPose3D 명명 반영) + +→ 시작하려면 `/sprint S0` 또는 `plan-architect` 에이전트 호출. + +## Blockers / Open Questions + +- [ ] 사용자에게서 실제 드론 영상 + Metashape export 확보 시점 미확정 +- [ ] GCP (Ground Control Points) 좌표 데이터 가용성 확인 필요 +- [ ] pycolmap/pyceres 윈도우 빌드 가능 여부 — conda-forge에 최신 wheel 있는지 확인 필요 + +## Activity Log + +| 일시 | 에이전트/사용자 | 액션 | 결과 | +|---|---|---|---| +| 2026-04-27 | user | 프로젝트 RailPose3D 명명, harness 구조 셋업 지시 | CLAUDE.md, PLAN.md, PROGRESS.md, .claude/* 생성 | + +--- + +**규약**: +- Sprint 시작 시 → 상태 🔄, 시작일 기록, contract 파일 링크. +- Sprint 완료 시 → 상태 ✅, 완료일·결과(metric 수치 포함) 기록. +- Blocker 발생 시 → 상태 ⛔, Blockers 섹션에 이유 기록. +- 모든 의미있는 액션은 Activity Log에 한 줄 추가. diff --git a/README.md b/README.md index 66f087b..299d883 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ # detectelectronpole +전철주와 레일과 같은 긴 물체를 감지하는 연구. \ No newline at end of file diff --git a/docs/plan.md b/docs/plan.md new file mode 100644 index 0000000..d56cff2 --- /dev/null +++ b/docs/plan.md @@ -0,0 +1,243 @@ +# 드론 영상 기반 전철주·레일 검출 → 3D 좌표 추출 파이프라인 계획 + +## Context (배경 및 문제 정의) + +사용자 목표: 드론 정사영상(orthomosaic)과 드론 동영상/사진으로부터 **전철주(catenary pole)** 와 **레일(rail)** 같은 가늘고 긴 구조물을 검출하고, 그 3D 좌표를 추출하는 것. + +이미 갖추어진 것: +- **SfM/Photogrammetry 파이프라인** (COLMAP/Metashape)으로 카메라 포즈와 sparse/dense cloud 생성 가능 +- 라벨링 30장 (검증/시드용으로 활용) + +현재 막혀있는 핵심 문제 4가지: +1. **전철주 하단(base, 지면 접점) 좌표를 정확히 잡지 못함** — 3D 좌표화의 기준점인데, 카메라가 oblique(비스듬히)일 때 **완금(crossarm, T자 가로팔)이 영상상 가장 아래쪽에 보이는 화소**가 되어, bbox 기반 detector가 완금을 base로 오인. +2. **라벨링 비용** — 30장만 보유. 일반적인 supervised 학습으론 불충분. +3. **SfM 메시 품질** — 가는 수직물·금속 레일은 텍스처가 부족해 dense reconstruction이 너덜너덜하게 깨짐. 메시에서 직접 객체 추출 불가. +4. **레일은 bbox로 표현 불가** — 화면을 가로지르는 긴 곡선이므로 segmentation/polyline이 자연스러운 표현. + +## 핵심 전략 + +**"메시에서 객체를 추출하지 말고, 2D 영상에서 정확히 검출한 뒤 SfM 카메라 포즈로 삼각측량하여 3D 좌표를 만든다."** SfM의 강점(카메라 포즈)만 쓰고 약점(가는 물체 dense recon)은 우회한다. + +세 모듈로 나누어 구축: +- **(A) 전철주 base 검출** — 4-keypoint pose estimation으로 *구조적*으로 base/완금 구분 +- **(B) 레일 polyline 검출** — Segmentation + skeletonization (사전학습된 레일 모델에서 transfer) +- **(C) 2D → 3D 통합** — 다중뷰 삼각측량 + DBSCAN 군집 + bundle adjustment + +전체 파이프라인은 **이미 운영 중인 SfM 출력에 detection 모듈만 끼워넣는 형태**라 최소 침습. + +## 모듈 A: 전철주 base 검출 + +### 핵심 통찰 +**완금이 base로 오인되는 문제는 통계적 문제가 아니라 기하학적 문제다.** Bounding box의 "가장 아래쪽 픽셀"은 oblique 시점에서 완금일 수 있으므로, 모델이 학습량과 무관하게 못 푼다. → **Pose/keypoint 모델로 "이 점은 base, 저 점은 완금 끝"을 의미적으로 구분**해야 풀린다. + +### 채택 모델: RTMPose-m (or YOLO11-pose) + 4-keypoint 스켈레톤 +- **스켈레톤**: `{base, top, left_crossarm_tip, right_crossarm_tip}` (각 전철주당 4점) +- **이유**: 사람 포즈 추정에서 "왼발/오른발"을 헷갈리지 않게 만든 것과 정확히 같은 트릭. base 채널은 오직 지면 접점에서만 supervise 되므로 완금과 구조적으로 구분됨. +- **Repo**: https://github.com/open-mmlab/mmpose (RTMPose), https://github.com/ultralytics/ultralytics (YOLO11-pose) +- **fallback**: ViTPose-Base (frozen DINOv2 backbone) — 30장 같은 극소 데이터에서 더 강함 + +### 라벨 부족(30장) 대응 — 4단계 부트스트랩 +1. **Day 1–3 (라벨 0개로 시작)**: Grounding-DINO 1.5 (`"utility pole"`, `"전봇대"`) → SAM 2.1 box prompt → 마스크의 PCA 주축의 아래쪽 끝 → base 후보. 30장에 대해 픽셀 오차 측정 = baseline. + - Repos: https://github.com/IDEA-Research/Grounding-DINO-1.5-API , https://github.com/facebookresearch/sam2 +2. **Day 4–5**: MVP 출력 중 깨끗한 것을 RTMPose 학습용 부트스트랩 라벨로 사용. +3. **Week 2+**: Albumentations + **copy-paste augmentation** (SAM2 마스크로 전철주를 잘라 다양한 배경에 붙이기, keypoint도 함께 변환). 5–10배 데이터 증강. +4. **Week 3+ (가장 큰 레버리지)**: **SfM-consistent self-training 루프** + - 한 뷰의 base detection을 SfM 포즈로 다른 뷰에 reprojection → pseudo-label + - reprojection error > 5px이면 outlier로 제거 (완금을 base로 잘못 잡으면 자동으로 걸러짐 — 다른 뷰의 정상 detection들과 3D상으로 일관되지 않으므로) + - 참고: EpipolarPose (CVPR'19), Pixel-Perfect SfM (ICCV'21) https://github.com/cvg/pixel-perfect-sfm + +### 하지 말아야 할 것 +- bbox 기반 YOLO/Faster-RCNN으로 base 추정 (위 기하 문제로 실패) +- BlenderProc 합성 데이터에만 의존 (sim-to-real gap) +- 30장으로 처음부터 큰 모델 full retrain + +## 모듈 B: 레일 polyline 검출 + +### 채택 모델: SegFormer-B2 + 3단계 transfer learning + skeletonize +- **이유**: 레일은 bbox 부적합. Segmentation은 nadir(평행선)/oblique(소실점 곡선) 모두 view-invariant하게 처리 가능. SegFormer-B2는 hierarchical attention으로 가늘고 긴 구조의 long-range continuity를 잘 잡으며, 라벨 효율이 좋음. +- **3단계 fine-tune**: RailSem19 (8,500 시퀀스, 65% IoU 사전학습 가능) → **UAV-RSOD** (드론 시점 레일 데이터셋, 가장 직접적 도메인) → 사용자의 30장 +- **Repos / Datasets**: + - SegFormer: https://github.com/NVlabs/SegFormer (HF: https://huggingface.co/blog/fine-tune-segformer) + - RailSem19: https://wilddash.cc/railsem19 + - UAV-RSOD: https://www.nature.com/articles/s41597-024-03952-3 + - 대안 아키텍처: NL-LinkNet-SSR (Drones MDPI 2024, 드론 레일 전용 F1=0.749) https://www.mdpi.com/2504-446X/8/11/611 + +### Polyline 추출 후처리 +1. 이진 마스크 (rail / not-rail) +2. **`skimage.morphology.skeletonize`** → 1px 중심선 +3. Connected component split → 각 레일 분리 (instance ID) +4. **Ramer-Douglas-Peucker** 단순화 (eps ~1–2px) → 정렬된 polyline +5. **선택적 sub-pixel refinement**: DeepLSD attraction field로 skeleton을 sub-pixel로 snap (https://github.com/cvg/DeepLSD) + +### MVP (week 1) +**Grounded-SAM 2** (텍스트 프롬프트 `"railway track"`, `"steel rail"`) → 30장 수작업 보정 → SegFormer fine-tune → 나머지 자동 라벨 → 반복(active learning loop). + +### 하지 말아야 할 것 +- LaneATT/CLRNet 등 레인 검출 모델 직접 적용 (전방 차량 시점 가정 — drone nadir에 부적합) +- Sat2Graph/RoadTracer 그래프 모델 (이 규모/감독 형태 아님) +- HAWP/LETR/M-LSD wireframe 검출기 (실내 Manhattan-world용) + +## 모듈 C: 2D → 3D 통합 + +### C-1. 전철주 (point 객체) + +| 단계 | 도구 | 비고 | +|---|---|---| +| 다중뷰 cross-view association | **DBSCAN in world XY** | 핵심 트릭 | +| Triangulation 초기화 | `pycolmap.triangulate_point` | https://github.com/colmap/pycolmap | +| Outlier 제거 | RANSAC + Huber loss | | +| Pose-fixed bundle adjustment | `pyceres` | https://github.com/cvg/pyceres | + +**Cross-view association 핵심 (어려운 부분)**: 전철주는 약 50m 간격으로 모양이 똑같아서 DINOv2 같은 appearance descriptor로는 구분 불가. → **기하 기반 군집화**: +1. 각 뷰의 2D base detection을 ray로 back-project +2. SfM dense cloud의 **로컬 ground plane**과 ray 교차 → 월드좌표 후보점 +3. **DBSCAN(eps≈0.5m, min_samples=3)** 으로 월드 XY 클러스터링 → 한 클러스터 = 한 물리적 전철주 +4. 클러스터 내 detection들로 pycolmap triangulate + +### C-2. 레일 (curve 객체) + +| 단계 | 도구 | +|---|---| +| 지면 추출 | **PDAL CSF (Cloth Simulation Filter)** https://pdal.io/en/latest/stages/filters.csf.html | +| Ray-ground 교차 | **Open3D `RaycastingScene`** | +| 곡선 fitting | `scipy.interpolate.splprep` (3D B-spline) | +| NURBS 정밀화 | `geomdl` https://github.com/orbingol/NURBS-Python | +| Bundle adjustment | `pyceres` (control points 변수화) | + +**파이프라인**: +1. SfM dense cloud → CSF로 ground 분류 → 로컬 표면 메시 +2. 각 뷰의 2D rail polyline을 3–5px 간격으로 샘플링 +3. 카메라 ray가 ground 표면과 교차하는 월드좌표 모음 +4. 모든 뷰의 월드좌표를 모아 호장(arc length)으로 정렬 → 3D B-spline fit +5. (선택) pyceres로 control point를 변수로 두고 모든 뷰의 2D polyline에 대한 reprojection 오차 최소화 + +### C-3. SfM dense step 교체 여부 판단 + +**결론: 교체하지 않는다.** Gaussian Splatting / NeRF는 photometric loss 기반이라 가는 물체 문제를 근본적으로 풀지 못함. 측정 정확도는 detection→triangulate가 더 강함. 단, **시각화 산출물**용으로만 2DGS(https://github.com/hbb1/2d-gaussian-splatting) 옵션으로 추가 가능. + +**SfM에서 활용**: 카메라 포즈 + intrinsics, sparse cloud (sanity check), **ground 분류된 dense cloud만** 사용. 메시는 버린다. + +## 전체 파이프라인 다이어그램 + +``` +드론 영상 (oblique + nadir) + │ + ▼ +[Stage 0] COLMAP / Metashape (기존) + → 카메라 [R|t], K, dense cloud + │ + ┌────┼────────────────────────┐ + ▼ ▼ ▼ +[Stage 1a] [Stage 1b] [Stage 1c] +PDAL CSF Pole 4-kpt detection Rail seg + polyline +ground (RTMPose) (SegFormer + skel) + │ │ │ + └──────┬───────┘ │ + ▼ │ +[Stage 2] Pole association │ + - ray → ground 교차 │ + - DBSCAN in world XY │ + │ │ + ▼ │ +[Stage 3] Pole triangulation │ + - pycolmap + Huber + pyceres BA │ + → 3D pole base point │ + ▼ + [Stage 4] Rail reconstruction + - Open3D raycasting (per sample) + - scipy splprep B-spline + - pyceres spline BA + → 3D rail centerline + │ │ + └──────────────┬──────────────────┘ + ▼ +[Stage 5] Output: GeoJSON/Shapefile (EPSG:5186) + DXF (ezdxf) +``` + +## 권장 디렉터리 구조 (신규 생성할 파일들) + +이미 존재하는 파일은 `docs/research.md` 와 `README.md` 뿐. 새로 만들 핵심 파일: + +``` +detectelectronpole/ +├── docs/ +│ ├── research.md # 기존 +│ └── plan.md # 이 계획의 한국어 사본 +├── data/ +│ ├── images/ # 드론 원본 +│ ├── sfm/ # COLMAP DB / Metashape export +│ └── labels/ +│ └── poles_4kpt.json # COCO-keypoints 포맷, 4-keypoint 스키마 +├── configs/ +│ ├── rtmpose_pole_4kpt.py # MMPose 설정 +│ └── segformer_rail.yaml # SegFormer fine-tune 설정 +├── src/ +│ ├── detection/ +│ │ ├── mvp_groundingdino_sam2.py # zero-label MVP (week 1) +│ │ ├── pole_keypoint.py # RTMPose 추론 wrapper +│ │ ├── rail_segment.py # SegFormer 추론 + skeletonize +│ │ └── augment_copy_paste.py # 키포인트 보존 copy-paste +│ ├── triangulation/ +│ │ ├── sfm_io.py # pycolmap / Metashape XML 로더 +│ │ ├── ground.py # PDAL CSF + Open3D RaycastingScene +│ │ ├── poles.py # DBSCAN association + pycolmap + pyceres +│ │ └── rails.py # raycasting + splprep + pyceres +│ ├── self_training/ +│ │ └── sfm_self_training.py # multi-view pseudo-label loop +│ └── export/ +│ └── geojson_dxf.py # 산출물 export +└── pipeline/ + └── run_full.py # end-to-end orchestrator +``` + +## 단계별 마일스톤 + +### Phase 1 (Week 1) — Zero-label MVP +- [ ] `mvp_groundingdino_sam2.py`: Grounding-DINO 1.5 + SAM 2.1로 30장 이미지에서 pole mask + base 후보 추출 +- [ ] PCA 주축 lower endpoint를 30장의 GT와 비교해 픽셀 오차 baseline 측정 +- [ ] Grounded-SAM 2로 같은 방법으로 rail 마스크 baseline 측정 +- **목적**: 라벨 0개로 어디까지 가는지 정량화. 실패 모드 카탈로그 작성. + +### Phase 2 (Week 2–3) — Detection 학습 +- [ ] 4-keypoint COCO-keypoints 포맷으로 30장 재라벨링 (point 4개 클릭이라 비용 낮음) +- [ ] copy-paste augmentation으로 ~300장으로 증강 +- [ ] RTMPose-m fine-tune (frozen backbone) +- [ ] SegFormer-B2를 RailSem19 → UAV-RSOD → 30장 3-stage transfer +- [ ] 두 모델 평가 (PCK, mIoU) + +### Phase 3 (Week 4) — 2D → 3D 통합 +- [ ] `sfm_io.py`로 Metashape/COLMAP 출력 로드 +- [ ] `ground.py`로 PDAL CSF 지면 추출 + Open3D RaycastingScene 구축 +- [ ] `poles.py`: DBSCAN association + pycolmap triangulate + pyceres BA → 3D pole points +- [ ] `rails.py`: ray-ground 교차 + B-spline fit → 3D rail polylines +- [ ] EPSG:5186 (한국 중부원점) GeoJSON export + +### Phase 4 (Week 5+) — Self-training 루프 +- [ ] `sfm_self_training.py`: 한 라운드 = 검출 → 삼각측량 → reprojection → pseudo-label → fine-tune +- [ ] reprojection error 임계값 5px로 outlier 자동 제거 +- [ ] 라운드별 mAP/PCK 추적 +- [ ] 수렴까지 반복 (보통 3–5 라운드) + +## 검증 방법 (Verification) + +1. **모듈 A 단독**: 30장 GT 대비 base keypoint **PCK@5px** 측정. MVP baseline → RTMPose baseline → self-training 라운드별 비교. +2. **모듈 B 단독**: 30장 GT 대비 rail mask **mIoU** 및 polyline **Hausdorff distance** 측정. +3. **모듈 C 단독**: Phase 1의 raw 검출 결과를 `triangulation/poles.py`에 넣어 3D pole 좌표를 GeoJSON으로 export. 측량 기준점(GCP) 또는 KORAIL 인벤토리와 비교 (없으면 두 측량 비행을 따로 돌려 일관성 검증). +4. **End-to-end**: 작은 trial section (예: 100m 구간, ~3 전철주 + 2 레일)에서 전체 파이프라인 1회 실행. 산출물 시각화: QGIS에서 GeoJSON + 정사영상 오버레이. +5. **회귀 테스트**: `pipeline/run_full.py --section trial_100m` 단일 명령으로 재현 가능해야 함. + +## 위험 요소 및 대응 + +| 위험 | 대응 | +|---|---| +| 전철주 base가 ballast/식생에 가려짐 | self-training 루프가 다른 뷰의 가시 base와 일관성 검증 → 평균 offset 보정 | +| SfM ground cloud 밀도 부족 (<50 pts/m²) | 비행 고도/오버랩 재조정, 또는 SuGaR/2DGS로 보강 (시각화 한정) | +| 한국 catenary 마스트 종류 다양 (단주 vs 문형) | 4-keypoint 스킴을 `pole_type` 클래스 분기 (단일 마스트 / 문형 가구)로 확장 | +| 좌표계 (local vs EPSG:5186) | GCP 3점 이상으로 7-parameter Helmert 변환 | +| 라벨 시드(30장)가 한 카메라 각도/조명 편향 | 첫 self-training 라운드에서 다양성 강제 (multi-view 클러스터당 최소 1장 샘플링) | + +## 핵심 참조 (전부 April 2026 기준 상용 가능) + +- **Pole detection**: MMPose RTMPose, SAM 2.1, Grounding-DINO 1.5/1.6, Pixel-Perfect SfM +- **Rail detection**: SegFormer, RailSem19, UAV-RSOD, NL-LinkNet-SSR, DeepLSD +- **3D 통합**: pycolmap, pyceres, PDAL, Open3D, scipy.interpolate, geomdl, ezdxf diff --git a/docs/research.md b/docs/research.md new file mode 100644 index 0000000..e9abb24 --- /dev/null +++ b/docs/research.md @@ -0,0 +1,114 @@ +2026년 4월 기준, 수직 또는 수평으로 긴 가는 물체(레일, 전철주 등) 탐지에 가장 유리한 기술은 '고정밀 3D 센싱 + 최신 딥러닝 모델'의 융합입니다. + +단순히 하나의 기술만이 아닌, 물체의 형태와 탐지 환경에 따라 최적의 기술 조합이 다르게 적용되고 있습니다. 특히 2026년 들어 발표된 연구들을 통해 기존 기술의 한계를 극복하는 획기적인 발전이 있었습니다. + +🛤️ 수평으로 긴 물체 (레일) 탐지 기술 +레일과 같이 지면에 깔려 수평으로 길게 뻗은 물체는 미세한 균열부터 단차까지 정밀하게 측정하는 것이 핵심입니다. + +3D 레이저 삼각측량 + 초고해상도 AI 분석: 미국 연방철도청(FRA)의 2026년 4월 보고서에 따르면, 이 기술은 레일의 표면 형상을 3차원으로 스캔하고, 초고해상도 분석이 가능한 YOLO26과 같은 최신 AI 모델을 통해 레일의 건강 상태를 수치화된 지수(TCHI)로 평가합니다. 이는 기존 레일 스캐닝 기술의 정밀도를 한 단계 끌어올린 것으로 평가됩니다. + +초음파를 활용한 비접촉 고속 탐지: 영국 워릭 대학교 팀은 EMAT라는 비접촉 센서를 이용해 초음파를 발생시켜 시속 80km가 넘는 고속 주행 중에도 레일의 표면 균열을 실시간으로 찾아낼 수 있는 기술을 선보였습니다. + +AI 기반 종합 모니터링 시스템: 인도 철도국이 도입한 통합 선로 모니터링 시스템(ITMS) 는 카메라와 레이저 센서로 수집한 이미지를 AI가 분석해 레일, 침목, 체결 장치의 결함을 종합적으로 판단합니다. + +⚡️ 수직으로 긴 물체 (전철주, 가는 기둥) 탐지 기술 +전철주처럼 지면에서 수직으로 솟은 구조물은 기둥과 그에 부착된 부품(완금, 애자 등)을 세밀하게 파악하는 것이 중요합니다. + +AI 기반 3D 모델링 자동화: 미국의 Looq AI사가 개발한 qPole은 드론이나 차량으로 촬영한 이미지를 AI로 분석해 전철주의 높이, 직경, 재질, 부속 자재 정보를 자동으로 추출하고, 오차 1cm 미만의 정밀한 CAD 모델을 생성합니다. + +LiDAR + 수직 정보 복원 기술: 자율주행 기술에서 발전한 LiDAR 센서는 정밀한 3D 포인트 클라우드 데이터를 제공합니다. 특히 2026년 새로 발표된 SVP 및 3DPillars 기술은 기존 LiDAR 방식의 약점이었던 '수직 정보 손실' 문제를 해결하여 가로등, 전봇대 등 가늘고 긴 물체 탐지 정확도를 크게 향상시켰습니다. + +🤔 어떤 기술을 선택해야 할까요? +구체적인 상황에 따라 권장되는 기술은 다음과 같습니다. + +감지 대상 권장 기술 핵심 이점 실제 적용 사례 +레일 (Rail) 3D 레이저 스캐닝 + YOLO26 AI 모델 미세한 표면 균열 및 단차를 높은 정밀도로 측정 및 분석 가능 고속철도 선로 검측, 도시철도 유지보수 +레일 표면 결함 초음파(EMAT) 비접촉 탐지 기존 방식 대비 매우 빠른 속도(80km/h 이상)로 레일 표면 결함 탐지 가능 일반 열차를 활용한 상시 선로 모니터링 +전철주 (Utility Pole) AI 기반 이미지 분석 (qPole 등) 1cm 미만 정밀도의 3차원 모델 자동 생성, 부속 자재 및 결함 종합 진단 한전, 미국 주요 전력사 등의 전력 시설물 관리 +전철주 및 가는 기둥 LiDAR + SVP / 3DPillars 기술 가느다란 기둥 형태의 3차원 공간 정보를 빠짐없이 포착, 탐지 정확도 향상 자율주행차량, 자율주행 지게차, 로봇의 장애물 인지 +선택 시 고려해야 할 추가 사항은 아래와 같습니다. + +탐지 환경: 주·야간, 안개 등 기상 조건에 민감하지 않은 LiDAR 또는 초음파 방식이 유리하며, AI 영상 분석은 경제성이 좋지만 기상 영향이 큽니다. + +정밀도: 1cm 미만의 초정밀 측정이 필요하면 3D 레이저 삼각측량, 밀리미터 단위의 균열 탐지에는 초음파 기술이 적합합니다. + +처리 속도: 실시간 탐지가 중요하다면 YOLO26과 같이 최적화된 AI 모델이나 3DPillars와 같은 고속 LiDAR 기술이 필수적입니다. + +결론적으로, 2026년 현재 가장 효과적인 전략은 센서로 정밀한 3D 데이터를 수집하고, 이를 수직 정보 복원이 강화된 최신 AI 모델로 분석하는 '3D 센싱-딥러닝 통합 기술' 입니다. + +궁금하신 점이나 더 자세한 정보가 필요하시면 언제든지 물어봐 주세요. + +USA: Looq AI has launched qPole, an AI-powered solution designed to make the power grid more reliable and resilient by understanding utility poles the way experienced engineers do. The system analyses physical structure, geometry, materials, and real-world context to generate engineering-ready models of each pole. + +With an estimated 180–200 million distribution poles in the US, many ageing or in extreme weather zones, utilities face growing pressure to maintain reliability and comply with wildfire and storm-hardening mandates. Traditional pole inspections are slow, labour-intensive, and sometimes hazardous, taking around 30 minutes per pole when combining fieldwork and office processing. + +qPole streamlines this workflow, reducing the total time to just seven minutes per pole. Field capture drops from 15 minutes to about two, while AI-assisted office processing takes only five minutes. The system automatically detects pole components such as crossarms and transformers, producing precise CAD models that identify current or potential failures and recommend remediation. + +“The AI captures a pole’s ‘DNA’– its height, diameter, material and attachments – just like a human engineer,” said Dominique Meyer, CEO of Looq AI. “This saves an estimated 19 million work hours annually in the US, allowing utilities to focus on grid hardening rather than manual data entry.” + +Field measurements are accurate to under a centimetre, preventing unnecessary replacements and optimising construction planning. Industry experts say qPole represents a major step forward in asset modelling, safety, and operational efficiency, offering utilities a practical, reliable tool to modernise their distribution network. + +Source: T&D World + +위의 내용을 추적해서 논문이나 프로그램 등 적용이 가능한가? +네, 제공해 주신 qPole 관련 기사 내용은 실제로 검증 가능한 제품이며 논문이나 프로그램 수준에서 충분히 적용 가능한 기술입니다. + +### 핵심 요약 + +| 구분 | 내용 | +| :--- | :--- | +| **제품 상태** | 2026년 2월 Distributech 전시회에서 정식 발표된 상용 제품 | +| **기술 방식** | 핸드헬드 장비로 촬영한 이미지를 AI가 분석하는 **Photogrammetry(사진 측량)** 기반 | +| **출력 결과** | SPIDAcalc, O-Calc, PLS-CADD 등 전주 구조 해석 툴에 바로 입력 가능한 **CAD 모델** | +| **실제 적용** | EPI(Engineering Partners, Inc.)가 SCE(Southern California Edison) 프로젝트에 이미 활용 | + + +### 1. 실제 사용 가능한 제품인가? + +**네, 실제로 검증이 완료된 상용 제품입니다.** + +- **발표일**: 2026년 2월, 미국 샌디에이고에서 열린 **Distributech 2026**에서 정식 발표되었습니다 . +- **제공사**: **Looq AI**社가 개발했으며, 미국 전력사들을 대상으로 이미 판매 및 적용이 시작된 상태입니다 . +- **기술 핵심**: 기존에는 '전주 데이터 수집에 현장 15분 + 사무실 검증 15분 = 총 30분'이 걸렸다면, qPole은 **현장 촬영 2분 + AI 자동 처리 5분 = 총 7분**으로 단축했다고 설명합니다 . + + +### 2. 논문이나 공개된 자료로 구현 가능한가? + +구현 가능성과 관련해서는 다음 두 가지 측면에서 접근할 수 있습니다. + +#### A. 상용 솔루션 직접 도입 (B2B) + +- **적용 대상**: 전력 공사, 엔지니어링 회사, 대규모 설비 관리 기관 +- **방법**: Looq AI에 **기업 문의**하여 컨설팅, 장비 도입, 파일럿 프로젝트 진행 +- **장점**: 발표된 성능(1cm 미만 정확도, 7분 처리)을 바로 활용 가능 +- **단점**: 비용 부담 및 기술 종속성 + +#### B. 유사 기술 자체 개발 (R&D) + +- **적용 대상**: 대학 연구실, AI 스타트업, 공공 연구 기관 +- **핵심 기술 구성**: + 1. **데이터 수집**: 스마트폰 또는 일반 카메라로 전주를 여러 각도에서 촬영 + 2. **3D 복원(Photogrammetry)**: COLMAP, Meshroom, 혹은 OpenCV의 Structure from Motion(SfM) 라이브러리 활용 + 3. **객체 탐지 & 분할**: YOLOv11 이상 버전 또는 Meta의 SAM(Segment Anything Model)을 fine-tuning하여 전주, 완금, 변압기 등을 탐지 + 4. **CAD 변환**: 탐지된 3D 모델에서 치수 추출 및 CAD 형식(DXF, STEP 등)으로 자동 변환하는 파이프라인 구축 +- **한계점**: + - **상용 제품 대비 성능 격차**: Looq AI의 '지구물리학적 추론(Geophysical Reasoning)' 엔진은 수년간 축적된 특화 데이터로 학습된 것으로, 공개 모델만으로 동일한 정확도(1cm 미만)를 내기는 어렵습니다. + - **전문 도메인 지식 부재**: 전주 설계 기준(NESC 등)과 연계한 '자동 결함 탐지' 및 '보수 방안 추천' 기능은 상당한 엔지니어링 지식이 필요합니다. + +### 3. 실제 적용 사례 + +2025년 11월, 엔지니어링 업체 **EPI**는 **남부 캘리포니아 에디슨(SCE)** 의 전주 데이터를 수집하는 프로젝트에서 qPole을 활용했습니다 . + +- **현장 촬영**: 단 2시간 만에 3D 스캔 완료 +- **데이터 전달**: 촬영 후 단 **24시간** 만에 구조 해석용 데이터를 SCE 엔지니어에게 전달 + +이는 기존에는 몇 주가 걸리던 작업을 하루 만에 끝낸 사례로, qPole 기술이 현장에서 실질적으로 통용될 수 있음을 보여줍니다. + +### 결론 및 제안 + +- **전력사, 대형 엔지니어링 업체** → **Looq AI (qPole) 도입 검토** + - ROI(투자 대비 효율)가 명확하고, 최신 기술을 안정적으로 활용할 수 있는 경로입니다. + +- **연구/개발 목적** → **오픈소스 기반 유사 시스템 개발** + - Photogrammetry(SfM) + 최신 Segmentation AI 모델을 결합한 Proof-of-Concept은 충분히 가능합니다. + - 관심 있으시면, 제가 위의 핵심 기술 구성 요소별로 더 자세한 구현 가이드라인을 제공해 드릴 수 있습니다. \ No newline at end of file