- 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>
665 lines
33 KiB
HTML
665 lines
33 KiB
HTML
<!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>
|