Files
s-canvas/README.md
2026-05-07 20:18:43 +09:00

405 lines
18 KiB
Markdown
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.
# 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. 자체 개발 자산이며, 외부 라이선스가 부여된 패키지는 각 패키지의 라이선스를 따릅니다.
문의: 프로젝트 관리자에게 직접 연락