지하철·지하상가 표지판 인식 실험 리포트
2026-09-24 · SAM 3 세그멘테이션 → OCR 텍스트 DB → 이미지 벡터 DB · 영상 3개 · NVIDIA GeForce RTX 2080 Ti · torch 2.11.0+cu128 · transformers 5.17.0
가능① SAM 3로 천장·벽면·바닥 표지판 텍스트 프롬프트 탐지
3,849탐지 영역 (샘플 프레임 1035개, 4 fps)
497 msSAM 3 프레임당 추론 (프롬프트 3개, fp16, RTX 2080 Ti)
30–36%② 텍스트 DB top-1 (미등록 프레임 → 등록 프레임)
0.949③ 같은 표지판 판별 AUC (DINOV2), 임계값 0.71
43%③ 시뮬레이션: 미등록 프레임 영역이 정답 표지판으로 매칭
0. 결론 요약
- ① SAM 3 가능 여부: 가능. SAM 3의 Promptable Concept Segmentation(텍스트 프롬프트)만으로 “hanging directional sign / wall sign / floor sign” 3개 개념을 별도 학습 없이 잡아냈다. 이미지 인코딩 1회 후 프롬프트 3개를 재사용해 프레임당 약 497 ms(2080 Ti, fp16)이며, 4 fps 샘플링 기준 3개 영상 1035프레임에서 3,849개 영역·1612개 트랙을 얻었다.
- ② OCR → 텍스트 DB: EasyOCR(ko+en)로 영역별 텍스트를 뽑아 SQLite에 넣고, 문자열 유사도(rapidfuzz)와 다국어 문장 임베딩을 6:4로 섞은 하이브리드 유사도로 검색했다. 미등록(홀수) 프레임 질의 → 등록(짝수) 프레임 갤러리 top-1 정확도는 영상별 35%, 36%, 30%. 트랙 내 OCR 문자열 일관성은 0.29로, 흔들림·원근에 따라 같은 표지판도 읽힌 글자가 달라지는 것이 주 오차원이다.
- ③ 이미지 벡터 DB: DINOv2-base 와 CLIP ViT-L/14 임베딩을 FAISS 로 색인해 비교한 결과 DINOV2가 같은 표지판 판별 AUC 0.949(CLIP 0.806, DINOv2 0.949)로 선택됐다. “모델” = 갤러리 임베딩 + 코사인 임계값 0.71. 미등록 프레임 시뮬레이션에서 1913개 영역 중 824개(43.1%)가 정답 트랙으로, 747개(39.0%)가 다른 트랙으로 매칭됐다.
주의(오버피팅): 영상이 한 벌씩뿐이라 ②③ 검증은 같은 영상의 짝수 프레임을 등록, 홀수 프레임을 질의로 쓴 동일 영상 내 교차 검증이다. 조명·각도·거리가 거의 같은 인접 프레임이므로 실제 일반화 성능보다 높게 나온다. 다른 날·다른 기기로 찍은 영상이 확보되면 갤러리/질의를 영상 단위로 분리해 다시 측정해야 한다.
1. 실험 환경
| 항목 | 값 |
|---|---|
| GPU | NVIDIA GeForce RTX 2080 Ti × 3 (영상 1개당 GPU 1개 병렬) |
| SAM 3 가중치 | facebook/sam3는 gated(수동 승인)이라 현재 토큰으로 403. 동일 파일 구성의 비게이트 미러 jetjodh/sam3(HF transformers 포맷, model.safetensors)로 실행. 공식 승인 후 --model facebook/sam3로 교체 가능(코드 변경 없음). |
| 프레임워크 | transformers 5.17.0 Sam3Model / Sam3Processor, fp16, 이미지 1008×1008 리사이즈 |
| 영상 | 영상 1 · 09:04 100.33s; 영상 2 · 09:06 43.4s; 영상 3 · 09:17 114.89s · 원본 1080×2320 세로(회전 메타), 30/120/240 fps → 4 fps 샘플, UI용 720p 30 fps 재인코딩 |
| 프롬프트 | ceiling_sign=“hanging directional sign” · wall_sign=“wall sign” · floor_marking=“floor sign” · presence threshold 0.4, 면적 0.15%~35% 필터, 프롬프트 간 IoU 0.7 NMS |
| 트래킹 | IoU 0.3 그리디 매칭, 3프레임 갭 허용(간이 트래커) — 트랙 ID는 ②③의 “같은 표지판” 유사 정답(pseudo label) |
| OCR | EasyOCR ko+en (CRAFT + korean_g2), GPU, 높이 96px 미만 crop 업스케일 |
| 텍스트 검색 | SQLite + rapidfuzz ratio(0.6) + paraphrase-multilingual-MiniLM-L12-v2 코사인(0.4) |
| 이미지 벡터 DB | FAISS IndexFlatIP · DINOv2-base(CLS+평균패치, 1536d) vs CLIP ViT-L/14(768d) |
2. ① SAM 3 탐지 결과
| 영상 | 길이(s) | 샘플 프레임 | 탐지 영역 | 트랙 | 평균 ms/frame | 중앙값 ms |
|---|---|---|---|---|---|---|
| 영상 1 · 09:04 | 100.33 | 401 | 1700 | 667 | 523 | 511 |
| 영상 2 · 09:06 | 43.4 | 174 | 492 | 219 | 474 | 465 |
| 영상 3 · 09:17 | 114.89 | 460 | 1657 | 726 | 494 | 480 |
오버레이 예시 (파랑=천장 안내판, 노랑=벽면 표지판, 초록=바닥 표식)
20260923_090446
20260923_090645
20260923_091747
관찰
- 천장 매달림 안내판(파랑 “나가는 곳/출구” 계열)과 노란 벽면 안내판, 바닥의 노란 안내 도장·KTX 유도선이 프롬프트만으로 분리된다. 작은 원거리 표지판도 잡히지만 score가 0.4~0.6 대로 낮다.
- 오탐: 천장 형광등·조명 박스가 “hanging directional sign”으로 가끔 잡힌다(대부분 score <0.5). 바닥의 노란 점자블록 일부가 “floor sign”으로 잡히는 경우가 있다. 운영 시 score 0.5 컷 + 트랙 길이 ≥2 조건으로 대부분 제거 가능.
- 트랙 수가 탐지 수 대비 많다(카메라 흔들림·4 fps 샘플링으로 IoU 매칭이 자주 끊김). 실시간 적용 시 SAM 3의 비디오 트래킹 모드(streaming) 또는 더 높은 샘플링이 필요하다.
- 속도: 2080 Ti fp16에서 프롬프트 3개 합산 약 497 ms → 약 2 fps. 모바일 실시간은 서버 추론 + 저해상도 프레임 전송 구조가 현실적이며, 프롬프트를 1개(“sign”)로 줄이면 약 1/2.
3. ② OCR → 텍스트 DB → 유사도 검색
| 영상 | crop | 텍스트 있음 | 비율 | 텍스트 트랙 | 트랙 내 OCR 일관성 | 질의 수 | top-1 | top-3 | 검색 ms |
|---|---|---|---|---|---|---|---|---|---|
| 20260923_090446 | 1700 | 569 | 33% | 216 | 0.23 | 197 | 35.0% | 50.2% | 0.64 |
| 20260923_090645 | 492 | 166 | 34% | 62 | 0.42 | 59 | 35.6% | 52.5% | 0.2 |
| 20260923_091747 | 1657 | 659 | 40% | 246 | 0.23 | 227 | 30.4% | 48.5% | 0.75 |
OCR 평균 19 ms/crop. “같은 표지판” 판정 임계값은 F1 최대 기준 0.60 (F1 0.26).
OCR 예시 (신뢰도 상위, 중복 제거)
나가 Exit
20260923_090446 · conf 1.00
20260923_090446 · conf 1.00
남자전용 Men
20260923_090446 · conf 1.00
20260923_090446 · conf 1.00
12 11
20260923_091747 · conf 1.00
20260923_091747 · conf 1.00
3 14
20260923_091747 · conf 0.99
20260923_091747 · conf 0.99
4 5 타는 곳
20260923_091747 · conf 0.99
20260923_091747 · conf 0.99
THE LUYI
20260923_091747 · conf 0.99
20260923_091747 · conf 0.99
타는 곳
20260923_090446 · conf 0.98
20260923_090446 · conf 0.98
GX A
20260923_090446 · conf 0.98
20260923_090446 · conf 0.98
UN QL
20260923_091747 · conf 0.98
20260923_091747 · conf 0.98
나가는 곳 E
20260923_090446 · conf 0.97
20260923_090446 · conf 0.97
용 화장실 onlytollet
20260923_090446 · conf 0.97
20260923_090446 · conf 0.97
UNI LO
20260923_091747 · conf 0.95
20260923_091747 · conf 0.95
텍스트 DB에 가장 자주 들어간 정규화 문자열 (상위 20)
나가는곳× 18smpo× 8타는곳× 7나가는곳e× 4나가논곳× 4111× 4uniqlo× 41211× 4smfo× 4나가논× 3나가는곳exit버다× 3나가는곳exit8다× 313114× 3터논곳는× 2lna× 2나가는곳ei× 2타는곳tracks추oue× 2타논곳× 2나가는공× 2unilo× 2
관찰
- 큰 글씨(나가는 곳, Exit, 출구 번호, 역명)는 안정적으로 읽히고, 작은 병기 글자(일본어·중국어·소형 영문)는 흔들림에 따라 결과가 자주 바뀐다. 트랙 단위로 다수결 문자열을 DB 엔트리로 쓰는 편이(현재 시뮬레이션 방식) 프레임 단위 저장보다 검색 품질이 좋다.
- 문자열 유사도만 쓰면 “출구 1”과 “출구 2”처럼 숫자 하나 차이를 구분하기 어렵다. 임베딩을 섞어도 완전히 해결되지 않으므로, 운영 DB에는 출구 번호·노선 번호를 별도 필드로 파싱해 정확 일치 조건을 두는 것을 권한다.
- 한국어 OCR 정확도 자체를 높이려면 PaddleOCR PP-OCRv5(한국어) 또는 VLM 기반 판독을 후보로 둔다(이번 실험은 설치 단순성 때문에 EasyOCR).
4. ③ 이미지 벡터 DB → 같은 표지판 판별
| 모델 | 차원 | 임베딩 ms/crop | 검증 AUC | 최적 임계값 | F1 | FPR 1% 임계값 | TPR@FPR1% | 양성/음성 평균 유사도 |
|---|---|---|---|---|---|---|---|---|
| dinov2 ★ | 1536 | 1.68 | 0.9490 | 0.710 | 0.450 | 0.663 | 57.3% | 0.677 / 0.324 |
| clip | 768 | 1.67 | 0.8057 | 0.930 | 0.243 | 0.913 | 28.6% | 0.848 / 0.734 |
| 모델 | 영상 | 갤러리 | 질의 | 트랙 | top-1 | top-5 | 검색 ms |
|---|---|---|---|---|---|---|---|
| dinov2 ★ | 20260923_090446 | 850 | 595 | 416 | 72.4% | 87.9% | 0.012 |
| dinov2 ★ | 20260923_090645 | 240 | 159 | 131 | 65.4% | 91.8% | 0.318 |
| dinov2 ★ | 20260923_091747 | 846 | 551 | 473 | 66.1% | 86.0% | 0.093 |
| clip | 20260923_090446 | 850 | 595 | 416 | 61.2% | 82.2% | 0.006 |
| clip | 20260923_090645 | 240 | 159 | 131 | 59.8% | 83.7% | 0.065 |
| clip | 20260923_091747 | 846 | 551 | 473 | 54.4% | 75.3% | 0.11 |
실시간 시뮬레이션 결과 (미등록 프레임만, UI의 “인식 시뮬레이션” 화면과 동일 데이터)
| 영상 | 추론 영역 | 임계값 통과 | 정답 트랙 | 정답률 | 오매칭률 | OCR 있음 | 텍스트 매칭 | 텍스트 매칭률 |
|---|---|---|---|---|---|---|---|---|
| 20260923_090446 | 850 | 725 | 399 | 46.9% | 38.4% | 275 | 183 | 66.5% |
| 20260923_090645 | 252 | 196 | 94 | 37.3% | 40.5% | 78 | 48 | 61.5% |
| 20260923_091747 | 811 | 650 | 331 | 40.8% | 39.3% | 308 | 220 | 71.4% |
| 합계 | 1913 | 1571 | 824 | 43.1% | 39.0% | 661 | 451 | 68.2% |
관찰
- DINOV2 임베딩은 인접 프레임의 같은 표지판을 높은 유사도로 묶고 다른 표지판과 분리한다(양성 평균 0.68 vs 음성 0.32). 임계값 0.71는 F1 최대 기준이며, 오매칭을 줄이려면 FPR 1% 기준 0.66를 쓴다.
- 오매칭의 주 원인은 (a) 같은 디자인의 표지판이 여러 위치에 반복 설치됨(노란 “나가는 곳” 판은 시각적으로 동일) (b) 간이 트래커가 같은 표지판에 새 트랙 ID를 부여해 “정답”이 갈라짐. 즉 실제 성능은 표 수치보다 좋을 가능성이 있고, 반대로 (a)는 위치 판별 목적에서는 이미지만으로 해결 불가 → 텍스트(출구 번호)와 결합해야 한다.
- 검색 지연은 갤러리 1936개 기준 <1 ms(FlatIP). 수만 개 이상이면 HNSW/IVF 인덱스로 전환.
5. UI
app/Next.js 16 + shadcn(base-nova) 모바일 가정 웹앱.cd app && npm run dev→ http://localhost:3000- 상단: 영상 3개 전환 버튼. 하단 탭: 세그멘테이션(영상 위에 SAM 3 폴리곤을 반투명 캔버스로 합성, 클래스별 on/off·투명도) ↔ 인식 시뮬레이션(현재 시점 영역별 crop·OCR·텍스트 DB 매칭·이미지 DB 매칭·판정, 누적 정답률).
- 데이터는
app/public/data/<video>/의 사전 계산 JSON(폴리곤·시뮬레이션 결과)과 crop 썸네일이며, 재실행은pipeline/01~04순서.
6. 한계와 다음 단계
- 공식
facebook/sam3접근 승인(HF에서 사용자 신청 필요) 후 미러와 가중치 해시 대조. SAM 3.1(facebook/sam3.1, 동일 게이트)도 후보. - 다른 날/기기 영상으로 영상 간(cross-video) 검증. 지금은 동일 영상 내 짝수/홀수 프레임 분리라 상한선에 가깝다.
- 트래커를 SAM 3 비디오 모드 또는 ByteTrack 류로 교체하고, 트랙 단위 대표 임베딩(평균)으로 갤러리를 구성해 오매칭을 줄인다.
- 실시간화: 서버 GPU 추론 + 모바일 WebRTC/HTTP 프레임 스트리밍, 프롬프트 1개·입력 해상도 축소로 5 fps 이상 확보. 온디바이스는 SAM 3 크기상 어렵고 경량 검출기(YOLO 계열) 증류가 필요.
- 텍스트 DB 스키마에 출구 번호·노선·역명 필드를 파싱해 넣고, 이미지 유사도 + 텍스트 정확 일치의 2단 판정으로 위치 판별 정확도를 올린다.
산출물: data/out/(detections/ocr/eval JSON, textdb.sqlite, 임베딩 npy, sign_matcher.json), app/public/data/(UI 데이터), pipeline/(01~05 스크립트).