Merge remote raw upload (184185c) into local lint-cleaned + Phase 0 history

Strategy: --allow-unrelated-histories with -X ours.
- Remote-only files: 0 (remote is subset of local).
- Local-only files: 80+ (Phase 0 산출물 + workspace 파편).
- Both-sides-different: 29 source/config files (local has lint cleanup applied,
  remote has raw upload). -X ours preserves local versions.
- Net effect on file tree: identical to pre-merge local; merge commit only links histories.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-08 14:52:00 +09:00

404
README.md
View File

@@ -0,0 +1,404 @@
# S-CANVAS
> **Generative Design & Visualization Engine**
> CAD 도면(DXF) · 공개 DEM · 위성영상 · Generative AI 를 결합해 **건설/토목 프로젝트의 3D 조감도와 구조물 시각화**를 자동 생성하는 데스크톱 엔진. **Saman Corp.** 자체 개발.
[![Python](https://img.shields.io/badge/python-3.9%2B-blue.svg)](https://www.python.org/)
[![Platform](https://img.shields.io/badge/platform-Windows%2010%2F11-lightgrey.svg)]()
[![GUI](https://img.shields.io/badge/GUI-CustomTkinter-success.svg)]()
[![3D](https://img.shields.io/badge/3D-PyVista%20%2B%20VTK-orange.svg)]()
---
## 1. 프로그램 개요
S-CANVAS 는 토목·건설 도면(DXF)을 **클릭 4번**에 사실 기반 3D 조감도로 변환하는 자동 파이프라인입니다.
```
[DXF 도면] [최종 결과물]
│ ▲
▼ │
레이어 분류 → 등고선/구조물 분리 AI 렌더링 (구조 보존)
│ ▲
▼ │
TIN 생성 (Delaunay 삼각화) ← 제어맵 추출 (Depth + Texture)
│ ▲
▼ │
DEM 자동 페치 (AWS Terrarium) → smoothstep blend │
│ │
▼ │
구조물 자동 빌드 (4단계): │
① 위치 인식 (PCA frame) │
② 굴착 pad + smoothstep 전이 │
③ TIN 재-Delaunay (수정된 지형) │
④ 3D 메시 배치 (취수탑/옹벽/수문/제수변실) │
│ │
▼ │
위성 타일 UV 매핑 (Seam-free 통합 메시) ────────────────────┘
```
### 1.1 핵심 진입점
- **`scanvas_maker.py`** — 메인 GUI 엔트리 (~6,300 LOC, CustomTkinter 기반)
- **`splash.py`** — 인트로 MP4 스플래시 (cv2 + Tk Toplevel)
- 메인 클래스: `SCanvasApp(ctk.CTk)` — 사이드바 워크플로 + 메인 맵뷰
- 상태 저장: `%LOCALAPPDATA%\S-CANVAS\` (DB · 로그 · 캐시)
### 1.2 모듈 구성
| 모듈 | 역할 |
|---|---|
| `scanvas_maker.py` | 메인 GUI, Step 1~4 워크플로 |
| `dem_extender.py` | AWS Terrarium DEM 페치 + 도넛 링 메시 |
| `geo_referencing.py` | 4점 매칭 GeoReferencing |
| `structure_placement.py` | 구조물 위치/방향 자동 인식 + TIN 굴착 |
| `structure_templates.py` | 구조물 유형 레지스트리 (Registry 패턴) |
| `intake_tower_*.py` | 취수탑 파서 + 3D 빌더 |
| `retaining_wall_*.py` | 옹벽 파서 + 3D 빌더 |
| `gate_*.py` | 수문 파서 + 3D 빌더 |
| `valve_chamber_*.py` | 제수변실 파서 + 3D 빌더 |
| `detail_parser.py` | 상세도면 DXF 치수 파서 (TEXT/MTEXT/DIMENSION) |
| `filename_classifier.py` | 파일명 → 구조물 유형 추정 |
| `dxf_geometry.py` | DXF 엔티티 → 좌표 추출 |
| `tile_downloader.py` | 위성 XYZ 타일 다운로드 |
| `gemini_renderer.py` | Gemini API/Vertex AI 렌더링 워커 |
| `structure_vlm_feedback.py` | Gemini Vision 기반 구조물 결과 검증 |
| `harness/` | 품질·재현성·이력 서브시스템 (SQLite + structlog) |
### 1.3 주요 기능
- **TIN 자동 생성**: DXF 등고선 → Delaunay 삼각화 → zero-basing
- **DEM 자동 확장**: 도면 범위 밖을 AWS 글로벌 DEM 으로 자동 채움 (3-Zone smoothstep 블렌드)
- **구조물 자동 빌드**: 도면 → 4단계 (위치인식 → 굴착 → TIN수정 → 3D배치). 취수탑·옹벽·수문·제수변실
- **Seam-free 통합 메시**: `merge_points=True` + `compute_normals(feature_angle=180)` 로 도면-DEM 경계 쉐이딩 불연속 제거
- **위성 타일 UV 매핑**: Google / ArcGIS / Bing / Vworld 등 9종 지원
- **AI 조감도 렌더링**: Gemini (Vertex / API Key) + Stability AI 3단계 폴백
- **VLM 피드백 루프**: Gemini Vision 으로 빌더 결과 ↔ 원본 도면 자동 검증
- **결정론적 재현성 (Harness)**: DXF 해시 → seed → 같은 입력 → 같은 출력
- **자동 품질 검증**: OpenCV 기반 해상도/선명도/채도 게이트
### 1.4 구조물 자동 빌드 워크플로 (핵심 차별화)
위성지도 결합 **이전** 에 실행되어 TIN 자체를 수정합니다. 구조물이 자연스럽게 지형에 묻히도록 4단계로 처리:
| 단계 | 내용 | 핵심 모듈 |
|---|---|---|
| **① 위치 인식** | DXF 폴리곤 추출 → PCA 로 frame 방향 결정 | `structure_placement.py` (`compute_orientation_from_points`) |
| **② 굴착 (Excavation)** | 폴리곤 평탄 pad + smoothstep 전이 — 구조물 발자국 영역만 매끈하게 | `structure_placement.py` (`apply_placement`) |
| **③ TIN 수정** | 굴착된 영역으로 Delaunay 재계산 → 메시 재구성 | `scanvas_maker.py` 내부 |
| **④ 3D 메시 배치** | 4개 quad 코너에 fit + `embed_offset` 으로 TIN 관통 방지 | `structure_placement.py` (`fit_meshes_to_quad`) |
**지원 구조물 4종** (`structure_types/structure_v1.yaml` 정의):
| 유형 | 빌더 | 입력 |
|---|---|---|
| 취수탑 (Intake Tower) | `intake_tower_3d_builder.py` | DXF 평면도 → 외곽 폴리곤 + 높이 |
| 옹벽 (Retaining Wall) | `retaining_wall_3d_builder.py` | DXF 단면도 → 단면 형상 + 길이 |
| 수문 (Gate) | `gate_3d_builder.py` | DXF 정면도 → 게이트 + 슬라브 |
| 제수변실 (Valve Chamber) | `valve_chamber_3d_builder.py` | DXF 평면도 → 박스 + 슬래브 + 점검구 |
**자동 분류**: `filename_classifier.py` 가 파일명 (예: "신설 취수탑.dxf") 으로 1차 후보 제시 → 사용자가 GUI 다이얼로그에서 최종 확정 → `structure_templates.REGISTRY` 가 적절한 빌더로 라우팅.
**치수 자동 추출**: `detail_parser.py` 가 DXF TEXT/MTEXT/DIMENSION 엔티티에서 치수값 파싱 → 빌더 파라미터로 변환 (수동 입력 불필요).
**메시 방향 보정** (디버깅에서 얻은 교훈):
- CW(시계방향) picks + Y-flip + detail/TIN 상대회전 + PCA frame_angle
- → 앞뒤 뒤집힘 / 좌우 반전 문제 자동 해결
**VLM 검증 루프**: 빌드 완료 후 `structure_vlm_feedback.py` 가 렌더 결과와 원본 DXF 평면도를 Gemini Vision 에 동시 전송 → 차이점 자연어 응답 → 사용자가 재시도 결정.
---
## 2. 필수 Package
### 2.1 시스템 요구사항
- **Python**: 3.9+ (권장 3.11+, 빌드 머신 검증 = 3.9.13)
- **Platform**: Windows 10/11 (Linux/macOS 는 GUI 일부 제한)
- **메모리**: 8GB+ (PyVista + VTK + 위성 타일 캐시)
- **디스크**: 1GB+ 여유 (DEM/타일 캐시 포함)
### 2.2 설치
```bash
pip install -r requirements.txt
```
### 2.3 카테고리별 패키지
```text
[GUI]
customtkinter==5.2.2 모던 Tk 기반 GUI 프레임워크
tkintermapview==1.29 위성 지도 인터랙티브 뷰
Pillow==11.3.0 이미지 처리 (PIL)
[3D / Mesh]
pyvista==0.46.5 메시 처리 + 렌더링 (VTK 자동 설치, ~150MB)
[Geospatial / DXF]
ezdxf==1.4.2 DXF 파싱 (R12~R2024)
pyproj==3.6.1 좌표계 변환 (EPSG)
rasterio==1.4.3 (선택) 로컬 GeoTIFF DEM (NGII 5m 등)
[Numerical]
numpy==2.0.2
scipy==1.13.1 Delaunay 삼각화, KDTree
matplotlib==3.9.4 signed-distance polygon paths
[Image / Video]
opencv-python==4.13.0.92 인트로 MP4 + Harness QualityValidator
[Network]
requests==2.32.5 HTTP 타일/DEM/AI 페치
[AI Rendering]
google-genai==1.47.0 Gemini SDK (Vertex AI / API Key 통합)
google-auth==2.49.2
[Persistence / Logging]
SQLAlchemy==2.0.49 JobRecord ORM
structlog==25.5.0 구조화 로깅
PyYAML==6.0.3 prompt_v*.yaml / structure_v*.yaml
[Build (선택)]
pyinstaller==6.18.0 Windows .exe 빌드
```
---
## 3. 간단 사용법
### 3.1 실행
```bash
python scanvas_maker.py
```
실행 흐름:
1. `splash.py``Design/logo_intro.mp4` 8초 인트로 재생 (페이드 인/아웃)
2. `SCanvasApp` 메인 창 기동 (1200×900)
3. 사이드바에서 워크플로 진행
### 3.2 사이드바 워크플로
```
SETTINGS
├─ Satellite Source (Google / ArcGIS / Vworld 등 선택)
├─ Vworld API Key (Vworld 사용 시)
├─ AI Engine (Gemini Vertex / Gemini API / Stability)
├─ GCP / API Key (선택한 엔진의 인증 정보)
└─ Project CRS (EPSG:5187 / 5186 / 5185 / 5181 / 3857)
WORKFLOW
1. TIN 생성 (DXF) ← DXF 파일 선택 + Delaunay 삼각화
🎯 TIN 이용 범위 ← (선택) 정밀 보존 영역 지정
1.5 DEM 으로 TIN 확장 ← AWS Terrarium 도넛 링 메시 추가
── 구조물 상세 3D 빌드 ← 위치인식 → 굴착 → TIN수정 → 3D배치
(취수탑·옹벽·수문·제수변실)
2. 위성지도 결합 ← 위성 타일 다운로드 + UV 매핑
(구조물 굴착 반영된 TIN 위에 매핑)
3. 제어맵 추출 ← Depth / Texture 캡처 (PyVista offscreen)
4. AI 렌더링 ← 최종 조감도 생성 (Gemini / Stability)
OPTIONS
├─ □ 와이어프레임 보기
├─ 뷰 버퍼 (%) (Step 2/3 외곽 여유)
├─ □ 지형 확장 (DEM)
└─ Light / Dark 테마 토글
```
### 3.3 결과 파일
| 파일 | 의미 |
|---|---|
| `rendered_birdseye.png` | 최종 AI 렌더 결과 |
| `capture_textured.png` | Step 3 위성+DEM 합성 control map |
| `depth_map.png` | Step 3 깊이 맵 |
| `lineart_map.png` | Step 3 라인아트 |
| `guide_composite.png` | 합성 가이드 이미지 |
| `scanvas_jobs.db` | SQLite 작업 이력 (Harness) |
| `scanvas_harness.log` | structlog 출력 |
### 3.4 인증 / API 키 설정
**모든 키는 코드에 하드코딩되지 않습니다.** 환경변수 또는 사이드바 입력란을 사용하세요.
| 키 | 환경변수 | 발급처 | 용도 |
|---|---|---|---|
| GCP Service Account | (파일) `gcp-key.json` | GCP Console → IAM | Gemini Vertex AI |
| GCP Project ID | `GCP_PROJECT_ID` | (gcp-key.json 자동 추출) | Vertex AI 라우팅 |
| GCP Location | `GCP_LOCATION` | — | Vertex AI 리전 (기본 `global`) |
| Gemini API Key | (UI 입력) | [aistudio.google.com](https://aistudio.google.com/) | Gemini API 직접 호출 |
| Stability API Key | (UI 입력) | [platform.stability.ai](https://platform.stability.ai/) | Stability AI 폴백 |
| **Vworld API Key** | **`VWORLD_API_KEY`** | [vworld.kr/dev](https://www.vworld.kr/dev/v4api.do) | 한국 위성 타일 (선택) |
| Google API Key (VLM) | `GOOGLE_API_KEY` 또는 `GEMINI_API_KEY` | aistudio.google.com | structure_vlm_feedback |
**Windows 환경변수 설정 예**:
```powershell
# 영구 (사용자 환경)
[Environment]::SetEnvironmentVariable("VWORLD_API_KEY", "your-key-here", "User")
[Environment]::SetEnvironmentVariable("GCP_PROJECT_ID", "your-project-id", "User")
# 현재 세션만
$env:VWORLD_API_KEY = "your-key-here"
```
**Gemini Vertex AI (서비스 어카운트)**:
1. GCP 프로젝트에서 서비스 어카운트 키 (JSON) 발급
2. `gcp-key.json` 을 프로젝트 루트 또는 `%LOCALAPPDATA%\S-CANVAS\` 에 배치
3. 자동으로 `GOOGLE_APPLICATION_CREDENTIALS` 설정 + `project_id` 추출
4. **`gcp-key.json``.gitignore` 차단됨** — 절대 커밋 금지
---
## 4. URL 기반 외부 데이터 소스
S-CANVAS 가 사용하는 모든 외부 URL 엔드포인트입니다. **인터넷 연결 필요**.
### 4.1 DEM (지형 고도) — 인증 불필요
| 소스 | URL 패턴 | 비고 |
|---|---|---|
| AWS Open Terrain Tiles | `https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png` | 글로벌, ~30m 정확도, **API 키 없음**. terrarium PNG 디코딩: `elev_m = (R*256 + G + B/256) - 32768`. SHA1(bbox+zoom) 캐시 → 재실행 시 네트워크 0 |
`dem_extender.py:48` 에 정의 → `cache/dem/` 에 PNG 캐시.
로컬 GeoTIFF (`cache/dem/local.tif`) 가 있으면 우선 사용 (rasterio 필요).
### 4.2 위성 타일 (Texture)
`scanvas_maker.py:248-258` — UI 에서 사용자 선택.
| 서버 | URL 패턴 | 인증 |
|---|---|---|
| Google Satellite | `https://mt{s}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}` | 불필요 |
| Google Hybrid | `https://mt{s}.google.com/vt/lyrs=y&x={x}&y={y}&z={z}` | 불필요 |
| ArcGIS World Imagery | `https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}` | 불필요 |
| ArcGIS Hybrid | `https://server.arcgisonline.com/ArcGIS/rest/services/Reference/World_Boundaries_and_Places/MapServer/tile/{z}/{y}/{x}` | 불필요 |
| Bing Aerial | `https://ecn.t{s}.tiles.virtualearth.net/tiles/a{q}.jpeg?g=1` | 불필요 |
| OpenStreetMap | `https://tile.openstreetmap.org/{z}/{x}/{y}.png` | 불필요 |
| OpenTopo | `https://tile.opentopomap.org/{z}/{x}/{y}.png` | 불필요 |
| Vworld 위성 | `https://api.vworld.kr/req/wmts/1.0.0/{vworld_key}/Satellite/{z}/{y}/{x}.jpeg` | **Vworld 키 필요** |
| Vworld 기본 | `https://api.vworld.kr/req/wmts/1.0.0/{vworld_key}/Base/{z}/{y}/{x}.png` | **Vworld 키 필요** |
| Vworld 하이브리드 | `https://api.vworld.kr/req/wmts/1.0.0/{vworld_key}/Hybrid/{z}/{y}/{x}.png` | **Vworld 키 필요** |
`tile_downloader.py:11` 에서 `tile.openstreetmap.org` 폴백 처리.
### 4.3 AI 렌더링 — 인증 필수
| 엔진 | 엔드포인트 | 인증 방식 |
|---|---|---|
| **Gemini (Vertex AI)** | `google-genai` SDK 내부 호출 | GCP Service Account (`gcp-key.json`) — `GOOGLE_APPLICATION_CREDENTIALS` 자동 설정 |
| **Gemini (API Key)** | `google-genai` SDK 내부 호출 | aistudio.google.com 키 |
| **Stability Conservative Upscale** | `https://api.stability.ai/v2beta/stable-image/upscale/conservative` | Bearer API Key |
| **Stability Creative Upscale** | `https://api.stability.ai/v2beta/stable-image/upscale/creative` | Bearer API Key |
| **Stability img2img (sd3.5-large)** | `https://api.stability.ai/v2beta/stable-image/generate/sd3` | Bearer API Key |
`scanvas_maker.py:6432` (Conservative/Creative) · `scanvas_maker.py:6477` (img2img) · `gemini_renderer.py` (Gemini 워커).
Stability 는 3단계 자동 폴백 (Conservative → Creative → img2img).
### 4.4 위성 지도 미리보기 (GUI)
`scanvas_maker.py:572``tkintermapview` 의 기본 타일 서버:
- `https://mt0.google.com/vt/lyrs=s&x={x}&y={y}&z={z}`
GeoReferencing 4점 매칭 시 사용자가 위성 위에 클릭하는 인터랙티브 맵.
### 4.5 캐시 정책
| 캐시 위치 | 내용 | 무효화 키 |
|---|---|---|
| `cache/dem/` | AWS Terrarium PNG 타일 | SHA1(bbox + zoom) |
| `cache/tiles/` | 위성 타일 PNG | URL 해시 |
| `scanvas_jobs.db` | SQLite Job 이력 (재현성) | dxf_hash + seed + prompt_hash |
**오프라인 동작**: 캐시 hit 시 네트워크 호출 0회. 첫 실행 후 동일 영역은 오프라인 가능.
---
## 5. 좌표계 / 단위
지원 EPSG (사이드바 선택):
| EPSG | 이름 | 영역 |
|---|---|---|
| **5187** | Korea 2000 / Central Belt 2010 | 한국 중부 (서울·경기·충청) — **기본값** |
| 5186 | Korea 2000 / Central Belt | 한국 중부 (구) |
| 5185 | Korea 2000 / West Belt 2010 | 한국 서부 |
| 5181 | Korea 2000 / Unified CS | 통합 좌표계 |
| 3857 | Web Mercator | 글로벌 (위성 타일 호환) |
**단위 강제**: DXF `$INSUNITS` 헤더가 부정확한 경우가 많아 **항상 m 로 강제** (`unit_override="m"`). KATEC 류 대형 좌표값을 mm 로 오판하는 사고 차단.
**Zero-Basing**: 큰 절대좌표(EPSG:5187 의 200,000m+) 의 float32 정밀도 손실 방지 → bbox 좌하단을 origin 으로 평행이동.
---
## 6. 디렉토리 구조
```
.
├── scanvas_maker.py # 메인 진입점
├── splash.py # 인트로 스플래시
├── requirements.txt # 의존성
├── S-CANVAS_brief.md # 상세 기술 문서 (NotebookLM 소스)
├── CHANGELOG.md # 수정 이력 (역순)
├── dem_extender.py # DEM 페치/링 메시
├── geo_referencing.py # 4점 매칭
├── structure_placement.py # 구조물 배치/굴착
├── structure_templates.py # 구조물 레지스트리
├── intake_tower_parser.py + _3d_builder.py
├── retaining_wall_parser.py + _3d_builder.py
├── gate_parser.py + _3d_builder.py
├── valve_chamber_parser.py + _3d_builder.py
├── detail_parser.py
├── filename_classifier.py
├── polygon_reconstructor.py
├── dxf_geometry.py
├── tile_downloader.py
├── view_detector.py
├── view_reconstructor.py
├── optional_detector.py
├── gemini_renderer.py
├── structure_vlm_feedback.py
├── resource_paths.py # PyInstaller 번들/소스 경로 분기
├── harness/ # 품질·재현성 서브시스템
│ ├── seed_manager.py # DXF 해시 → seed
│ ├── quality_validator.py # OpenCV 자동 QA
│ ├── prompt_registry.py # YAML 버전 관리
│ └── logger.py # SQLite ORM + structlog
├── prompt_templates/
│ └── prompt_v1.yaml # 시간대/앙각/구조보존 프롬프트
├── structure_types/
│ └── structure_v1.yaml # 구조물 유형 정의
├── Design/ # 브랜딩 자산
│ ├── logo_V2.png
│ ├── SAMAN_CI.gif
│ └── logo_intro.mp4 # 8s, 24fps, 1280×720
├── SAMPLE_CAD/ # 테스트용 DXF 샘플
└── cache/dem/ # 런타임 DEM 캐시 (gitignore)
```
---
## 7. 추가 문서
- **`S-CANVAS_brief.md`** — 시스템 아키텍처/혁신 포인트/메모리화된 결정 사항 (개발자/리뷰어용 심층 자료)
- **`CHANGELOG.md`** — 모든 수정 이력 (역순, 날짜/파일/사유/diff 요지)
- **`Build_log.txt`** — 사용자 원본 요청 이력
---
## 8. 라이선스 / 저작권
© 2026 **Saman Corp.** All rights reserved.
본 프로그램은 Saman Corp. 자체 개발 자산이며, 외부 라이선스가 부여된 패키지는 각 패키지의 라이선스를 따릅니다.
문의: 프로젝트 관리자에게 직접 연락