S-CANVAS (Saman Corp.) — DXF + DEM + AI 기반 3D 조감도 생성 엔진. ~24k LOC Python (scanvas_maker.py 7072 LOC GUI + 구조물 파서/빌더 다수). 이 커밋은 7-iter cleanup이 적용된 상태로 import: - F821 8 + B023 6: 비동기 lambda + except/loop 변수 캡처 NameError (Py3.13에서 reproduce 확인된 진짜 버그) - RUF012 4 + RUF013 1: ClassVar / implicit Optional 명시화 - F811/B905/B904/F401/F841/W293/F541/UP/SIM/RUF/PLR 700+ cleanup/modernization 신규 파일: - ruff.toml: target=py313, Korean unicode/저자 스타일/도메인 복잡도 무력화 - requirements-py313.txt: pyproj>=3.7, scipy>=1.14, numpy>=2.0.2 (Py3.13 wheel) - .gitignore: gcp-key.json, 캐시, 백업, 생성 이미지 제외 검증: ruff 0 errors, py_compile 0 errors, import 33/33 OK on Py3.13.13. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
738 lines
31 KiB
Markdown
738 lines
31 KiB
Markdown
# S-CANVAS — 프로그램 핵심 소개 (NotebookLM 발표자료 소스)
|
||
|
||
> **목적**: 본 문서는 NotebookLM 에 업로드하여 발표 슬라이드, Audio Overview,
|
||
> 마인드맵 등을 자동 생성하기 위한 **단일 소스 텍스트** 입니다. S-CANVAS
|
||
> 프로그램의 정체성·아키텍처·핵심 기술·워크플로·차별화 포인트를 한 파일에
|
||
> 응축했습니다.
|
||
|
||
---
|
||
|
||
## 1. 한 줄 요약
|
||
|
||
**S-CANVAS** — *Generative Design & Visualization Engine*. CAD 도면(DXF)과 실제
|
||
공개 DEM·위성영상·Generative AI 를 결합해 **건설/토목 프로젝트의 3D 조감도와
|
||
구조물 시각화**를 자동 생성하는 데스크톱 엔진. **Saman Corp.** 자체 개발.
|
||
|
||
- 원래 명칭: `EG-VIEW` (AI 기반 조감도 생성 시스템)
|
||
- 2026-04-24 리브랜딩: `S-CANVAS` (Generative Design & Visualization Engine)
|
||
- 메인 진입점: `scanvas_maker.py`
|
||
- 플랫폼: Windows 10/11, Python + CustomTkinter GUI
|
||
|
||
---
|
||
|
||
## 2. 배경 & 문제 정의
|
||
|
||
### 2.1 기존 토목·건설 시각화의 한계
|
||
|
||
전통적인 CAD 평면도는 **2D**이며, 등고선·구조물 윤곽만 담겨 있어 실제 지형
|
||
맥락(주변 산세, 강, 식생, 도로망)이 빠져 있다. 발주처 보고·주민 설명회에서는
|
||
**"실제 어떻게 보이는가"** 가 중요하지만, 기존 워크플로에서는:
|
||
|
||
1. CAD 도면을 3D 모델러(Civil 3D/Revit/Rhino) 로 다시 그리고
|
||
2. GIS 데이터를 별도로 받아 좌표 정합
|
||
3. 위성영상을 매핑하고
|
||
4. 렌더 엔진(Lumion/Twinmotion/V-Ray) 으로 조명·재질·하늘 설정
|
||
5. 카메라 앵글 잡고 이미지 출력
|
||
|
||
이 과정이 **수일에서 수주** 걸리며, 변경이 생길 때마다 대부분의 단계를 다시
|
||
수행해야 한다.
|
||
|
||
### 2.2 S-CANVAS 의 가설
|
||
|
||
> *"DXF 등고선·구조물 레이어 + 공개 DEM + 위성타일 + Generative AI 만으로,
|
||
> 클릭 4번에 사실 기반 3D 조감도를 만들 수 있다."*
|
||
|
||
- DXF 의 등고선 → TIN(Triangulated Irregular Network) 자동 생성
|
||
- DXF 범위 밖 지형 → AWS Open Terrain Tiles(글로벌 DEM) 자동 채움
|
||
- 표면 텍스처 → Google/ArcGIS/Vworld 위성 타일을 UV 매핑
|
||
- 최종 사실감 향상 → Gemini/Stability AI 가 **구조 보존 모드**로 재렌더
|
||
|
||
### 2.3 핵심 가치 제안 (Value Proposition)
|
||
|
||
| 기존 | S-CANVAS |
|
||
|---|---|
|
||
| 모델링 수일 | TIN 생성 30초 |
|
||
| 외부 라이선스 GIS | 공개 DEM 자동 페치 |
|
||
| 수동 좌표 정합 | 4점 매칭 자동 GeoRef |
|
||
| 렌더 1회 수시간 | AI 렌더 1분 이내 |
|
||
| 변경 → 재모델링 | 변경 → 재실행 (캐시 재활용) |
|
||
|
||
---
|
||
|
||
## 3. 시스템 아키텍처
|
||
|
||
### 3.1 모듈 구성 (Top Level)
|
||
|
||
```
|
||
scanvas_maker.py # 메인 GUI 엔트리 (~6300 LOC, CustomTkinter)
|
||
├── splash.py # 인트로 MP4 스플래시 (cv2 + Tk Toplevel)
|
||
├── dem_extender.py # AWS Terrarium DEM 페치 + 도넛 링 메시
|
||
├── geo_referencing.py # 4점 매칭 GeoReferencing 워크플로
|
||
├── structure_placement.py # 구조물 위치/방향 자동 인식 + TIN 굴착
|
||
├── structure_templates.py # 구조물 유형 레지스트리 (Registry 패턴)
|
||
├── intake_tower_3d_builder.py # 취수탑 3D
|
||
├── intake_tower_parser.py # 취수탑 DXF 치수 파싱
|
||
├── retaining_wall_3d_builder.py # 옹벽 3D
|
||
├── retaining_wall_parser.py
|
||
├── gate_3d_builder.py # 수문 3D
|
||
├── gate_parser.py
|
||
├── valve_chamber_3d_builder.py # 제수변실 3D
|
||
├── valve_chamber_parser.py
|
||
├── detail_parser.py # 상세도면 DXF 치수 파서 (TEXT/MTEXT/DIMENSION)
|
||
├── filename_classifier.py # 파일명 → 구조물 유형 추정
|
||
├── polygon_reconstructor.py
|
||
├── dxf_geometry.py # DXF 엔티티 → 좌표
|
||
├── tile_downloader.py # 위성 XYZ 타일 다운로드
|
||
├── view_detector.py / view_reconstructor.py # 도면 뷰 영역 인식
|
||
├── optional_detector.py
|
||
├── gemini_renderer.py # Gemini API/Vertex AI 렌더링 워커
|
||
├── structure_vlm_feedback.py # Gemini Vision 으로 구조물 결과 검증
|
||
├── egview_maker.py 의 후신: # (이전 명칭, 현재 scanvas_maker.py 로 통합/리네임)
|
||
│
|
||
└── harness/ # 품질·재현성·이력 서브시스템
|
||
├── seed_manager.py # DXF 해시 기반 결정론적 seed
|
||
├── quality_validator.py # OpenCV 기반 이미지 자동 QA
|
||
├── prompt_registry.py # 프롬프트 버전 관리 (YAML)
|
||
└── logger.py # SQLite ORM (JobRecord) + structlog
|
||
|
||
prompt_templates/prompt_v1.yaml # 렌더 프롬프트 (시간대/앙각/구조보존)
|
||
structure_types/structure_v1.yaml # 구조물 유형 정의
|
||
|
||
Design/ # 브랜딩 자산
|
||
├── logo_V2.png # 로고 (다크 bg → 런타임 스트립)
|
||
├── Logo.png # 이전 로고 (투명 bg)
|
||
├── SAMAN_CI.gif # Saman Corp 크레딧
|
||
├── logo_intro.mp4 # 인트로 스플래시 (8s, 24fps, 1280×720)
|
||
├── homepage_sample.png # UI 디자인 레퍼런스
|
||
└── page_sample.png
|
||
|
||
cache/dem/ # AWS Terrarium 타일 캐시
|
||
scanvas_jobs.db # SQLite 작업 이력 (Harness)
|
||
scanvas_harness.log # structlog 출력
|
||
scanvas_diagnostic.log # Step 1 진단 로그
|
||
```
|
||
|
||
### 3.2 데이터 흐름 (Pipeline)
|
||
|
||
```
|
||
[DXF 도면] → [레이어 분류] → [등고선/구조물 분리]
|
||
↓
|
||
[GeoRef 4점 매칭] → [투영 CRS 결정 (EPSG:5187 등)]
|
||
↓
|
||
[TIN 생성 (Delaunay)] → [TIN core 정밀 영역 지정 (선택)]
|
||
↓
|
||
[DEM 자동 페치 (AWS Terrarium)] → [도넛 링 메시] → [smoothstep blend]
|
||
↓
|
||
[seam-free 통합 메시 (merge_points + compute_normals)]
|
||
↓
|
||
[위성 타일 다운로드] → [UV 매핑 (texture_map_to_plane)]
|
||
↓
|
||
[3D 미리보기 (PyVista)] → [카메라 앵글 캡처] → [제어맵]
|
||
↓
|
||
[Gemini/Stability AI 렌더링 (구조 보존 모드)]
|
||
↓
|
||
[QualityValidator 자동 검증] → [scanvas_jobs.db 이력 기록]
|
||
↓
|
||
[고해상도 PNG 출력]
|
||
```
|
||
|
||
### 3.3 외부 의존성
|
||
|
||
**Python 라이브러리** (주요):
|
||
- `customtkinter` — 모던 Tk GUI 프레임워크
|
||
- `tkintermapview` — 위성지도 뷰
|
||
- `ezdxf` — DXF 파싱
|
||
- `numpy`, `scipy` — 수치 연산, Delaunay
|
||
- `pyvista` + `vtk` — 3D 메시 렌더링
|
||
- `pyproj` — 좌표계 변환
|
||
- `Pillow (PIL)` — 이미지 처리
|
||
- `opencv-python (cv2)` — 비디오 / 이미지 검증
|
||
- `requests` — HTTP 타일 페치
|
||
- `google-genai` — Gemini SDK (Vertex AI 또는 API Key)
|
||
- `sqlalchemy` + `structlog` — 이력 추적
|
||
- `PyYAML` — 프롬프트/구조물 정의
|
||
|
||
**외부 데이터 소스**:
|
||
- AWS Open Terrain Tiles (`s3.amazonaws.com/elevation-tiles-prod/terrarium`)
|
||
— 글로벌 DEM, ~30m 정확도, API 키 불필요
|
||
- Google Satellite / ArcGIS World Imagery / Bing Aerial / Vworld
|
||
— 위성 타일 (사용자 선택)
|
||
- Google Vertex AI / Gemini API — 생성형 렌더링
|
||
- (옵션) Stability AI API — img2img 폴백
|
||
|
||
---
|
||
|
||
## 4. 핵심 워크플로 (Step-by-Step)
|
||
|
||
### Step 0 — DXF 로드 & 레이어 분류
|
||
|
||
DXF 를 열면 자동으로 레이어를 분석해 **구조물 유형**(취수탑/제수변실/옹벽/수문/
|
||
지형 등고선/도로/하천 등)을 추정. `filename_classifier.py` 가 파일명 패턴(예:
|
||
"신설 취수탑.dxf") 으로 1차 후보를 제시하고, 사용자가 GUI 다이얼로그에서 최종
|
||
확정. 결과는 `structure_v1.yaml` 의 정의에 따라 빌더로 라우팅.
|
||
|
||
### Step 1 — TIN 생성 (DXF)
|
||
|
||
지형 레이어의 등고선 점·LINE/LWPOLYLINE 정점을 추출 → Delaunay 삼각화 →
|
||
**zero-basing** (원점을 도면 bbox 좌하단으로 평행이동, 부동소수점 정밀도 보존) →
|
||
PyVista PolyData 메시 생성.
|
||
|
||
**핵심 디테일**:
|
||
- 단위 자동 감지 → KATEC 좌표(EPSG:5187 등) 는 항상 m 로 강제 (`unit_override="m"`)
|
||
- DEM datum offset 사전 계산 → 이후 단계에서 **공통 offset 재사용**으로 seam Z 단차 0 보장
|
||
- DEM 격자도 사전 페치 → `_dem_elev_grid` / `_dem_grid_bounds` 로 캐시
|
||
|
||
### Step 1.5 — DEM 으로 TIN 확장
|
||
|
||
DXF 범위 밖이 절벽처럼 잘리는 문제 해결. AWS Terrarium 에서 **외곽 도넛 링**을
|
||
받아 TIN 과 이어 붙임.
|
||
|
||
**3-Zone 구조** (메모리화된 핵심 결정):
|
||
1. **Core** (`tin_core_bbox` 안): 원본 TIN 100% 보존. 사용자가 정밀 영역으로 지정.
|
||
2. **Transition** (core ~ bbox, `blend_width_m`): smoothstep `3t² − 2t³` 으로 TIN ↔ DEM Z 부드럽게 블렌드.
|
||
3. **DEM 외곽 링**: AWS DEM 으로 채움.
|
||
|
||
**Seam 처리 (다층 방어)**:
|
||
- **수직 datum 보정**: TIN 경계 근처 점들의 (TIN Z – DEM Z) 중앙값을 offset 으로 차감
|
||
- **공유 정점 weld**: TIN bbox 변 정점을 DEM 링 inner 경계로 강제 → 동일 XY 공유
|
||
- **smoothstep feather**: 경계 0 = TIN, feather_m = DEM 으로 C¹ 연속
|
||
- **벽 컷 (slope_ratio 기반)**: `slope_ratio = z_span / max_edge` 가 4.0 (≈76°) 초과 + Z스팬 30m + max_edge 5m 충족 시 "벽 삼각형" 으로 컷. **절대 Z 컷은 절대 사용하지 않음** (구멍이 뚫림 — 메모리화된 교훈)
|
||
- **링 Laplacian 1pass**: 톱니 fin 마지막 평활. 경계 정점은 pin
|
||
|
||
### Step 2 — 위성지도 결합 (Draping)
|
||
|
||
선택한 타일 서버에서 **확장된 bbox 전체**의 위성 이미지 다운로드 → 단일 큰
|
||
이미지로 합성 → `texture_map_to_plane` 으로 UV 매핑 → 텍스처 입힌 3D 메시.
|
||
|
||
**Seam-free 통합 렌더링** (2026-04-24 후속 수정):
|
||
- 이전: `tin_mesh` 와 `tin_extension_mesh` 가 두 개의 별도 PolyData → 경계에서
|
||
쉐이딩 불연속 (사각 선이 보이는 증상)
|
||
- 수정: `merge(merge_points=True, tolerance=0.01)` 로 공유 정점 weld → 위상적
|
||
단일 표면. `compute_normals(feature_angle=180)` 로 모든 edge smooth → seam
|
||
법선 평활. UV 좌표는 `_uv_mapping_params` 저장 후 merge 후 재적용.
|
||
|
||
### Step 3 — 제어맵 추출
|
||
|
||
PyVista 카메라 앵글로 오프스크린 캡처:
|
||
- `capture_textured.png` — 위성+DEM 합성된 control map
|
||
- `capture_depth.png` — 깊이 맵 (Eye-Dome Lighting 활성화)
|
||
|
||
이 2개 이미지가 Step 4 의 AI 렌더 입력.
|
||
|
||
### Step 4 — AI 렌더링 (구조 보존 모드)
|
||
|
||
`prompt_templates/prompt_v1.yaml` 기반으로 프롬프트 구성:
|
||
- **시간대 프리셋**: daytime / sunset / night / dawn / overcast
|
||
- **앙각 프리셋**: top_down / high_angle / oblique / low_angle
|
||
- **구조 보존 지시**: "maintain exact terrain shape, contours, and layout from the input image"
|
||
- **품질 향상**: "8K ultra sharp detail, professional drone photography quality"
|
||
- **네거티브**: "blurry, distorted, watermark, changed terrain layout, moved structures..."
|
||
|
||
**렌더 엔진 3종**:
|
||
1. **Gemini (Vertex AI)** — google-genai SDK, GCP 인증
|
||
2. **Gemini (API Key)** — google-genai SDK, AI Studio 키
|
||
3. **Stability AI (API)** — 3단계 폴백:
|
||
1. Conservative Upscale (원본 보존 최우선)
|
||
2. Creative Upscale (조금 더 창의적)
|
||
3. img2img 초저강도 (strength=0.2)
|
||
|
||
각 렌더 호출은 Harness 가 **결정론적 seed** + **prompt 버전** + **품질 점수**를
|
||
자동 기록 → 동일 입력에 동일 출력 보장.
|
||
|
||
---
|
||
|
||
## 5. 핵심 기술 컴포넌트 / 혁신 포인트
|
||
|
||
### 5.1 DEM 자동 통합 (`dem_extender.py`)
|
||
|
||
- **AWS Open Terrain Tiles** terrarium PNG 디코딩:
|
||
`elev_m = (R*256 + G + B/256) - 32768`
|
||
- 좌표계 변환: 투영 CRS(예: EPSG:5187) → WGS84 → 타일 좌표 (Web Mercator z/x/y)
|
||
- 캐시: SHA1(bbox+zoom) 으로 PNG 캐시 → 재실행 시 네트워크 0
|
||
- **로컬 GeoTIFF 우선**: `cache/dem/local.tif` 가 있으면 NGII 5m DEM 등 고정밀
|
||
데이터 우선 사용 (rasterio 필요)
|
||
|
||
### 5.2 Seam-free 메시 통합 (Render-fix 2026-04-24)
|
||
|
||
> 원래 메모리: "벽 이슈는 create_tin_from_dxf 부터 점검. 판정은 slope_ratio 만,
|
||
> 절대 Z 컷은 구멍 낸다."
|
||
|
||
**문제**: TIN+DEM 을 두 개 별도 PolyData 로 add_mesh 하면 normal 평균이 메시
|
||
경계를 못 넘어 사각 쉐이딩 선이 보임.
|
||
|
||
**해결 (1순위 + 2순위 동시)**:
|
||
```python
|
||
merged = target.merge(ext_mesh, merge_points=True, tolerance=0.01)
|
||
if not isinstance(merged, pv.PolyData):
|
||
merged = merged.extract_surface()
|
||
# 텍스처 모드: UV 재적용 (TCoords 가 merge 에서 소실되므로)
|
||
if textured and uv_params:
|
||
merged = merged.texture_map_to_plane(origin=..., point_u=..., point_v=...,
|
||
inplace=False)
|
||
merged.compute_normals(feature_angle=180.0, auto_orient_normals=True,
|
||
consistent_normals=True, inplace=True)
|
||
```
|
||
|
||
- `merge_points=True` → 공유 경계 정점 **물리적 weld** (1순위 unified Delaunay 효과)
|
||
- `feature_angle=180` → 모든 edge smooth, 경계 normal 평균 (2순위 normal averaging 효과)
|
||
- 폴백: 실패 시 기존 2-mesh 렌더 경로 유지
|
||
|
||
### 5.3 구조물 자동 빌드 (`structure_*_3d_builder.py`)
|
||
|
||
| 유형 | 빌더 | 입력 |
|
||
|---|---|---|
|
||
| 취수탑 | `intake_tower_3d_builder.py` | DXF 평면도 → 외곽 폴리곤 + 높이 |
|
||
| 옹벽 | `retaining_wall_3d_builder.py` | DXF 단면도 → 단면 + 길이 |
|
||
| 수문 | `gate_3d_builder.py` | DXF 정면도 → 게이트 형상 + 슬라브 |
|
||
| 제수변실 | `valve_chamber_3d_builder.py` | DXF 평면도 → 박스 + 슬래브 + 점검구 |
|
||
|
||
**구조물 위치인식 → 굴착 → TIN수정 → 3D배치** (메모리화된 워크플로):
|
||
1. DXF 레이어에서 폴리곤 인식
|
||
2. PCA 로 frame 방향 결정 (`compute_orientation_from_points`)
|
||
3. TIN 에 **굴착 pad** 영역 평탄화 + smoothstep 전이 + Delaunay 재계산
|
||
4. 메시 4개 quad 코너에 fit (`fit_meshes_to_quad`)
|
||
5. 구조물 메시를 `embed_offset` 으로 약간 묻어 TIN 관통 방지
|
||
|
||
**메시 방향 보정** (메모리화된 디테일):
|
||
- CW(시계방향) picks + Y-flip + detail/TIN 상대회전 + PCA frame_angle
|
||
→ 앞뒤 뒤집힘 문제 해결
|
||
|
||
### 5.4 VLM 피드백 루프 (`structure_vlm_feedback.py`)
|
||
|
||
Gemini Vision 으로 **빌더 결과물의 시각적 정합성**을 자동 검증.
|
||
- Render 후 이미지 + 원본 DXF 평면도를 함께 모델에 보냄
|
||
- 차이점 자연어로 응답 받음
|
||
- 사용자에게 표시해 재시도 결정
|
||
|
||
### 5.5 Geo-Referencing 3단계 (`geo_referencing.py`)
|
||
|
||
> 메모리: "미리보기 → 위치설정(4점 매칭) → 확정"
|
||
|
||
1. **미리보기**: DXF bbox 를 위성지도에 임시 투영 (대략 위치)
|
||
2. **위치설정**: 사용자가 DXF 의 4 모서리를 실제 위성지도 위치와 매칭 클릭
|
||
3. **확정**: Affine transform 행렬 계산 → 투영 CRS(EPSG:5187 등) 자동 결정
|
||
|
||
### 5.6 Harness — 재현성 + 품질 + 이력
|
||
|
||
세 컴포넌트가 모든 AI 렌더 호출을 감싼다:
|
||
|
||
**SeedManager**: `SHA256(dxf_file_hash)[:4] → uint32 seed`. 같은 DXF → 같은 seed.
|
||
|
||
**QualityValidator**: 3개 게이트
|
||
1. 해상도 ≥ 1024px
|
||
2. Laplacian variance ≥ 50.0 (선명도)
|
||
3. HSV saturation 평균 ≥ 0.15 (단색 평면 출력 탐지)
|
||
|
||
**JobLogger** + SQLite `JobRecord` ORM:
|
||
```
|
||
id · dxf_path · dxf_hash · timestamp
|
||
seed · prompt_version · prompt_hash ← 재현성 3종
|
||
status (pending/running/done/failed)
|
||
output_path · quality_score · latency_ms
|
||
error_message
|
||
```
|
||
|
||
**PromptRegistry**: `prompt_v*.yaml` 버전 관리. 비교/저장/해시 역조회 API.
|
||
|
||
---
|
||
|
||
## 6. GUI 디자인 & 브랜딩
|
||
|
||
### 6.1 디자인 철학 (homepage_sample 참고)
|
||
|
||
- **Light 기본 테마** (사용자가 Dark 토글 가능)
|
||
- **카드형 시각 계층화**: 헤더 → SETTINGS → WORKFLOW → OPTIONS → 크레딧 푸터
|
||
- **1px 구분선**: `("#DEE2E6", "#3F3F3F")` 테마 쌍으로 자동 스위칭
|
||
- **uppercase 섹션 헤더**: 10pt bold muted gray
|
||
- **메인 액션 강조**: Step 4 버튼만 Saman 오렌지 `#E67E22`, 나머지는 blue 테마 파생
|
||
|
||
### 6.2 사이드바 구조
|
||
|
||
```
|
||
[ S-CANVAS Logo (logo_V2.png, 다크 bg 소프트 스트립) ]
|
||
Generative Design & Visualization Engine
|
||
─────────────────────────────────
|
||
SETTINGS
|
||
Satellite Source / Vworld Key (프리필 완료) / AI Engine /
|
||
GCP/API Key / Vertex Location / Project CRS
|
||
─────────────────────────────────
|
||
WORKFLOW
|
||
1. TIN 생성 (DXF) [filled blue]
|
||
🎯 TIN 이용 범위 (정밀 구역) [outlined]
|
||
1.5 DEM으로 TIN 확장 [outlined]
|
||
2. 위성지도 결합 [outlined]
|
||
3. 제어맵 추출 [outlined]
|
||
4. AI 렌더링 [filled ORANGE — primary CTA]
|
||
구조물 상세 3D 빌드 [filled green]
|
||
간단 치수 추가 (구) [muted]
|
||
🗔 3D 뷰 다시 열기 [filled dark]
|
||
─────────────────────────────────
|
||
OPTIONS
|
||
□ 와이어프레임 보기
|
||
뷰 버퍼 (%) [Step2/3]
|
||
□ 지형 확장 (DEM)
|
||
[Light ▼]
|
||
─────────────────────────────────
|
||
[ Saman CI (흰배경 알파 변환) ]
|
||
```
|
||
|
||
### 6.3 메인 영역
|
||
|
||
- **map_frame**: tkintermapview, corner_radius=12, border_color 테마쌍
|
||
- **textbox**: Consolas 12pt 로그
|
||
- **status_bar**: `● READY` 인디케이터 + 한 줄 상태
|
||
|
||
### 6.4 인트로 스플래시 (`splash.py`)
|
||
|
||
- **트리거**: `__main__` 에서 `SCanvasApp()` 생성 직전
|
||
- **재생**: cv2 VideoCapture 로 logo_intro.mp4 (24fps, 8s, 1280×720) 프레임 디코드 → PIL → Tk Label
|
||
- **효과**:
|
||
- 알파 0→1 페이드인 400ms
|
||
- MP4 자체 애니메이션 재생
|
||
- 알파 1→0 페이드아웃 400ms 후 destroy
|
||
- frameless overrideredirect, topmost, 화면 중앙 배치
|
||
- 비디오 아래 44px 오렌지 italic tagline bar
|
||
- **안정성**:
|
||
- max_duration_s=12 safety cap
|
||
- 비디오 끝 자동 감지 → 페이드아웃
|
||
- 임시 tk.Tk → 완전 destroy → SCanvasApp() 의 새 ctk.CTk 충돌 없음
|
||
- 파일 없음/cv2 실패 → silent skip
|
||
|
||
### 6.5 자산 처리 헬퍼
|
||
|
||
```python
|
||
_load_image_strip_white_bg(path, threshold=240) # 흰 배경 → 알파 0 (SAMAN_CI)
|
||
_load_image_strip_dark_bg(path, v_low=30, v_high=80) # 어두운 bg 소프트 전이 (logo_V2)
|
||
```
|
||
|
||
logo_V2 결과: 36.7% 완전투명 / 49.5% 부분알파 / 13.8% 불투명 → halo 없는 부드러운 엣지.
|
||
|
||
---
|
||
|
||
## 7. AI 렌더링 파이프라인 상세
|
||
|
||
### 7.1 프롬프트 구성 (`prompt_v1.yaml`)
|
||
|
||
```yaml
|
||
time_presets:
|
||
daytime: "bright daylight, clear blue sky, sharp shadows, vivid green vegetation"
|
||
sunset: "golden hour sunset, warm orange light, long dramatic shadows"
|
||
night: "nighttime aerial view, moonlight reflections, city lights in distance"
|
||
dawn: "early dawn, soft pink and purple sky, morning mist over valleys"
|
||
overcast: "overcast sky, diffused soft light, muted colors, atmospheric fog"
|
||
|
||
angle_presets:
|
||
top_down: "top-down overhead aerial view, directly above"
|
||
high_angle: "high-angle bird's-eye view, slightly tilted"
|
||
oblique: "oblique aerial perspective, 3/4 view showing terrain depth"
|
||
low_angle: "low-angle dramatic perspective, cinematic sweep"
|
||
|
||
structure_preservation:
|
||
- "enhance the existing satellite terrain texture and details"
|
||
- "maintain exact terrain shape, contours, and layout from the input image"
|
||
- "preserve water bodies, roads, and structural positions precisely"
|
||
- "do NOT add or remove any major landscape features"
|
||
|
||
quality_enhancement:
|
||
- "photorealistic architectural visualization"
|
||
- "professional drone photography quality"
|
||
- "8K ultra sharp detail, high dynamic range"
|
||
- "realistic vegetation depth and canopy textures"
|
||
|
||
negative_prompt: |
|
||
blurry, low quality, distorted, watermark, text, logo,
|
||
cartoon, anime, illustration, painting, sketch,
|
||
oversaturated, underexposed, noisy, artifacts,
|
||
changed terrain layout, moved structures, wrong topology
|
||
```
|
||
|
||
### 7.2 Stability AI 3단계 폴백
|
||
|
||
**1단계 — Conservative Upscale**
|
||
- mode: "conservative"
|
||
- creativity: UI 슬라이더 값 그대로 (기본 0.3)
|
||
- 목표: 원본 위성 텍스처 최대 보존
|
||
|
||
**2단계 — Creative Upscale**
|
||
- mode: "creative"
|
||
- creativity: min(strength, 0.35)
|
||
- 목표: 약간 더 사실적인 디테일 추가
|
||
|
||
**3단계 — img2img 초저강도**
|
||
- strength: min(strength, 0.2)
|
||
- 모델: sd3.5-large
|
||
- 목표: 위 2개 실패 시 백업
|
||
|
||
각 단계 실패 → 다음 단계 자동 폴백. 3개 모두 실패 시 `fail_job(error="모든 API 방법 실패")`.
|
||
|
||
### 7.3 Gemini 경로 (`gemini_renderer.py`)
|
||
|
||
- Vertex AI: google-genai SDK, GCP Project ID + gcp-key.json
|
||
- API Key: aistudio.google.com 발급 키
|
||
- 동일 인터페이스로 호출 → 인증만 다름
|
||
|
||
---
|
||
|
||
## 8. 좌표계 / 단위 / 정밀도
|
||
|
||
### 8.1 지원 CRS (사용자 선택)
|
||
|
||
| 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 | 글로벌 (위성 타일 호환) |
|
||
|
||
### 8.2 단위 강제 (메모리화된 결정)
|
||
|
||
> "extract_tin_shapes 는 unit_override='m' 고정 (자동감지가 KATEC 좌표를 mm 로 오판)"
|
||
|
||
DXF `$INSUNITS` 헤더가 부정확한 경우가 많아 **항상 m 로 강제**. KATEC 류
|
||
대형 좌표값(예: x=200000, y=550000) 을 mm 로 오판하면 단위가 1000배 어긋남.
|
||
|
||
### 8.3 Zero-Basing
|
||
|
||
큰 절대좌표(예: EPSG:5187 의 200,000m+)는 float32 정밀도에서 sub-meter 오차
|
||
발생 → **bbox 좌하단을 origin 으로 평행이동** → 모든 메시는 zero-based.
|
||
구조물 빌더는 절대좌표(`structure_v1.yaml` 의 origin) 와 zero-based 사이를
|
||
명시적으로 변환.
|
||
|
||
---
|
||
|
||
## 9. 메모리화된 핵심 결정 사항 (요약)
|
||
|
||
> 이 결정들은 과거 디버깅에서 얻은 교훈으로, 코드 곳곳의 분기 판단 기준이 됨.
|
||
|
||
1. **Structure Placement Workflow**: 위치인식 → 굴착 → TIN수정 → 3D배치 4단계
|
||
2. **Geo-Referencing**: 미리보기 → 4점 매칭 → 확정 3단계
|
||
3. **TIN Shape Unit**: extract_tin_shapes 는 항상 `unit_override="m"` (자동감지 금지)
|
||
4. **Excavation**: 폴리곤 평탄 pad + smoothstep 전이 + Delaunay 재계산.
|
||
구조물은 `embed_offset` 으로 TIN 관통 방지
|
||
5. **Mesh Orientation**: CW picks + Y-flip + 상대회전 + PCA frame_angle 로
|
||
앞뒤 뒤집힘 해결
|
||
6. **TIN/DEM 벽 근본 접근**: 벽 이슈는 `create_tin_from_dxf` 부터 점검. 판정은
|
||
`slope_ratio (z_span / max_edge)` 만 사용. **절대 Z 컷은 구멍 낸다**
|
||
7. **TIN 3-Zone**: core(원본 TIN) / transition(smoothstep) / DEM 확장.
|
||
`tin_core_bbox` · `blend_width_m` 로 제어
|
||
8. **CHANGELOG 의무**: 모든 수정은 `CHANGELOG.md` 에 즉시 기록 (역순, 날짜/파일/사유/diff 요지)
|
||
|
||
---
|
||
|
||
## 10. 최근 주요 수정 이력 (2026-04-24 ~)
|
||
|
||
### [render-fix] DEM 확장 경계 사각 선 제거 (후속)
|
||
- 두 개의 별도 PolyData → seam shading discontinuity 발생
|
||
- `merge(merge_points=True, tolerance=0.01)` + `compute_normals(feature_angle=180)`
|
||
로 1순위(구조 통합) + 2순위(법선 평활) 동시 달성
|
||
- 텍스처 모드에서 TCoords 소실 → `_uv_mapping_params` 저장 후 merge 후 재적용
|
||
|
||
### [rebrand] EG-VIEW → S-CANVAS 전면 리네이밍
|
||
- 클래스 `EGViewApp → SCanvasApp`
|
||
- 파일 `egview_maker.py → scanvas_maker.py`
|
||
- DB/로그 `egview_*.db/log → scanvas_*.db/log`
|
||
- 144개 문자열 occurrence (43 파일) 일괄 교체
|
||
|
||
### [ui-redesign] Light 기본 테마 + 사이드바 카드형
|
||
- `set_appearance_mode("light")`
|
||
- 모든 하드코딩 색상을 `(light, dark)` 튜플 쌍으로 → 자동 테마 스위칭
|
||
- SETTINGS / WORKFLOW / OPTIONS 섹션 헤더 + 1px 구분선
|
||
- SAMAN_CI 흰 배경 68.5% 알파 변환
|
||
|
||
### [feature] Vworld 키 프리필 + logo_V2 + 인트로 스플래시
|
||
- Vworld API 키 하드코딩 (사용자 제공)
|
||
- 로고 `Logo.png → logo_V2.png` (다크 bg 소프트 스트립)
|
||
- 신규 `splash.py` — cv2 기반 8초 MP4 스플래시 + 페이드 인/아웃
|
||
- `__main__` 에서 `show_intro_splash()` → `SCanvasApp()` 순차 실행
|
||
|
||
---
|
||
|
||
## 11. 차별화 포인트 (발표 슬라이드용 강조)
|
||
|
||
### 11.1 **사실 기반 vs 환각 기반**
|
||
경쟁 도구가 AI 의 상상으로 지형을 만들 때, S-CANVAS 는 **실측 DEM + 실제 위성영상**
|
||
을 베이스로 깔고 AI 는 **사실감 향상만** 담당. → 발주처에 실제와 다른 지형을
|
||
제출하는 사고 원천 차단.
|
||
|
||
### 11.2 **Seam-free 통합 메시**
|
||
TIN(설계 도면) + DEM(실제 지형) + 위성텍스처 + AI 렌더가 모두 **하나의 연속
|
||
표면**으로 합쳐짐. 사용자가 어디까지가 도면이고 어디부터가 실제 지형인지 구분
|
||
못 하도록.
|
||
|
||
### 11.3 **결정론적 재현성** (Harness)
|
||
DXF 해시 → seed → AI 렌더. 같은 입력에 항상 같은 출력. 발주처에 제출했던 그림과
|
||
1주일 뒤 회의에서 띄울 그림이 픽셀 단위로 동일.
|
||
|
||
### 11.4 **모듈러 구조물 빌더**
|
||
취수탑·제수변실·옹벽·수문 4종 즉시 지원. `structure_v1.yaml` 에 정의 추가하면
|
||
새 유형 확장 가능. DXF 단면도 → 자동 치수 파싱 → 3D 배치까지 클릭 1번.
|
||
|
||
### 11.5 **공개 데이터 + 무료 티어**
|
||
AWS Open Terrain Tiles · Google Satellite · ArcGIS World Imagery 모두 무료.
|
||
유일한 유료 항목은 AI 렌더(Gemini/Stability) — 그것도 사용자 본인 키 사용.
|
||
|
||
### 11.6 **자동 품질 게이트**
|
||
모든 렌더 결과를 OpenCV 로 즉시 검증. 흐릿하거나 단색 출력은 자동 PASS/FAIL
|
||
표시 → 사용자가 100장을 일일이 확인할 필요 없음.
|
||
|
||
### 11.7 **Saman Corp 자체 개발**
|
||
국내 토목·건설 도메인 지식이 코드 곳곳에 — KATEC 단위 처리, 한국 EPSG 코드
|
||
프리셋, Vworld 타일 지원, 한글 UI·로그 등.
|
||
|
||
---
|
||
|
||
## 12. 기술 스택 요약 (한눈에)
|
||
|
||
```
|
||
[Frontend / GUI]
|
||
└─ CustomTkinter (Tk 기반 모던 위젯) + tkintermapview
|
||
|
||
[3D Engine]
|
||
└─ PyVista + VTK (메시 처리·렌더링)
|
||
|
||
[Geospatial]
|
||
├─ ezdxf (DXF 파싱)
|
||
├─ pyproj (좌표 변환)
|
||
├─ scipy.spatial.Delaunay (TIN)
|
||
└─ AWS Open Terrain Tiles (DEM)
|
||
|
||
[Image / Video]
|
||
├─ Pillow (PIL)
|
||
├─ OpenCV (cv2)
|
||
└─ tkintermapview (위성 타일)
|
||
|
||
[AI Rendering]
|
||
├─ Google google-genai (Vertex AI / API Key)
|
||
└─ Stability AI REST API
|
||
|
||
[Persistence]
|
||
├─ SQLAlchemy + SQLite (JobRecord)
|
||
├─ structlog (구조화 로깅)
|
||
└─ PyYAML (프롬프트/구조물 정의)
|
||
|
||
[Build / Distribution] (계획)
|
||
└─ PyInstaller (단일 .exe 또는 onedir 배포)
|
||
```
|
||
|
||
---
|
||
|
||
## 13. 배포 / 패키징 계획
|
||
|
||
**PyInstaller** 로 Windows .exe 빌드 예정. 주요 챌린지:
|
||
- PyVista + VTK 200MB+ 바이너리 → `--collect-all pyvista vtkmodules` 필요
|
||
- pyproj PROJ data → 명시적 hook
|
||
- google-genai → hidden imports
|
||
- 런타임 쓰기 경로(DB·로그·캐시) → `%LOCALAPPDATA%\S-CANVAS\` 분리 (onedir/onefile 양쪽)
|
||
- 자산 경로 (`Design/`, `prompt_templates/`, `structure_types/`) → `sys._MEIPASS` 핸들링
|
||
|
||
배포 형태 (예정): `dist/scanvas_maker/` 폴더 통째로 zip → 약 150-200MB 압축. 사용자는
|
||
.exe 더블클릭으로 실행, 본인 GCP 키 또는 Stability API 키만 환경변수/UI 입력.
|
||
|
||
---
|
||
|
||
## 14. 향후 로드맵
|
||
|
||
1. **다국어 (i18n)**: 한국어/영어 토글
|
||
2. **추가 구조물 유형**: 배수문·교량·터널·송수관 등
|
||
3. **Linux/macOS 지원**: 현재 Windows 전용
|
||
4. **클라우드 렌더 큐**: 다수 DXF 일괄 처리 후 결과 한꺼번에 ZIP
|
||
5. **VR/AR 출력**: glTF 익스포트 → Unity/Unreal 연동
|
||
6. **변경 추적**: 같은 DXF 의 V1/V2 자동 비교 → 변경 영역 하이라이트
|
||
|
||
---
|
||
|
||
## 15. 핵심 메시지 (발표 마무리용)
|
||
|
||
> **S-CANVAS 는 토목·건설 도면을, "수일짜리 모델링 작업" 에서 "클릭 4번의 자동
|
||
> 파이프라인" 으로 압축한다. 사실 기반(DEM·위성) 위에서 AI 가 사실감만 더하므로
|
||
> 환각 위험 없이, 결정론적 재현성을 보장하면서, 발주처·주민설명회 발표용 조감도를
|
||
> 분 단위로 양산할 수 있다. — Saman Corp.**
|
||
|
||
---
|
||
|
||
## 부록 A. 진입점 / 실행 방법
|
||
|
||
```bash
|
||
python scanvas_maker.py
|
||
```
|
||
|
||
진행 순서:
|
||
1. `splash.py` 가 logo_intro.mp4 8초 재생 (페이드 인/아웃)
|
||
2. `SCanvasApp(ctk.CTk)` 메인 창 기동
|
||
3. (사이드바) DXF 파일 선택 → CRS 확인 → Step 1~4 순차 실행
|
||
4. (옵션) 구조물 상세 3D 빌드 / 와이어프레임 / DEM 확장
|
||
5. AI 렌더 결과는 `rendered_birdseye.png` (와 SQLite 이력)
|
||
|
||
## 부록 B. 디렉토리 구조
|
||
|
||
```
|
||
D:\2026\00_EGVIEW2\
|
||
├── scanvas_maker.py # 메인 진입점
|
||
├── splash.py # 인트로 스플래시
|
||
├── dem_extender.py
|
||
├── geo_referencing.py
|
||
├── 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
|
||
│
|
||
├── harness/
|
||
│ ├── __init__.py
|
||
│ ├── seed_manager.py
|
||
│ ├── quality_validator.py
|
||
│ ├── prompt_registry.py
|
||
│ └── logger.py
|
||
│
|
||
├── prompt_templates/
|
||
│ └── prompt_v1.yaml
|
||
│
|
||
├── structure_types/
|
||
│ └── structure_v1.yaml
|
||
│
|
||
├── Design/
|
||
│ ├── Logo.png
|
||
│ ├── logo_V2.png (현재 사용)
|
||
│ ├── SAMAN_CI.gif
|
||
│ ├── logo_intro.mp4 (8s, 24fps, 1280×720)
|
||
│ ├── homepage_sample.png
|
||
│ └── page_sample.png
|
||
│
|
||
├── cache/dem/ (런타임 DEM 캐시)
|
||
├── scanvas_jobs.db (SQLite 이력)
|
||
├── scanvas_harness.log (structlog)
|
||
├── scanvas_diagnostic.log (Step 1 진단)
|
||
├── CHANGELOG.md (수정 이력 역순)
|
||
└── Build_log.txt (사용자 원본 요청 로그)
|
||
```
|
||
|
||
## 부록 C. 핵심 메트릭 (발표 슬라이드 숫자용)
|
||
|
||
- 메인 모듈: 6300+ LOC (`scanvas_maker.py`)
|
||
- 지원 구조물 유형: 4종 (취수탑·제수변실·옹벽·수문) + 확장 가능
|
||
- 지원 좌표계: 5종 EPSG (한국 4 + Web Mercator 1)
|
||
- 지원 위성 타일 서버: 9종 (Google·ArcGIS·Bing·OSM·OpenTopo·Vworld 3종 등)
|
||
- 지원 AI 렌더 엔진: 3종 (Gemini Vertex·Gemini API·Stability)
|
||
- DEM 자동 페치: AWS Terrarium 글로벌, ~30m 정확도
|
||
- 인트로 스플래시: 8.0초 (24fps × 192 frames @ 1280×720)
|
||
- 메시 통합 weld 톨러런스: 0.01m (1cm)
|
||
- TIN 3-Zone 블렌드: smoothstep `3t² − 2t³`
|
||
- 벽 컷 임계: slope_ratio > 4.0 (≈76°) AND z_span > 30m AND e_max > 5m
|
||
|
||
---
|
||
|
||
*본 문서는 NotebookLM 의 단일 소스로 사용 가능하도록 자기완결형으로 작성되었습니다.
|
||
업로드 후 "Audio Overview", "마인드맵", "발표 슬라이드 초안" 자동 생성을 권장합니다.*
|