sam31server 전환, 라멘 파이프라인 정리, 문서 추가
- sam31server를 SAM3.1 서버로 전환 (x-anylabeling01 대체) - detect_raamen.py: B/C 분류 기반 라멘형 전철주 검출 파이프라인 정비 - sam3_everything_explore.py: Discovery Sweep 탐색 모드 정리 - detect_all_objects.py: 타일 검출 개선 - docs/railway-client-guide.html: 서버·도구·파이프라인 전체 가이드 추가 - tools 추가: detect_control_box, group_ramen_poles, render_everything_by_label, render_label_polygons, debug_vh Closes #1 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
664
docs/railway-client-guide.html
Normal file
664
docs/railway-client-guide.html
Normal file
@@ -0,0 +1,664 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Railway Client — 사용 가이드</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #0f1117; --bg2: #1a1d27; --bg3: #22263a;
|
||||
--border: #2e3354; --accent: #4f8ef7; --accent2: #7c3aed;
|
||||
--green: #22c55e; --yellow: #f59e0b; --red: #ef4444;
|
||||
--text: #e2e8f0; --muted: #8892a4; --code-bg: #12151f;
|
||||
--orange: #f97316;
|
||||
}
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body { background: var(--bg); color: var(--text); font-family: 'Segoe UI', system-ui, sans-serif; line-height: 1.6; }
|
||||
a { color: var(--accent); text-decoration: none; }
|
||||
|
||||
nav { background: var(--bg2); border-bottom: 1px solid var(--border); padding: 0 2rem; display: flex; align-items: center; gap: 2rem; height: 52px; position: sticky; top: 0; z-index: 100; }
|
||||
nav .brand { font-weight: 700; font-size: 1rem; color: var(--accent); letter-spacing: .05em; }
|
||||
nav a { color: var(--muted); font-size: .85rem; }
|
||||
nav a:hover { color: var(--text); }
|
||||
|
||||
.layout { display: flex; min-height: calc(100vh - 52px); }
|
||||
aside { width: 250px; flex-shrink: 0; background: var(--bg2); border-right: 1px solid var(--border); padding: 1.5rem 1rem; position: sticky; top: 52px; height: calc(100vh - 52px); overflow-y: auto; }
|
||||
aside h4 { font-size: .68rem; text-transform: uppercase; letter-spacing: .1em; color: var(--muted); margin-bottom: .6rem; margin-top: 1.2rem; }
|
||||
aside h4:first-child { margin-top: 0; }
|
||||
aside ul { list-style: none; }
|
||||
aside ul li a { display: block; padding: .28rem .6rem; border-radius: 5px; font-size: .82rem; color: var(--muted); }
|
||||
aside ul li a:hover { background: var(--bg3); color: var(--text); }
|
||||
aside ul li a.priority { color: var(--orange); font-weight: 600; }
|
||||
main { flex: 1; padding: 2.5rem 3rem; max-width: 980px; }
|
||||
|
||||
h1 { font-size: 1.9rem; font-weight: 700; margin-bottom: .4rem; }
|
||||
h2 { font-size: 1.3rem; font-weight: 600; margin: 2.5rem 0 1rem; padding-bottom: .4rem; border-bottom: 1px solid var(--border); color: var(--accent); }
|
||||
h2.priority-h2 { color: var(--orange); border-color: #7c3310; }
|
||||
h3 { font-size: 1rem; font-weight: 600; margin: 1.5rem 0 .6rem; color: #c4cfe8; }
|
||||
p { margin-bottom: .8rem; color: #c4cfe8; font-size: .93rem; }
|
||||
|
||||
.badge { display: inline-block; padding: .15rem .55rem; border-radius: 4px; font-size: .72rem; font-weight: 600; letter-spacing: .04em; margin-right: .3rem; }
|
||||
.badge-orange { background: #431407; color: #fed7aa; }
|
||||
.badge-blue { background: #1e3a6e; color: #93c5fd; }
|
||||
.badge-green { background: #14532d; color: #86efac; }
|
||||
|
||||
pre { background: var(--code-bg); border: 1px solid var(--border); border-radius: 8px; padding: 1rem 1.2rem; overflow-x: auto; margin: .8rem 0 1.2rem; font-size: .82rem; line-height: 1.7; }
|
||||
code { font-family: 'Cascadia Code', 'Fira Code', Consolas, monospace; }
|
||||
p code, li code, td code { background: var(--code-bg); border: 1px solid var(--border); border-radius: 3px; padding: .1em .4em; font-size: .82em; color: #a5b4fc; }
|
||||
|
||||
table { width: 100%; border-collapse: collapse; font-size: .85rem; margin: .8rem 0 1.4rem; }
|
||||
th { background: var(--bg3); color: var(--muted); text-align: left; padding: .55rem .8rem; font-weight: 600; font-size: .78rem; text-transform: uppercase; letter-spacing: .05em; border-bottom: 1px solid var(--border); }
|
||||
td { padding: .5rem .8rem; border-bottom: 1px solid #1e2235; vertical-align: top; }
|
||||
tr:hover td { background: #161929; }
|
||||
|
||||
.card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 1rem; margin: 1rem 0; }
|
||||
.card { background: var(--bg2); border: 1px solid var(--border); border-radius: 8px; padding: 1.1rem 1.2rem; }
|
||||
.card.highlight { border-color: var(--orange); }
|
||||
.card h4 { font-size: .9rem; font-weight: 600; margin-bottom: .4rem; }
|
||||
.card p { font-size: .82rem; color: var(--muted); margin: 0; }
|
||||
|
||||
.step { display: flex; gap: 1rem; margin-bottom: 1.5rem; }
|
||||
.step-num { flex-shrink: 0; width: 28px; height: 28px; background: var(--accent); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: .8rem; font-weight: 700; color: #fff; margin-top: .15rem; }
|
||||
.step-num.orange { background: var(--orange); }
|
||||
.step-body { flex: 1; }
|
||||
.step-body h4 { font-size: .92rem; font-weight: 600; margin-bottom: .3rem; }
|
||||
.step-body p { font-size: .85rem; color: var(--muted); margin: 0 0 .4rem; }
|
||||
|
||||
.dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: .4rem; }
|
||||
.dot-green { background: var(--green); }
|
||||
.swatch { display: inline-block; width: 14px; height: 14px; border-radius: 3px; vertical-align: middle; margin-right: .4rem; border: 1px solid rgba(255,255,255,.15); }
|
||||
|
||||
.alert { border-radius: 7px; padding: .8rem 1rem; margin: .8rem 0; font-size: .85rem; border-left: 3px solid; }
|
||||
.alert-info { background: #0c1a3a; border-color: var(--accent); color: #93c5fd; }
|
||||
.alert-warn { background: #2a1a00; border-color: var(--yellow); color: #fde68a; }
|
||||
.alert-ok { background: #0a2e1a; border-color: var(--green); color: #86efac; }
|
||||
.alert-pri { background: #2a1000; border-color: var(--orange); color: #fed7aa; }
|
||||
|
||||
.pipeline-row { display: flex; align-items: center; gap: .6rem; margin: 1rem 0; flex-wrap: wrap; }
|
||||
.pipe-box { background: var(--bg3); border: 1px solid var(--border); border-radius: 6px; padding: .5rem .9rem; font-size: .85rem; font-weight: 600; }
|
||||
.pipe-box.highlight { border-color: var(--orange); color: var(--orange); }
|
||||
.pipe-arrow { color: var(--muted); font-size: 1.1rem; }
|
||||
|
||||
.subtitle { color: var(--muted); font-size: .9rem; margin-bottom: 1.5rem; }
|
||||
hr { border: none; border-top: 1px solid var(--border); margin: 2rem 0; }
|
||||
ul.body-list { padding-left: 1.4rem; margin-bottom: .8rem; }
|
||||
ul.body-list li { font-size: .87rem; color: #c4cfe8; margin-bottom: .25rem; }
|
||||
|
||||
.cb-legend { display: flex; gap: 1.5rem; margin: .8rem 0; }
|
||||
.cb-item { display: flex; align-items: center; gap: .5rem; font-size: .87rem; }
|
||||
.cb-box { width: 32px; height: 32px; border-radius: 5px; display: flex; align-items: center; justify-content: center; font-weight: 800; font-size: 1rem; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav>
|
||||
<span class="brand">⚡ Railway Client</span>
|
||||
<a href="#raamen">🔴 라멘 검출</a>
|
||||
<a href="#everything">탐색 모드</a>
|
||||
<a href="#server">서버</a>
|
||||
<a href="#tools">전체 도구</a>
|
||||
<a href="#categories">카테고리</a>
|
||||
</nav>
|
||||
|
||||
<div class="layout">
|
||||
<aside>
|
||||
<h4>🔴 최우선 파이프라인</h4>
|
||||
<ul>
|
||||
<li><a href="#raamen" class="priority">라멘형 전철주 검출</a></li>
|
||||
<li><a href="#raamen-pipeline" class="priority">2단계 파이프라인</a></li>
|
||||
<li><a href="#raamen-cb" class="priority">B/C 분류 설명</a></li>
|
||||
<li><a href="#raamen-args">파라미터 상세</a></li>
|
||||
</ul>
|
||||
<h4>탐색 모드</h4>
|
||||
<ul>
|
||||
<li><a href="#everything">Everything 모드 개요</a></li>
|
||||
<li><a href="#everything-usage">사용법</a></li>
|
||||
<li><a href="#everything-prompt">Discovery Prompt</a></li>
|
||||
<li><a href="#everything-output">출력 해석</a></li>
|
||||
</ul>
|
||||
<h4>서버 (sam31server)</h4>
|
||||
<ul>
|
||||
<li><a href="#server-setup">설치 요구사항</a></li>
|
||||
<li><a href="#server-start">서버 시작</a></li>
|
||||
<li><a href="#server-api">API 엔드포인트</a></li>
|
||||
</ul>
|
||||
<h4>클라이언트 도구</h4>
|
||||
<ul>
|
||||
<li><a href="#tool-detect-all">detect_all_objects</a></li>
|
||||
<li><a href="#tool-autolabel">sam3_autolabel</a></li>
|
||||
<li><a href="#tool-batch">sam3_batch_label</a></li>
|
||||
<li><a href="#tool-yoloworld">yoloworld_sam3_pipeline</a></li>
|
||||
<li><a href="#tool-video">video_sam3_segment</a></li>
|
||||
<li><a href="#tool-render">렌더링 도구</a></li>
|
||||
</ul>
|
||||
<h4>설정</h4>
|
||||
<ul>
|
||||
<li><a href="#categories">카테고리 (railway_zone.json)</a></li>
|
||||
<li><a href="#prompts">프롬프트 파일</a></li>
|
||||
</ul>
|
||||
<h4>시스템</h4>
|
||||
<ul>
|
||||
<li><a href="#overview">아키텍처 개요</a></li>
|
||||
<li><a href="#quickstart">빠른 시작</a></li>
|
||||
<li><a href="#troubleshoot">트러블슈팅</a></li>
|
||||
</ul>
|
||||
</aside>
|
||||
|
||||
<main>
|
||||
|
||||
<!-- ══════════════════════════════════════════════
|
||||
1. 라멘형 전철주 검출 — 최우선
|
||||
══════════════════════════════════════════════ -->
|
||||
<section id="raamen">
|
||||
<h1>Railway Client 사용 가이드</h1>
|
||||
<p class="subtitle">드론 항공 이미지 기반 철도 시설물 자동 검출 파이프라인</p>
|
||||
|
||||
<div class="alert alert-ok">
|
||||
<strong>서버 상태:</strong> <span class="dot dot-green"></span>sam31server SAM 3.1 → localhost:8000 구동 중
|
||||
</div>
|
||||
|
||||
<h2 class="priority-h2" id="raamen">🔴 라멘형(門) 전철주 검출 파이프라인</h2>
|
||||
<span class="badge badge-orange">최우선 기능</span>
|
||||
|
||||
<p>드론 사선 촬영 이미지에서 門자형 라멘 구조 전철주를 검출한다. SAM3.1로 폴리곤을 추출한 후 소실점(Vanishing Point) 기반 기하학 분석으로 C(기둥)/B(빔)를 분류, 라멘 그룹을 판정한다.</p>
|
||||
|
||||
<div id="raamen-cb">
|
||||
<h3>B / C 접두어 — 분류 기준</h3>
|
||||
<div class="cb-legend">
|
||||
<div class="cb-item">
|
||||
<div class="cb-box" style="background:#1a3a6e; color:#93c5fd; border:2px solid #3b82f6;">C</div>
|
||||
<div>
|
||||
<strong>Column (기둥)</strong><br>
|
||||
<span style="color:var(--muted); font-size:.82rem;">소실점 방향으로 향하는 수직·사선 폴리곤.<br>각도 임계값(<code>--c-thresh</code>, 기본 20°) 이내.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cb-item">
|
||||
<div class="cb-box" style="background:#3a1a00; color:#fed7aa; border:2px solid #f97316;">B</div>
|
||||
<div>
|
||||
<strong>Beam (빔)</strong><br>
|
||||
<span style="color:var(--muted); font-size:.82rem;">수평으로 뻗은 폴리곤.<br>소실점 방향에서 크게 벗어난 것.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-info">
|
||||
라멘 구조 판정: <strong>B 1개 + 그 아래 C 2개 이상</strong> → 門자형 라멘으로 분류. 출력 이미지에 B/C 접두어 + 그룹 번호로 표시.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="raamen-pipeline">
|
||||
<h3>2단계 파이프라인</h3>
|
||||
<div class="pipeline-row">
|
||||
<div class="pipe-box">① detect_all_objects.py</div>
|
||||
<span class="pipe-arrow">→</span>
|
||||
<div class="pipe-box" style="color:var(--muted)">JSON annotation<br><small>(catenary_pole polygons)</small></div>
|
||||
<span class="pipe-arrow">→</span>
|
||||
<div class="pipe-box highlight">② detect_raamen.py</div>
|
||||
<span class="pipe-arrow">→</span>
|
||||
<div class="pipe-box" style="color:var(--muted)">B/C 분류<br>라멘 그룹 판정</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>Step 1 — 전철주 폴리곤 추출</h3>
|
||||
<pre><code>cd D:\MYCLAUDE_PROJECT\railway-client
|
||||
|
||||
.venv\Scripts\python.exe tools/detect_all_objects.py `
|
||||
--input "data/역사구간/1.회덕역/DJI_20260306100900_0034.JPG" `
|
||||
--categories configs/railway_zone.json `
|
||||
--tiles all `
|
||||
--cols 4 --rows 3 --overlap 0.10 --workers 2</code></pre>
|
||||
|
||||
<p>출력: <code>output/detect/DJI_20260306100900_0034.json</code> — X-AnyLabeling 호환 annotation</p>
|
||||
|
||||
<h3>Step 2 — 라멘 구조 분석</h3>
|
||||
<pre><code>.venv\Scripts\python.exe tools/detect_raamen.py `
|
||||
--image "data/역사구간/1.회덕역/DJI_20260306100900_0034.JPG" `
|
||||
--label "output/detect/DJI_20260306100900_0034.json" `
|
||||
--output "output/raamen/DJI_20260306100900_0034_raamen.png"</code></pre>
|
||||
|
||||
<p>출력: 이미지 위에 C/B 라벨 + 라멘 그룹 번호 오버레이</p>
|
||||
|
||||
<div id="raamen-args">
|
||||
<h3>detect_raamen.py 파라미터</h3>
|
||||
<table>
|
||||
<tr><th>파라미터</th><th>기본값</th><th>설명</th></tr>
|
||||
<tr><td><code>--image</code></td><td>필수</td><td>원본 이미지 경로</td></tr>
|
||||
<tr><td><code>--label</code></td><td>필수</td><td>JSON annotation 파일 (detect_all_objects 출력)</td></tr>
|
||||
<tr><td><code>--output</code></td><td>자동 생성</td><td>결과 이미지 저장 경로</td></tr>
|
||||
<tr><td><code>--class-names</code></td><td><code>catenary_pole</code></td><td>분석할 클래스 이름 (쉼표 구분)</td></tr>
|
||||
<tr><td><code>--class-ids</code></td><td>없음</td><td>클래스 ID로 필터링 (대안)</td></tr>
|
||||
<tr><td><code>--epsilon</code></td><td>4.0</td><td>폴리곤 단순화 강도 (높을수록 단순)</td></tr>
|
||||
<tr><td><code>--c-thresh</code></td><td>20.0°</td><td>C(기둥) 판정 각도 임계값</td></tr>
|
||||
<tr><td><code>--b-max-diff</code></td><td>75.0°</td><td>B(빔) 최대 각도 편차</td></tr>
|
||||
<tr><td><code>--margin</code></td><td>30px</td><td>근접성 그룹핑 거리</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h3>4단계 내부 처리 흐름</h3>
|
||||
<table>
|
||||
<tr><th>Phase</th><th>처리 내용</th></tr>
|
||||
<tr><td>Phase 1</td><td>폴리곤 단순화(approxPolyDP) + 소실점(Vanishing Point) 계산</td></tr>
|
||||
<tr><td>Phase 2</td><td>동적 C/B 분류 — 소실점 기반 기대 각도와 비교</td></tr>
|
||||
<tr><td>Phase 3</td><td>근접성 기반 그룹핑 — B 앵커 기준으로 아래 C 탐색</td></tr>
|
||||
<tr><td>Phase 4</td><td>라멘 구조 판정 + 가림(occlusion) 예외 처리</td></tr>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<!-- ══════════════════════════════════════════════
|
||||
2. Everything 탐색 모드
|
||||
══════════════════════════════════════════════ -->
|
||||
<section id="everything">
|
||||
<h2>Everything 탐색 모드 (Discovery Sweep)</h2>
|
||||
|
||||
<p>새 지역·새 구간을 처음 분석할 때 사용. SAM3.1 텍스트 grounding 방식이므로 완전 무프롬프트는 불가 — 대신 광범위한 <strong>Discovery Prompt</strong>로 이미지에 존재하는 모든 객체를 일괄 검출하고 라벨 빈도를 집계한다.</p>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<strong>용도:</strong> 어떤 객체가 이 이미지에 얼마나 나오는지 파악 → 이후 <code>detect_all_objects.py</code> 의 카테고리·conf 튜닝에 활용.
|
||||
</div>
|
||||
|
||||
<div id="everything-usage">
|
||||
<h3>사용법</h3>
|
||||
<pre><code>cd D:\MYCLAUDE_PROJECT\railway-client
|
||||
|
||||
# 기본 (8×6 타일)
|
||||
.venv\Scripts\python.exe tools/sam3_everything_explore.py `
|
||||
--input "data/역사구간/1.회덕역/DJI_20260306100900_0034.JPG"
|
||||
|
||||
# 타일 수 조정 (고해상도 드론 이미지)
|
||||
.venv\Scripts\python.exe tools/sam3_everything_explore.py `
|
||||
--input "data/역사구간/..." `
|
||||
--cols 8 --rows 6 --conf 0.10 --workers 4
|
||||
|
||||
# 특정 영역(ROI)만 탐색
|
||||
.venv\Scripts\python.exe tools/sam3_everything_explore.py `
|
||||
--input "..." `
|
||||
--zone 1000 500 3000 2500
|
||||
|
||||
# Discovery Prompt에 추가 키워드 삽입
|
||||
.venv\Scripts\python.exe tools/sam3_everything_explore.py `
|
||||
--input "..." `
|
||||
--prompt-extra "signal box, relay cabinet"</code></pre>
|
||||
</div>
|
||||
|
||||
<h3>파라미터</h3>
|
||||
<table>
|
||||
<tr><th>파라미터</th><th>기본값</th><th>설명</th></tr>
|
||||
<tr><td><code>--input</code></td><td>필수</td><td>이미지 파일 경로</td></tr>
|
||||
<tr><td><code>--cols</code></td><td>8</td><td>가로 타일 분할 수</td></tr>
|
||||
<tr><td><code>--rows</code></td><td>6</td><td>세로 타일 분할 수</td></tr>
|
||||
<tr><td><code>--overlap</code></td><td>0.10</td><td>타일 겹침 비율</td></tr>
|
||||
<tr><td><code>--conf</code></td><td>0.10</td><td>신뢰도 임계값 (탐색 모드라 낮게 설정)</td></tr>
|
||||
<tr><td><code>--workers</code></td><td>4</td><td>병렬 처리 스레드 수</td></tr>
|
||||
<tr><td><code>--nms</code></td><td>0.40</td><td>NMS IoU 임계값</td></tr>
|
||||
<tr><td><code>--zone</code></td><td>없음</td><td>ROI 좌표 (X1 Y1 X2 Y2) — 관심 영역만 처리</td></tr>
|
||||
<tr><td><code>--prompt-extra</code></td><td>없음</td><td>Discovery Prompt에 추가할 키워드</td></tr>
|
||||
</table>
|
||||
|
||||
<div id="everything-prompt">
|
||||
<h3>Discovery Prompt (내장)</h3>
|
||||
<p>다음 키워드 묶음이 자동으로 SAM3.1에 전달된다:</p>
|
||||
<pre><code>railroad track, railway rail,
|
||||
catenary pole, overhead line pole, electric pole,
|
||||
overhead wire, catenary wire, power line cable,
|
||||
railway sleeper, concrete tie,
|
||||
guardrail, highway barrier, road fence,
|
||||
bridge, viaduct, overpass,
|
||||
vegetation, tree, bush, grass,
|
||||
building, structure, roof, wall,
|
||||
vehicle, car, truck,
|
||||
road, asphalt, pavement,
|
||||
slope, embankment, retaining wall,
|
||||
noise barrier, sound wall,
|
||||
signal, sign board,
|
||||
small dark object on ballast, small square metal box on ground,
|
||||
control box on ballast, gray square lid on gravel,
|
||||
flat metal cover on ground, bright square object on gravel</code></pre>
|
||||
<p><code>--prompt-extra</code>로 추가 키워드를 붙일 수 있다.</p>
|
||||
</div>
|
||||
|
||||
<div id="everything-output">
|
||||
<h3>출력 해석</h3>
|
||||
<pre><code>output/everything/
|
||||
└── DJI_20260306100900_0034_everything.jpg ← 모든 segment 오버레이
|
||||
|
||||
콘솔 출력 (라벨 빈도 집계):
|
||||
catenary pole : 47
|
||||
railway rail : 31
|
||||
overhead wire : 28
|
||||
concrete tie : 19
|
||||
control box on ballast: 8
|
||||
...</code></pre>
|
||||
<div class="alert alert-info">
|
||||
<strong>활용:</strong> 빈도 상위 라벨 → <code>railway_zone.json</code>의 프롬프트에 추가하거나 conf 조정. 빈도 0인 카테고리 → 해당 이미지에 없는 객체.
|
||||
</div>
|
||||
|
||||
<h3>탐색 후 타겟 검출로 전환</h3>
|
||||
<div class="pipeline-row">
|
||||
<div class="pipe-box highlight">sam3_everything_explore</div>
|
||||
<span class="pipe-arrow">→</span>
|
||||
<div class="pipe-box" style="color:var(--muted)">라벨 빈도 확인<br>프롬프트 튜닝</div>
|
||||
<span class="pipe-arrow">→</span>
|
||||
<div class="pipe-box">detect_all_objects</div>
|
||||
<span class="pipe-arrow">→</span>
|
||||
<div class="pipe-box">detect_raamen (선택)</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ══════════════════════════════════════════════
|
||||
3. 서버
|
||||
══════════════════════════════════════════════ -->
|
||||
<section id="server-setup">
|
||||
<h2>서버 설정 (sam31server)</h2>
|
||||
|
||||
<h3 id="overview">시스템 아키텍처</h3>
|
||||
<pre><code>┌─────────────────────────────────┐
|
||||
│ sam31server │
|
||||
│ FastAPI app/main.py :8000 │
|
||||
│ GET /health │
|
||||
│ POST /v1/predict │
|
||||
│ Model: SAM 3.1 Multiplex (CUDA)│
|
||||
└──────────────┬──────────────────┘
|
||||
│ HTTP localhost:8000
|
||||
┌──────────────▼──────────────────┐
|
||||
│ railway-client tools │
|
||||
│ detect_all_objects.py │ ─┐
|
||||
│ detect_raamen.py ◀────────┘ │ 라멘 파이프라인
|
||||
│ sam3_everything_explore.py │
|
||||
│ sam3_autolabel.py ... │
|
||||
└─────────────────────────────────┘</code></pre>
|
||||
|
||||
<h3>요구사항</h3>
|
||||
<table>
|
||||
<tr><th>항목</th><th>버전</th></tr>
|
||||
<tr><td>Python</td><td>3.12</td></tr>
|
||||
<tr><td>PyTorch</td><td>2.10.0+cu126 (CUDA 필수)</td></tr>
|
||||
<tr><td>triton-windows</td><td>3.6.0.post25</td></tr>
|
||||
<tr><td>FastAPI</td><td>≥ 0.115.0</td></tr>
|
||||
<tr><td>SAM 3.1 모델</td><td>HuggingFace 자동 다운로드 (최초 1회)</td></tr>
|
||||
</table>
|
||||
|
||||
<h3>디렉토리 구조</h3>
|
||||
<pre><code>sam31server/
|
||||
├── sam3/ ← SAM 3.1 모델 라이브러리
|
||||
├── app/
|
||||
│ ├── main.py ← FastAPI 진입점
|
||||
│ ├── api/predict.py ← POST /v1/predict
|
||||
│ ├── api/health.py ← GET /health
|
||||
│ ├── models/segment_anything_3.py
|
||||
│ ├── core/registry.py
|
||||
│ └── tasks/inference.py
|
||||
├── configs/
|
||||
│ ├── server.yaml ← 포트·동시성
|
||||
│ ├── models.yaml ← enabled: segment_anything_3
|
||||
│ └── auto_labeling/segment_anything_3.yaml
|
||||
├── bpe_simple_vocab_16e6.txt.gz
|
||||
└── start_server.bat</code></pre>
|
||||
</section>
|
||||
|
||||
<section id="server-start">
|
||||
<h2>서버 시작</h2>
|
||||
|
||||
<div class="step">
|
||||
<div class="step-num">1</div>
|
||||
<div class="step-body">
|
||||
<h4>sam31server 디렉토리에서 실행</h4>
|
||||
<pre><code>cd D:\MYCLAUDE_PROJECT\sam31server
|
||||
|
||||
# 배치파일
|
||||
start_server.bat
|
||||
|
||||
# 또는 직접
|
||||
.venv\Scripts\python.exe -m app.main
|
||||
|
||||
# 또는 uvicorn 직접 (Windows 이벤트루프 이슈 우회)
|
||||
.venv\Scripts\python.exe -m uvicorn app.main:app --host 0.0.0.0 --port 8000</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-num">2</div>
|
||||
<div class="step-body">
|
||||
<h4>정상 구동 확인</h4>
|
||||
<pre><code>INFO | Loading [segment_anything_3] (Segment Anything 3.1)...
|
||||
INFO | SAM3 model loaded successfully
|
||||
INFO | Uvicorn running on http://0.0.0.0:8000</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="server-api">
|
||||
<h2>API 엔드포인트</h2>
|
||||
|
||||
<h3>POST /v1/predict</h3>
|
||||
<pre><code>{
|
||||
"model": "segment_anything_3",
|
||||
"image": "<base64 JPEG>",
|
||||
"params": {
|
||||
"text_prompt": "catenary pole, overhead line pole",
|
||||
"conf_threshold": 0.25,
|
||||
"show_masks": true,
|
||||
"show_boxes": false
|
||||
}
|
||||
}</code></pre>
|
||||
<p>응답:</p>
|
||||
<pre><code>{
|
||||
"data": {
|
||||
"shapes": [
|
||||
{ "label": "catenary pole", "shape_type": "polygon",
|
||||
"points": [[x,y], ...], "score": 0.87 }
|
||||
]
|
||||
}
|
||||
}</code></pre>
|
||||
|
||||
<table>
|
||||
<tr><th>params 키</th><th>설명</th></tr>
|
||||
<tr><td><code>text_prompt</code></td><td>텍스트 grounding (쉼표로 다중 객체)</td></tr>
|
||||
<tr><td><code>marks</code></td><td>rectangle / point 시각 프롬프트</td></tr>
|
||||
<tr><td><code>conf_threshold</code></td><td>신뢰도 임계값</td></tr>
|
||||
<tr><td><code>show_masks</code></td><td>polygon 반환 (true 권장)</td></tr>
|
||||
<tr><td><code>show_boxes</code></td><td>bounding box 반환</td></tr>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<!-- ══════════════════════════════════════════════
|
||||
4. 전체 도구
|
||||
══════════════════════════════════════════════ -->
|
||||
<section id="tools">
|
||||
<h2>클라이언트 도구 전체 목록</h2>
|
||||
<table>
|
||||
<tr><th>스크립트</th><th>기능</th><th>출력</th><th>우선순위</th></tr>
|
||||
<tr style="background:#1a0e00;">
|
||||
<td><code>detect_all_objects.py</code></td>
|
||||
<td>타일 분할 + 다중 카테고리 검출 + NMS</td>
|
||||
<td>PNG + JSON</td>
|
||||
<td><span class="badge badge-orange">핵심</span></td>
|
||||
</tr>
|
||||
<tr style="background:#1a0e00;">
|
||||
<td><code>detect_raamen.py</code></td>
|
||||
<td>門형 라멘 전철주 B/C 분류 + 그룹 판정</td>
|
||||
<td>PNG</td>
|
||||
<td><span class="badge badge-orange">핵심</span></td>
|
||||
</tr>
|
||||
<tr style="background:#0c1a0c;">
|
||||
<td><code>sam3_everything_explore.py</code></td>
|
||||
<td>Discovery Sweep — 전체 객체 탐색 + 빈도 집계</td>
|
||||
<td>PNG + 통계</td>
|
||||
<td><span class="badge badge-green">탐색</span></td>
|
||||
</tr>
|
||||
<tr><td><code>sam3_autolabel.py</code></td><td>전철주 + 레일 zone 기반 자동 라벨링</td><td>JSON</td><td></td></tr>
|
||||
<tr><td><code>sam3_batch_label.py</code></td><td>폴더 배치 → X-AnyLabeling JSON</td><td>JSON</td><td></td></tr>
|
||||
<tr><td><code>yoloworld_sam3_pipeline.py</code></td><td>YOLO-World bbox → SAM3 polygon</td><td>JSON+PNG</td><td></td></tr>
|
||||
<tr><td><code>video_sam3_segment.py</code></td><td>영상 프레임 추출 → 세그멘테이션</td><td>JSON/frame</td><td></td></tr>
|
||||
<tr><td><code>render_everything_by_label.py</code></td><td>라벨별 색상 렌더링</td><td>PNG</td><td></td></tr>
|
||||
<tr><td><code>render_label_polygons.py</code></td><td>polygon 오버레이</td><td>PNG</td><td></td></tr>
|
||||
<tr><td><code>sam3_segment_everything.py</code></td><td>이미지 전체 세그멘테이션</td><td>PNG</td><td></td></tr>
|
||||
<tr><td><code>railway_pipeline.py</code></td><td>전체 파이프라인 통합</td><td>종합</td><td></td></tr>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section id="tool-detect-all">
|
||||
<h2>detect_all_objects.py</h2>
|
||||
<pre><code>.venv\Scripts\python.exe tools/detect_all_objects.py `
|
||||
--input "data/역사구간/..." `
|
||||
--categories configs/railway_zone.json `
|
||||
--tiles all `
|
||||
--cols 4 --rows 3 --overlap 0.10 --workers 2</code></pre>
|
||||
|
||||
<table>
|
||||
<tr><th>파라미터</th><th>기본값</th><th>설명</th></tr>
|
||||
<tr><td><code>--input</code></td><td>필수</td><td>이미지 또는 폴더</td></tr>
|
||||
<tr><td><code>--categories</code></td><td>railway_zone.json</td><td>카테고리 설정 파일</td></tr>
|
||||
<tr><td><code>--tiles</code></td><td>all</td><td><code>9-24</code>, <code>1,5,9</code>, <code>all</code></td></tr>
|
||||
<tr><td><code>--cols / --rows</code></td><td>8 / 6</td><td>타일 분할 수</td></tr>
|
||||
<tr><td><code>--overlap</code></td><td>0.10</td><td>타일 겹침 비율</td></tr>
|
||||
<tr><td><code>--workers</code></td><td>4</td><td>병렬 스레드</td></tr>
|
||||
<tr><td><code>--conf</code></td><td>카테고리별</td><td>전역 신뢰도 오버라이드</td></tr>
|
||||
</table>
|
||||
<p>출력: <code>output/detect/이미지명_detected.png</code> + <code>.json</code></p>
|
||||
</section>
|
||||
|
||||
<section id="tool-autolabel">
|
||||
<h2>sam3_autolabel.py</h2>
|
||||
<pre><code>.venv\Scripts\python.exe tools/sam3_autolabel.py `
|
||||
--input data/역사구간/1.회덕역/ `
|
||||
--output output/labels/</code></pre>
|
||||
</section>
|
||||
|
||||
<section id="tool-batch">
|
||||
<h2>sam3_batch_label.py</h2>
|
||||
<pre><code>.venv\Scripts\python.exe tools/sam3_batch_label.py `
|
||||
--input data/역사구간/ `
|
||||
--prompt "railway catenary pole" `
|
||||
--output output/batch_labels/ `
|
||||
--workers 8</code></pre>
|
||||
</section>
|
||||
|
||||
<section id="tool-yoloworld">
|
||||
<h2>yoloworld_sam3_pipeline.py</h2>
|
||||
<pre><code>.venv\Scripts\python.exe tools/yoloworld_sam3_pipeline.py `
|
||||
--input data/역사구간/ `
|
||||
--output output/labeled/</code></pre>
|
||||
</section>
|
||||
|
||||
<section id="tool-video">
|
||||
<h2>video_sam3_segment.py</h2>
|
||||
<pre><code>.venv\Scripts\python.exe tools/video_sam3_segment.py</code></pre>
|
||||
<p>스크립트 상단에서 영상 경로·프롬프트·프레임 추출 간격 직접 수정.</p>
|
||||
</section>
|
||||
|
||||
<section id="tool-render">
|
||||
<h2>렌더링 도구</h2>
|
||||
<pre><code># 라벨별 색상 렌더링
|
||||
.venv\Scripts\python.exe tools/render_everything_by_label.py `
|
||||
--input output/labels/image.json --image data/.../image.JPG
|
||||
|
||||
# polygon 오버레이
|
||||
.venv\Scripts\python.exe tools/render_label_polygons.py `
|
||||
--input output/labels/ --image data/.../</code></pre>
|
||||
</section>
|
||||
|
||||
<!-- ══════════════════════════════════════════════
|
||||
5. 카테고리
|
||||
══════════════════════════════════════════════ -->
|
||||
<section id="categories">
|
||||
<h2>카테고리 설정 — railway_zone.json</h2>
|
||||
<p>경로: <code>configs/railway_zone.json</code></p>
|
||||
<table>
|
||||
<tr><th>이름</th><th>한국어</th><th>색상</th><th>conf</th><th>우선순위</th></tr>
|
||||
<tr><td><code>control_box</code></td><td>컨트롤박스</td><td><span class="swatch" style="background:rgb(255,255,0)"></span>노랑</td><td>0.15</td><td>2</td></tr>
|
||||
<tr><td><code>vehicle</code></td><td>차량</td><td><span class="swatch" style="background:#fff"></span>흰색</td><td>0.25</td><td>2</td></tr>
|
||||
<tr><td><code>catenary_pole</code></td><td>전철주</td><td><span class="swatch" style="background:rgb(255,0,255)"></span>마젠타</td><td>0.25</td><td>3</td></tr>
|
||||
<tr><td><code>railway</code></td><td>철도 레일</td><td><span class="swatch" style="background:rgb(0,0,255)"></span>파랑</td><td>0.25</td><td>4</td></tr>
|
||||
<tr><td><code>fence</code></td><td>팬스/울타리</td><td><span class="swatch" style="background:rgb(0,255,0)"></span>초록</td><td>0.50</td><td>5</td></tr>
|
||||
<tr><td><code>sleeper</code></td><td>침목</td><td><span class="swatch" style="background:rgb(0,128,255)"></span>하늘</td><td>0.20</td><td>6</td></tr>
|
||||
<tr><td><code>ballast</code></td><td>자갈도상</td><td><span class="swatch" style="background:rgb(30,50,100)"></span>남색</td><td>0.20</td><td>7</td></tr>
|
||||
<tr><td><code>bracket</code></td><td>브라켓/암</td><td><span class="swatch" style="background:rgb(0,80,200)"></span>청색</td><td>0.20</td><td>8</td></tr>
|
||||
<tr><td><code>bridge</code></td><td>교량/교각</td><td><span class="swatch" style="background:rgb(0,165,255)"></span>주황하늘</td><td>0.25</td><td>8</td></tr>
|
||||
<tr><td><code>building</code></td><td>건물</td><td><span class="swatch" style="background:rgb(50,50,255)"></span>청보라</td><td>0.25</td><td>8</td></tr>
|
||||
<tr><td><code>retaining_wall</code></td><td>방음벽/옹벽</td><td><span class="swatch" style="background:rgb(60,180,60)"></span>연두</td><td>0.25</td><td>9</td></tr>
|
||||
<tr><td><code>culvert</code></td><td>암거/소교량</td><td><span class="swatch" style="background:rgb(180,60,180)"></span>보라</td><td>0.20</td><td>9</td></tr>
|
||||
<tr><td><code>service_road</code></td><td>유지보수 도로</td><td><span class="swatch" style="background:rgb(80,120,160)"></span>회청</td><td>0.30</td><td>10</td></tr>
|
||||
<tr><td><code>farmland</code></td><td>농지</td><td><span class="swatch" style="background:rgb(50,200,50)"></span>밝은초록</td><td>0.25</td><td>11</td></tr>
|
||||
<tr><td><code>vegetation</code></td><td>식생</td><td><span class="swatch" style="background:rgb(0,120,0)"></span>짙은초록</td><td>0.25</td><td>12</td></tr>
|
||||
</table>
|
||||
<div class="alert alert-info">
|
||||
<strong>우선순위(priority):</strong> 낮을수록 cross-class NMS에서 우선 보존. 우선순위 2 = 최우선(컨트롤박스·차량), 12 = 최하위(식생).
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="prompts">
|
||||
<h2>프롬프트 파일</h2>
|
||||
<p>일부 도구는 <code>prompts/</code> 폴더의 텍스트 파일을 사용:</p>
|
||||
<pre><code>prompts/
|
||||
├── pole.txt ← 전철주 검출용 프롬프트
|
||||
├── rail.txt ← 레일 검출용 프롬프트
|
||||
├── bracket.txt ← 브라켓 검출용
|
||||
└── sleeper.txt ← 침목 검출용</code></pre>
|
||||
</section>
|
||||
|
||||
<!-- ══════════════════════════════════════════════
|
||||
6. 빠른 시작 / 트러블슈팅
|
||||
══════════════════════════════════════════════ -->
|
||||
<section id="quickstart">
|
||||
<h2>빠른 시작 — 단일 이미지 전체 파이프라인</h2>
|
||||
|
||||
<div class="step">
|
||||
<div class="step-num orange">1</div>
|
||||
<div class="step-body">
|
||||
<h4>SAM3.1 서버 시작 (터미널 A)</h4>
|
||||
<pre><code>cd D:\MYCLAUDE_PROJECT\sam31server
|
||||
.venv\Scripts\python.exe -m app.main</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-num orange">2</div>
|
||||
<div class="step-body">
|
||||
<h4>[선택] 탐색 모드로 객체 파악 (터미널 B)</h4>
|
||||
<pre><code>cd D:\MYCLAUDE_PROJECT\railway-client
|
||||
.venv\Scripts\python.exe tools/sam3_everything_explore.py `
|
||||
--input "data/역사구간/1.회덕역/.../DJI_20260306100900_0034.JPG" `
|
||||
--cols 4 --rows 3</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-num orange">3</div>
|
||||
<div class="step-body">
|
||||
<h4>전철주 검출 + JSON annotation 생성</h4>
|
||||
<pre><code>.venv\Scripts\python.exe tools/detect_all_objects.py `
|
||||
--input "data/역사구간/1.회덕역/.../DJI_20260306100900_0034.JPG" `
|
||||
--categories configs/railway_zone.json `
|
||||
--tiles all --cols 4 --rows 3 --overlap 0.10 --workers 2</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-num orange">4</div>
|
||||
<div class="step-body">
|
||||
<h4>라멘 구조 분석</h4>
|
||||
<pre><code>.venv\Scripts\python.exe tools/detect_raamen.py `
|
||||
--image "data/역사구간/1.회덕역/.../DJI_20260306100900_0034.JPG" `
|
||||
--label "output/detect/DJI_20260306100900_0034.json" `
|
||||
--output "output/raamen/DJI_20260306100900_0034_raamen.png"</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="troubleshoot">
|
||||
<h2>트러블슈팅</h2>
|
||||
<table>
|
||||
<tr><th>오류</th><th>원인</th><th>해결</th></tr>
|
||||
<tr><td><code>Connection refused :8000</code></td><td>서버 미실행</td><td>sam31server에서 서버 시작</td></tr>
|
||||
<tr><td><code>Torch not compiled with CUDA</code></td><td>CPU 버전 torch</td><td><code>pip install torch==2.10.0+cu126 --index-url .../cu126</code></td></tr>
|
||||
<tr><td><code>No module named 'triton'</code></td><td>triton-windows 미설치</td><td><code>pip install triton-windows==3.6.0.post25</code></td></tr>
|
||||
<tr><td><code>No module named 'decord'</code></td><td>decord 미설치</td><td><code>pip install decord</code></td></tr>
|
||||
<tr><td>라멘 검출 없음</td><td>catenary_pole conf 너무 높음</td><td>Step 1에서 <code>--conf 0.10</code> 로 낮춤</td></tr>
|
||||
<tr><td>C/B 분류 오류</td><td>소실점 계산 불안정</td><td><code>--c-thresh</code> 조정 (기본 20°, 높이면 C 기준 완화)</td></tr>
|
||||
<tr><td>Windows 이벤트루프 경고</td><td>Python 3.12 + uvicorn</td><td><code>python -m uvicorn app.main:app --host 0.0.0.0 --port 8000</code></td></tr>
|
||||
</table>
|
||||
|
||||
<hr>
|
||||
<p style="color:var(--muted); font-size:.8rem; text-align:center;">Railway Client Guide · SAM 3.1 + FastAPI · 2026-06</p>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
280
docs/진행현황_2026-05-22.md
Normal file
280
docs/진행현황_2026-05-22.md
Normal file
@@ -0,0 +1,280 @@
|
||||
# 철도 디지털 트윈 — AI 라벨링 파이프라인 진행 현황
|
||||
|
||||
> 기준일: 2026-05-22
|
||||
> 프로젝트: `d:\MYCLAUDE_PROJECT\x-anylabeling01`
|
||||
> 목표: 드론 부감 이미지에서 철도 지장물 자동 탐지 → YOLOv26 학습 데이터 구축 → 디지털 트윈
|
||||
|
||||
---
|
||||
|
||||
## 1. 탐지 대상 (지장물 카테고리)
|
||||
|
||||
| 카테고리 | 한글 | 상태 |
|
||||
|---|---|---|
|
||||
| raamen (ラーメン) | 라멘형 전철주 | ✅ 완료 |
|
||||
| pole | 전철주 일반 | ✅ 완료 |
|
||||
| rail | 레일 | ✅ 완료 |
|
||||
| sleeper | 침목 | ✅ 완료 |
|
||||
| control_box | 컨트롤박스 (소형 금속 박스) | 🔄 진행 중 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 완료된 작업
|
||||
|
||||
### 2-1. 라멘형 전철주 검출 (`tools/detect_raamen.py`)
|
||||
|
||||
- **방식**: SAM3.1 텍스트 grounding + VP(소실점) 기반 H/V 분류
|
||||
- **입력**: 드론 고해상도 이미지 (.JPG), 선택적으로 `--json` (detect_all_objects.py 결과 재사용)
|
||||
- **출력**: AnyLabeling JSON (폴리곤 + H/V 라벨)
|
||||
- **핵심 알고리즘**:
|
||||
- 이미지를 타일 분할 → SAM3.1 병렬 호출
|
||||
- VP(소실점) 다중 시드 + 반복 정제로 수직(V) / 수평(H) 분류
|
||||
- H-max-diff 필터로 수평 빔과 수직 기둥 구분
|
||||
- Cross-NMS로 중복 제거
|
||||
- **최근 커밋**: `923c396` (VP 다중 시드 + 반복 정제로 V/H 분류 정확도 향상)
|
||||
|
||||
### 2-2. 전체 객체 검출 UI (`tools/web_ui.py`)
|
||||
|
||||
- FastAPI + uvicorn 기반 웹 UI
|
||||
- 이미지 선택 → 타일/카테고리 설정 → `detect_all_objects.py` 실행 → 결과 프리뷰
|
||||
- **개선 사항** (이번 세션):
|
||||
- 마우스 휠 줌 + 드래그 팬 추가
|
||||
- `--save-labels` → `--save-json` 수정 (txt 금지, JSON만)
|
||||
- `--debug` 플래그 제거 (존재하지 않는 옵션)
|
||||
- 프리뷰 해상도 1200 → 2000px
|
||||
|
||||
### 2-3. 전체 객체 검출 (`tools/detect_all_objects.py`)
|
||||
|
||||
- SAM3.1 텍스트 grounding, 카테고리별 타일 검출
|
||||
- `configs/railway_zone.json` 카테고리 설정 사용
|
||||
- **출력 경로 개선**: `output/detect/{이미지명}/tiles{N}_{카테고리}_{번호}.jpg`
|
||||
- `--save-json` 옵션으로 AnyLabeling JSON 저장
|
||||
|
||||
### 2-4. SAM3 Everything 탐색 (`tools/sam3_everything_explore.py`)
|
||||
|
||||
- 38개 항목 광역 프롬프트로 이미지 전체 탐색
|
||||
- 타일 병렬 처리 + NMS
|
||||
- JSON 라벨 통계 출력 (text_prompt 후보 발굴용)
|
||||
- `--prompt-extra` 옵션으로 추가 어휘 주입 가능
|
||||
- **테스트 결과** (DJI_20260306113838_0004.JPG, 8×6 타일):
|
||||
- 총 7,111개 세그먼트 검출
|
||||
- control_box 관련 라벨 1,740개 매칭
|
||||
- **문제**: 정밀도 ~1-2%, false positive 폭증 → SAM3 텍스트 grounding 한계 확인
|
||||
|
||||
---
|
||||
|
||||
## 3. 핵심 결정 사항
|
||||
|
||||
### 3-1. YOLOv26 단독 전환 (2026-05-19)
|
||||
|
||||
**배경**: SAM3.1 텍스트 grounding으로 control_box 탐지 시도 → 완전 실패
|
||||
|
||||
| 시도 | 결과 |
|
||||
|---|---|
|
||||
| 고립 프롬프트 ("railway control box, electrical cabinet...") | 0 → 0 검출 |
|
||||
| conf 0.20 → 0.10 낮춤 | 0 → 0 검출 |
|
||||
| 16×12 세밀한 타일 그리드 | 0 → 0 검출 |
|
||||
| 인접 4개 타일 수동 실험 | 0 → 0 검출 |
|
||||
| 38개 광역 프롬프트 | 1,740개 매칭, 정밀도 ~1-2% |
|
||||
|
||||
**결론**: SAM3.1은 부감 드론 시점 소형 객체 텍스트 grounding에 부적합
|
||||
|
||||
**전환**: **YOLOv26 학습 데이터셋 구축 → fine-tune → 추론** 경로
|
||||
|
||||
**준비 완료**:
|
||||
- `yolo26n.pt`, `yolo26n-seg.pt` 사전학습 모델 (프로젝트 루트)
|
||||
- ultralytics 26.x 설치 완료
|
||||
- X-AnyLabeling-Server YOLO endpoint 연동 완료
|
||||
|
||||
### 3-2. SAM3 활용 방식 변경
|
||||
|
||||
- **폐기**: SAM3 텍스트 grounding → 단독 검출기
|
||||
- **유지**: SAM3 everything 모드 → 후보 bbox 생성기 (인간 라벨링 보조)
|
||||
- **신규**: YOLOv26 fine-tune → production 검출기
|
||||
|
||||
---
|
||||
|
||||
## 4. 현재 진행 중: control_box 라벨링 파이프라인
|
||||
|
||||
### 4-1. 전략 (부트스트랩)
|
||||
|
||||
```
|
||||
SAM3 everything → 1,740개 후보 bbox
|
||||
↓
|
||||
labeling_server.py (인간 투표)
|
||||
↓
|
||||
MIN_VOTES=3, TRUE_RATIO=0.6 필터
|
||||
↓
|
||||
YOLO 학습용 txt 라벨
|
||||
↓
|
||||
YOLOv26 fine-tune (yolo26n.pt)
|
||||
↓
|
||||
production 검출기
|
||||
```
|
||||
|
||||
### 4-2. 라벨링 서버 (`tools/labeling_server.py`) — v2
|
||||
|
||||
**실행 명령:**
|
||||
```bash
|
||||
python tools/labeling_server.py \
|
||||
--json "data/역사이미지/slope/DJI_20260306113838_0004_everything.json" \
|
||||
--reset
|
||||
```
|
||||
|
||||
**브라우저**: `http://서버IP:7001`
|
||||
|
||||
**UI 방식** (v2 — 전체 이미지 오버레이):
|
||||
- 전체 드론 이미지 표시 (최대 3000px)
|
||||
- SAM3 후보 1,740개를 색상 bbox로 오버레이
|
||||
- 🟡 노랑: 미투표
|
||||
- 🟢 초록: 컨트롤박스 YES
|
||||
- 🔴 빨강: 아님 NO
|
||||
- ⬜ 회색: 타인 투표 완료
|
||||
- 마우스 휠 줌 / 드래그 이동 / F키 맞춤
|
||||
- bbox 클릭 → YES/NO 즉시 저장 (SQLite)
|
||||
- 호버 → 라벨명 + 점수 툴팁
|
||||
|
||||
**집계 기준**:
|
||||
- `MIN_VOTES = 3` (3인 이상 투표)
|
||||
- `TRUE_RATIO = 0.6` (60% 이상 YES)
|
||||
|
||||
**YOLO 내보내기**: `POST /api/export` → `labels/yolo_export/*.txt`
|
||||
|
||||
**DB**: `labels/labeling.db` (SQLite, 로컬)
|
||||
- `candidates` 테이블: json_idx, label, score, bbox, image_path
|
||||
- `votes` 테이블: candidate_id, user, vote, ts (UNIQUE 제약 — 1인 1표)
|
||||
|
||||
### 4-3. 현재 데이터
|
||||
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| 입력 이미지 | `data/역사이미지/slope/DJI_20260306113838_0004.JPG` |
|
||||
| everything JSON | `DJI_20260306113838_0004_everything.json` |
|
||||
| 전체 세그먼트 | 7,111개 |
|
||||
| control_box 후보 | 1,740개 |
|
||||
| 라벨링 진행 | 미시작 (서버 실행 대기) |
|
||||
|
||||
---
|
||||
|
||||
## 5. 다음 할 일 (TODO)
|
||||
|
||||
### 즉시
|
||||
- [ ] `labeling_server.py` 배포 → 직원 20명 라벨링 시작
|
||||
- [ ] 추가 이미지 `_everything.json` 생성 (현재 1장 → 더 많이 필요)
|
||||
|
||||
### SAM3 Everything 프롬프트 재설계 (⚠️ 중요)
|
||||
- **문제**: 현재 DISCOVERY_PROMPT 38개 항목이 control_box를 제대로 분리 못함
|
||||
- **방향**: 더 구체적인 시각적 특징 기반 프롬프트 필요
|
||||
- 기존: `"small square box", "compact trackside junction box"` 등 → FP 폭증
|
||||
- 새 방향 검토 필요:
|
||||
- 크기/형태 명시: `"small gray square metal lid on ground"`, `"square dark gray lid flush with ballast"`
|
||||
- 재질/색상 특징: `"weathered gray metal surface"`, `"flat square cover"`
|
||||
- 부감 시점 특화: `"top-down view of small electrical enclosure"`
|
||||
|
||||
### 학습 후
|
||||
- [ ] 50-100개 이상 확정 라벨 → `yolo train` 실행
|
||||
```bash
|
||||
yolo train model=yolo26n.pt data=configs/control_box.yaml epochs=100 imgsz=640
|
||||
```
|
||||
- [ ] 검출 성능 검증 → 미달 시 데이터 추가 수집
|
||||
|
||||
---
|
||||
|
||||
## 6. 설정 파일
|
||||
|
||||
### `configs/railway_zone.json` (control_box 항목)
|
||||
```json
|
||||
{
|
||||
"name": "control_box",
|
||||
"name_kr": "컨트롤박스",
|
||||
"prompt": "small square gray metal box beside rail, compact trackside junction box, small near-square electrical enclosure on the ground, small cube-shaped equipment box next to track",
|
||||
"conf": 0.15,
|
||||
"priority": 2
|
||||
}
|
||||
```
|
||||
|
||||
### SAM3 Everything DISCOVERY_PROMPT 현재 상태 (재설계 필요)
|
||||
```
|
||||
"railroad track, railway rail,
|
||||
catenary pole, overhead line pole, electric pole,
|
||||
overhead wire, catenary wire, power line cable,
|
||||
railway sleeper, concrete tie,
|
||||
guardrail, highway barrier, road fence,
|
||||
bridge, viaduct, overpass,
|
||||
vegetation, tree, bush, grass,
|
||||
building, structure, roof, wall,
|
||||
vehicle, car, truck,
|
||||
road, asphalt, pavement,
|
||||
slope, embankment, retaining wall,
|
||||
noise barrier, sound wall,
|
||||
signal, sign board"
|
||||
```
|
||||
→ control_box 관련 항목 부재 → everything 탐색에서 누락됨
|
||||
|
||||
---
|
||||
|
||||
## 7. 기술 제약 / 주의사항
|
||||
|
||||
- **CLI 명령어**: bash 한 종류만 사용 (PowerShell 혼용 금지)
|
||||
- **라벨 저장**: `--save-json` 옵션만 사용 (txt 금지)
|
||||
- **detect_all_objects.py**: `--workers 8` 항상 명시
|
||||
- **SAM3 서버**: `python.exe -m app.main` (X-AnyLabeling-Server 디렉토리에서 실행, localhost:8000)
|
||||
- **GDINO**: 폐기됨. SAM3.1 텍스트 프롬프트 (`params.text_prompt`) 직접 사용
|
||||
- **YOLOv26**: ultralytics 26.x, 모델 파일 `yolo26n.pt` / `yolo26n-seg.pt` (프로젝트 루트)
|
||||
|
||||
---
|
||||
|
||||
## 8. 주요 파일 구조
|
||||
|
||||
```
|
||||
x-anylabeling01/
|
||||
├── tools/
|
||||
│ ├── detect_all_objects.py # 메인 검출 도구 (SAM3 타일 검출)
|
||||
│ ├── detect_raamen.py # 라멘형 전철주 전용 검출
|
||||
│ ├── web_ui.py # 웹 UI (FastAPI, port:8001)
|
||||
│ ├── labeling_server.py # CAPTCHA 라벨링 서버 (port:7001) ← 신규 v2
|
||||
│ ├── sam3_everything_explore.py # SAM3 전체 탐색 (프롬프트 발굴용) ← 개선 필요
|
||||
│ ├── sam3_segment_everything.py # SAM3 포인트 그리드 세그멘테이션
|
||||
│ └── post_merge_poles.py # 전철주 병합 후처리
|
||||
├── configs/
|
||||
│ └── railway_zone.json # 카테고리별 프롬프트/conf/priority
|
||||
├── labels/
|
||||
│ ├── labeling.db # 라벨링 투표 SQLite DB
|
||||
│ └── yolo_export/ # YOLO txt 내보내기 결과
|
||||
├── output/
|
||||
│ └── detect/{이미지명}/ # 검출 결과 이미지 (타일/카테고리별)
|
||||
├── yolo26n.pt # YOLOv26 nano 사전학습 모델
|
||||
└── yolo26n-seg.pt # YOLOv26 nano segmentation 모델
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 다음 세션 시작 프롬프트
|
||||
|
||||
```
|
||||
철도 디지털 트윈 프로젝트 (x-anylabeling01) 이어서 진행합니다.
|
||||
|
||||
[현재 상태]
|
||||
- detect_raamen.py (라멘형 전철주) 검출 완료
|
||||
- detect_all_objects.py (다중 카테고리 SAM3 검출) 완료
|
||||
- web_ui.py 개선 완료 (줌/팬, --save-json, 폴더 구조)
|
||||
- labeling_server.py v2 완료 (전체 이미지 + bbox 오버레이, 클릭 투표)
|
||||
|
||||
[오늘 할 일: SAM3 Everything 프롬프트 재설계]
|
||||
tools/sam3_everything_explore.py의 DISCOVERY_PROMPT를 재설계해야 합니다.
|
||||
|
||||
현재 문제:
|
||||
- 기존 38개 항목 광역 프롬프트로 DJI_20260306113838_0004.JPG 처리 시
|
||||
- 7,111개 세그먼트, control_box 관련 1,740개 매칭 → 정밀도 ~1-2%
|
||||
- control_box (소형 정사각형 금속 박스, 자갈도상 옆 지면에 놓인 것)가 제대로 분리 안 됨
|
||||
|
||||
목표:
|
||||
- control_box를 효과적으로 탐지할 수 있는 SAM3 텍스트 프롬프트 발굴
|
||||
- 혹은 SAM3 everything → 후보 bbox 1,740개에서 더 정밀하게 필터링하는 방법 개선
|
||||
- 최종적으로는 YOLOv26 학습 데이터 50-100개 확보
|
||||
|
||||
[기술 제약]
|
||||
- CLI는 bash만 사용 (PowerShell 혼용 금지)
|
||||
- 라벨 저장은 --save-json만 (txt 금지)
|
||||
- SAM3 서버: python.exe -m app.main (X-AnyLabeling-Server에서, localhost:8000)
|
||||
- detect_all_objects.py 실행 시 --workers 8 항상 명시
|
||||
```
|
||||
Reference in New Issue
Block a user