Files
railway-client/docs/railway-client-guide.html
minsung 4c15d5ff5d 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>
2026-06-02 10:11:52 +09:00

665 lines
33 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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": "&lt;base64 JPEG&gt;",
"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>