이미지 뷰어
GeoOmni 이미지 뷰어는 항공사진측량을 위한 Vulkan GPU 가속 고성능 뷰어입니다. 대용량 원본 영상(16k+ 픽셀)을 실시간으로 렌더링하며, 서브픽셀 관측 정밀도를 보장합니다.
상태바 엔진 모드 표시
뷰어 하단 상태바에는 현재 운영 중인 세 가지 엔진의 모드가 실시간으로 표시됩니다:
이 정보는 현재 하드웨어가 지원하는 최상위 기능을 자동으로 선택하여 표시합니다. 관측 정밀도와 성능은 각 모드에 따라 달라지므로, 작업 전에 반드시 확인하시기 바랍니다.
IMG — 이미지 엔진 모드
이미지 데이터를 GPU에 로드하고 렌더링하는 방식입니다.
Tiled(.gopy)
피라미드 타일 LOD 모드 — 대용량 항공영상 전용.
.gopy 파일은 압축 옵션에 따라 두 가지 변종으로 운영됩니다. 상태바에는 어느 변종이 활성인지 명시됩니다.
Tiled(.gopy/raw) — 무손실 변종 (정밀 관측 권장)
| 항목 | 내용 |
|---|---|
| 사용 조건 | .gopy 파일의 compression = None(Raw RGBA)일 때 자동 활성화 |
| 처리 방식 | 압축 없이 원본 RGBA 픽셀을 타일 단위로 GPU에 업로드 |
| LOD 선택 | 현재 줌 레벨에 따라 최적 피라미드 레벨 자동 선택 (Level 0 = 원본, L+1 = 1/2 축소) |
| 메모리 점유 | 타일 단위 LRU 캐시 (기본 128 타일, 약 32MB VRAM) |
| 정밀도 | 100% 무손실 — 원본 픽셀 값 그대로 보존, 서브픽셀 관측 가능 |
| 장점 | 16k+ 거대 이미지도 빠른 표시, 정밀 관측 보장, WIC JPEG 디코딩 우회 |
| 단점 | 디스크 사용량 큼 (raw 대비 ~4배) |
Tiled(.gopy/jpeg*) — 손실 변종 (미리보기 전용)
| 항목 | 내용 |
|---|---|
| 사용 조건 | .gopy 파일의 compression = JPEG(quality=85 기본)일 때 자동 활성화 |
| 처리 방식 | JPEG 타일을 WIC로 디코딩하여 RGBA로 GPU에 업로드 |
| 정밀도 | ⚠️ 손실 압축 — 픽셀당 ±1~3 단위 오차, 블록 경계 아티팩트 가능 |
| 장점 | 디스크 사용량 작음 (raw 대비 ~1/4), 일반 패닝/미리보기에 충분 |
| 단점 | GCP 정밀 관측, 자동 매칭에는 부적합. 상태바 라벨 끝의 별표(*)가 손실을 의미 |
정밀 관측 시 무손실 경로 강제
Tiled(.gopy/jpeg*)는 일반 패닝/미리보기 용도로만 사용하세요.
GCP 정밀 관측, 타이포인트 자동 매칭이 필요한 작업에서는:
.gopy빌드 시compression=raw옵션을 사용하거나- 대용량 이미지의 경우
Sparse(L0)+gopy/raw(L1+)Hybrid 모드를 활성화하세요.
파일 구성 예시:
D:/Data/aerial_01.tif ← 원본 TIFF (1.1 GB)
D:/Data/aerial_01.tif.gopy ← .gopy raw (~1.5 GB, 무손실) → Tiled(.gopy/raw)
또는
D:/Data/aerial_01.tif.gopy ← .gopy jpeg (~100 MB, 손실) → Tiled(.gopy/jpeg*)
상태바 추가 정보: LOD L2 | 24 타일 (현재 레벨 2, 가시 24 타일)
Sparse(L0)+gopy(L1+) — Hybrid Sparse 모드
거대 항공영상 전용 — L0=원본 sparse, L1+=피라미드 — 현재 동작 가능 (자동 활성).
사용 조건 (모두 만족 시 자동 진입)
.gopy사이드카가 raw 압축(compression == None) — JPEG.gopy는 현재 미지원- 원본 이미지와
.gopy의 origW/H가 일치 (sourceModifiedMs 검증) - GPU가
sparseResidencyImage2D지원 (현재 AMD Radeon 8060S, NVIDIA RTX 등 데스크탑 대부분) - 위 3개 모두 만족 시
VulkanWidget::loadImage()의 폴백 체인이 자동으로LoadSparseHybrid경로 진입
| 항목 | 내용 |
|---|---|
| L0 (mip 0) | 원본 이미지에서 가시 영역만 Sparse Virtual Texture 페이지로 상주 (WIC ROI 디코딩) |
| L1+ (mip 1, 2, …) | .gopy 피라미드의 해당 레벨을 mip 1+로 채움 (raw RGBA → staging 업로드) |
| mip tail | 작은 mip들은 imageMipTailFirstLod 이상으로 묶여 단일 메모리 블록으로 한 번에 사전 채움 |
| 포맷 통일 | 모든 mip이 R8G8B8A8_UNORM (Sparse 표준 제약: mip chain의 단일 포맷) |
| 정밀도 | L0 = 100% 무손실 (원본 직접). L1+ = raw .gopy → 무손실 |
| 활성 mip | floor(log2(100 / actualZoomPercent)). 줌 ≥ 100% → mip 0, 50% → mip 1, 25% → mip 2 |
| 상태바 예시 | IMG:Sparse(L0:48p)+gopy/raw(L1+) — 현재 48개 sparse 페이지 상주, L1+는 raw .gopy |
| 장점 | 수십 GB 거대 이미지도 가시 영역만 메모리 상주 + 정밀 관측 보장 + 빠른 개관 |
동작 흐름
사용자: aerial_01.tif 더블클릭
├─ aerial_01.tif.gopy(compression=raw) 존재 + sparseResidencyImage2D 지원?
│ → LoadSparseHybrid 시도
│ 1. Sparse VkImage(R8G8B8A8) + mip chain 생성
│ 2. mip tail (작은 mip들) 사전 채우기
│ 3. WIC decoder/converter 캐싱 (mip 0 ROI 디코딩 재사용)
│ 4. descriptor set을 sparse imageView로 묶음
│
▼ 매 프레임 (Render)
├─ activeMip = ComputeActiveMip()
├─ EnsureSparsePages(activeMip) — 가시 페이지를 BindTileMip 푸시
│ └─ 이미 상주한 페이지는 IsTileResident 가드로 디코딩 우회
└─ 일반 quad draw
.gopy 압축에 따른 동작
| 압축 종류 | hybrid 지원 | mip 0 정밀도 | mip 1+ 정밀도 | 상태바 |
|---|---|---|---|---|
| None (raw) | ✅ | 100% 무손실 (원본 ROI) | 100% 무손실 (raw 직접 복사) | Sparse(L0:Np)+gopy/raw(L1+) |
| JPEG | ✅ | 100% 무손실 (원본 ROI) | ⚠️ 손실 (WIC 디코드 → mipCache) | Sparse(L0:Np)+gopy/jpeg*(L1+) |
JPEG hybrid 모드에서도 mip 0(L0)는 원본 이미지에서 직접 ROI 디코딩되므로 100% 무손실. 줌 ≥ 100% 정밀 관측 시 자동으로 mip 0이 활성화되어 GCP 서브픽셀 정밀도가 보장된다. mip 1+ 손실은 일반 패닝/개관 시점에만 영향 (별표
*로 손실 표시).
진행 중인 개선 (Stage C)
| 항목 | 상태 | 비고 |
|---|---|---|
| BatchBindAndUpload (sparse bind/submit batch) | ✅ 완료 | 100 페이지 → 1 batch (stall 100배 절감) |
| MAX_BATCH=16 throttle (한 프레임당) | ✅ 완료 | 첫 로드 시 7프레임에 분산 |
| mip 1+ 호스트 raw 캐시 LRU 3슬롯 | ✅ 완료 | 줌 변경 시 재디코드 회피 |
JPEG .gopy hybrid 지원 |
✅ 완료 | DecodeJpegBytesToRGBA + mipCache 통합 |
| Fence 기반 비동기 batch | ✅ 완료 | vkQueueSubmit(fence) 후 즉시 return → 매 프레임 stall 0 |
| pendingTiles set (corrupt sample 회피) | ✅ 완료 | IsTileResident가 resident + pending 둘 다 검사 |
| Device features 활성화 (sparse, MDI) | ✅ 완료 | vkGetPhysicalDeviceFeatures 쿼리 후 지원 기능만 enable. 이전 빌드는 features 미활성 — sparse VkImage 생성이 사실상 거부될 수 있었음 |
| 앱 시작/종료 스모크 테스트 | ✅ 통과 | schtasks 9초 이상 안정 동작, 크래시 0건 |
| sparse 큐 family 분리 | ⏳ 미수행 | AMD는 graphics 큐가 sparse 지원, NVIDIA는 분리 가능 |
| Hybrid 시각 검증 | ⏳ .gopy 파일 부재 | sandbox 환경에 raw .gopy 빌드 후 사용자 시각 검증 필요 |
Sparse Hybrid 폴백 체인
- Sparse + 원본 +
.gopy모두 사용 가능 + raw →Sparse(L0)+gopy/raw(L1+)(전 mip 무손실) - Sparse + 원본 +
.gopy(JPEG) →Sparse(L0)+gopy/jpeg*(L1+)(mip 0 무손실, mip 1+ 손실) - Sparse 미지원 또는 원본 부재 →
Tiled(.gopy/raw)또는Tiled(.gopy/jpeg*) .gopy부재 →KTX2(Lossless)또는RGBA
Sparse-ready
Vulkan Sparse Virtual Texture 준비됨 — GPU가 하드웨어 가상 메모리 지원.
| 항목 | 내용 |
|---|---|
| 사용 조건 | GPU가 sparseResidencyImage2D 기능 지원 |
| 가상 주소 공간 | 최대 128 TB (하드웨어별 차이) |
| 처리 방식 | 이미지를 거대한 가상 텍스처로 매핑, 필요한 페이지만 On-Demand로 VRAM에 상주 |
| 메모리 점유 | 실제 표시 영역 × 약간의 오버헤드 |
| 정밀도 | 100% 무손실 — 페이지 배치만 가상화, 픽셀 값은 원본 그대로 |
| 장점 | 수백 GB 이미지도 단일 텍스처 주소 공간으로 처리 가능 |
| 단점 | .gopy 변환 없이 원본 TIF 직접 사용은 후속 기능 |
타일 데이터 소스
Sparse 경로의 타일 데이터는 무손실 KTX2 컨테이너 또는 원본 TIFF의 WIC 디코딩 결과에서 공급됩니다. BC7 등 손실 코덱은 사용하지 않으며, 픽셀 값은 원본 그대로 보존됩니다.
Sparse-ready는 "준비됨" 상태로, 현재 렌더링 경로는 Tiled 또는 기본 경로를 사용하지만
향후 Sparse Virtual Texture가 활성화될 수 있는 하드웨어임을 의미합니다.
KTX2(Lossless)
Khronos Texture 2.0 컨테이너 — 무손실 경로 (현재 실사용 기본).
KTX2는 컨테이너 포맷입니다
KTX2는 압축 알고리즘 자체가 아니라 텍스처 컨테이너입니다.
하나의 .ktx2 파일은 여러 종류의 픽셀 포맷을 담을 수 있습니다:
- 무손실:
R8G8B8A8_UNORM,R8G8B8A8_SRGB,R16G16B16A16_UNORM— 원본 TIFF와 동등한 정밀도 - 손실 (향후): BC7, BC6H 등 GPU 블록 압축 코덱
- 수퍼컴프레션 (향후): Zstd, BasisLZ 등
GeoOmni는 무손실 포맷만 활성화하여 KTX2를 일종의 "GPU 친화적 무손실 사이드카 파일"로 사용합니다.
| 항목 | 내용 |
|---|---|
| 사용 조건 | 이미지 옆에 동반 *.ktx2 파일이 존재할 때 자동 활성화 |
| 포맷 화이트리스트 | R8G8B8A8_UNORM, R8G8B8A8_SRGB, R16G16B16A16_UNORM |
| 처리 방식 | 컨테이너에서 mip 0 + 사전 계산된 미니맵 체인을 그대로 GPU로 업로드 |
| WIC 디코딩 | 불필요 — KTX2 파서가 직접 읽음, 로딩이 RGBA 경로보다 빠름 |
| 정밀도 | 100% 무손실 — 원본 픽셀 값 보존, 서브픽셀 관측 가능 |
| 장점 | WIC 디코딩 우회, 미리 계산된 mip, 헤더가 단순, 로딩 빠름 |
| 단점 | TIFF → KTX2(무손실) 사전 변환 필요 |
파일 구성 예시:
D:/Data/aerial_01.tif ← 원본 (1.1 GB, RGBA)
D:/Data/aerial_01.tif.ktx2 ← 무손실 KTX2 컨테이너 (~1.1 GB + mip)
→ 뷰어에서 더블클릭 시 KTX2를 우선 사용
BC7 + 손실 압축 (향후 기능 / Future)
현재 비활성화 — 추후 단계로 연기됨
BC7 등 손실 블록 압축 경로는 대규모 정사영상(수백 GB) 미리보기 검증 단계에서 재개됩니다. 현재 빌드는 KTX2 컨테이너 안의 BC7 포맷을 거부합니다.
| 항목 | 내용 (예정) |
|---|---|
| 사용 조건 | GPU가 textureCompressionBC 기능 지원 + 사용자 옵트인 |
| 압축률 | 원본 RGBA 대비 약 4배 (32bpp → 8bpp) |
| 정밀도 | ⚠️ 손실 — 픽셀당 ±1~3 단위 오차, 블록 경계 아티팩트 가능 |
| 자동 폴백 정책 | 100% 이상 확대 / 관측 모드 진입 시 무손실 경로 강제 |
| 상태바 표시 | 활성화 시 KTX2(BC7)로 별도 표시 (현재는 미노출) |
| 사용 케이스 | 대량 정사영상 일괄 미리보기, 썸네일 캐시, 멀티 모자이크 회람용 |
GCP 정밀 관측 / 자동 매칭이 주 작업인 단계에서는 BC7 경로를 사용하지 않습니다.
RGBA
기본 모드 — 압축 없이 원본 RGBA로 GPU에 업로드.
| 항목 | 내용 |
|---|---|
| 사용 조건 | BC 압축 미지원 또는 기본 경로 사용 시 |
| 처리 방식 | WIC로 이미지 디코딩 → RGBA 32bpp → 단일 VkImage 업로드 |
| GPU 크기 제한 | maxImageDimension2D 초과 시 자동 축소 (비율 유지) |
| 정밀도 | 100% 무손실 |
| 장점 | 모든 GPU 지원, 가장 단순한 경로 |
| 단점 | VRAM 사용량 최대, 거대 이미지는 축소됨 |
메모리 예시: - 14118 × 20150 RGBA = 약 1.1 GB → GPU 제한(16384px)에 맞춰 11465 × 16384로 축소
VEC — 벡터 엔진 모드
GCP, 타이포인트, 도화(벡터 피처) 등의 오버레이를 그리는 방식입니다.
MDI+GPU
Multi-Draw Indirect + GPU 기반 컬링 — 최상위 성능.
| 항목 | 내용 |
|---|---|
| 사용 조건 | multiDrawIndirect + VK_KHR_draw_indirect_count 지원 |
| 드로우 방식 | GPU 컴퓨트 셰이더가 드로우 명령을 직접 작성, CPU는 단 1회 호출 |
| 컬링 | 프러스텀 컬링, 가시성 필터, 레이어 필터 모두 GPU에서 처리 |
| 처리 피처 수 | 수만 ~ 수십만 개 단일 드로우콜로 처리 |
| 장점 | CPU 오버헤드 최소, 대량 벡터 데이터 실시간 편집 가능 |
| 하드웨어 | 데스크탑 GPU 대부분 지원 (Vulkan 1.2+) |
사용 예시: GCP 50,000개 + 타이포인트 200,000개 동시 렌더링 시에도 60 FPS 유지.
MDI
Multi-Draw Indirect만 지원 — drawIndirectCount 미지원 환경.
| 항목 | 내용 |
|---|---|
| 사용 조건 | multiDrawIndirect만 지원, drawIndirectCount 미지원 |
| 드로우 방식 | CPU가 가시성 판단 후 MDI 버퍼에 드로우 명령 기록 |
| 컬링 | CPU 사이드 프러스텀 컬링 |
| 처리 피처 수 | 수천 ~ 수만 개 |
| 장점 | GPU 의존도 낮음, 호환성 높음 |
| 단점 | 편집이 빈번한 경우 CPU 오버헤드 |
Basic
기본 드로우콜 경로 — 피처당 개별 draw.
| 항목 | 내용 |
|---|---|
| 사용 조건 | multiDrawIndirect 미지원 |
| 드로우 방식 | 각 피처마다 vkCmdDraw 개별 호출 |
| 처리 피처 수 | 수백 ~ 수천 개 |
| 장점 | 가장 단순, 모든 GPU 호환 |
| 단점 | CPU 병목, 피처 수에 비례하여 프레임 시간 증가 |
현재 GeoOmni는 Vulkan 1.0+ 데스크탑 GPU에서 실행되므로 대부분 MDI 이상이 선택됩니다.
STEREO — 스테레오 렌더링 모드
좌/우 이미지 쌍을 동시에 렌더링하여 입체 관측을 지원합니다.
QuadBuffer
NVIDIA Quad Buffer Stereo — 전문 사진측량 표준.
| 항목 | 내용 |
|---|---|
| 필수 장비 | NVIDIA Quadro P-series / RTX A-series (Quadro RTX 4000 이상 권장) |
| 모니터 | 편광 3D 모니터 또는 셔터 글래스 (120 Hz 이상) |
| 드라이버 | NVIDIA 3D Vision Pro 설정 활성화 |
| 출력 | 좌/우안 이미지를 하드웨어 수준에서 동기화 |
| 대상 | 항공사진측량 실무, DAT/EM Summit 대체 |
Quad Buffer 활성화 조건
- NVIDIA 벤더 GPU + Quadro/RTX Pro 라인업
- NVIDIA 드라이버의 "Stereo Enabled" 설정
- Vulkan 인스턴스 생성 시 자동 감지 → 미지원 장비에서는
Multiview로 자동 폴백
Multiview(N)
VK_KHR_multiview 기반 멀티뷰 렌더링 — 표준 GPU에서 스테레오.
| 항목 | 내용 |
|---|---|
| 사용 조건 | Vulkan 1.1+ 또는 VK_KHR_multiview + maxMultiviewViewCount >= 2 |
| 렌더 방식 | 단일 렌더 패스에서 좌/우 뷰 동시 렌더링 (gl_ViewIndex) |
| 성능 이점 | 일반 듀얼 뷰 대비 드로우콜 50% 절감, CPU 오버헤드 2배 감소 |
| 괄호 안 숫자 (N) | GPU가 지원하는 최대 뷰 수 (일반적으로 2~6) |
| 출력 | Side-by-Side 화면 분할 또는 애너글리프 합성 |
| 대상 | 일반 데스크탑 GPU로 스테레오 관측 |
현재 AMD Radeon 8060S: Multiview(6) — 최대 6뷰 동시 지원.
SideBySide
Side-by-Side 듀얼 뷰포트 폴백 — 모든 GPU 지원.
| 항목 | 내용 |
|---|---|
| 사용 조건 | Multiview 미지원 또는 기본 폴백 |
| 렌더 방식 | 두 개의 독립 VulkanWidget, 좌/우 이미지 별도 렌더링 |
| 동기화 | 줌/패닝 동기화 (CPU 레벨) |
| 장점 | 모든 GPU에서 작동, 구현 단순 |
| 단점 | 드로우콜 2배, CPU 오버헤드 약간 증가 |
폴백 체인 — 자동 모드 선택
앱 시작 시 다음 순서로 하드웨어 기능을 확인하여 최상위 모드로 자동 설정합니다:
이미지 엔진
이미지 더블클릭
├─ Sparse 지원 + 원본 + .gopy 모두 존재?
│ → Sparse(L0)+gopy/raw(L1+) [Hybrid — 현재 동작 가능]
│
├─ .gopy 파일 + compression=raw?
│ → Tiled(.gopy/raw) (피라미드 타일 LOD, 무손실)
│
├─ .gopy 파일 + compression=jpeg?
│ → Tiled(.gopy/jpeg*) (피라미드 타일 LOD, 손실 — 정밀 관측 부적합)
│
├─ .ktx2 동반 파일 존재?
│ → KTX2(Lossless) (무손실 컨테이너, mip 사전 계산)
│
├─ Sparse 지원?
│ → Sparse-ready (가상 텍스처 준비, 단일 mip)
│
└─ 그 외
→ RGBA (WIC 디코딩, 무손실)
무손실 우선 원칙:
Sparse(L0)+gopy/raw,Tiled(.gopy/raw),KTX2(Lossless),RGBA는 모두 100% 무손실입니다.Tiled(.gopy/jpeg*)는 손실이며 미리보기 전용입니다 (별표*표시).참고: BC7 + 손실 압축 KTX2 경로는 코드에 골격만 존재하며 현재 빌드에서는 비활성화되어 자동 폴백 체인에 등장하지 않습니다. 대규모 정사영상 검증 단계에서 옵트인으로 재개됩니다.
벡터 엔진
스테레오 엔진
NVIDIA Quadro/RTX Pro + 드라이버 스테레오 활성화?
→ QuadBuffer
VK_KHR_multiview + maxMultiviewViewCount >= 2?
→ Multiview(N)
그 외
→ SideBySide
정밀도 원칙
항공사진측량 작업에서 서브픽셀 정밀도는 절대 원칙입니다.
| 원칙 | 설명 |
|---|---|
| 100% = 1:1 픽셀 | 줌 100%에서 이미지 1픽셀이 모니터 1픽셀에 정확히 대응 |
| 서브픽셀 관측 | 800~1600% 확대 + 관측 모드로 0.1px 이하 정밀도 달성 |
| 무손실 우선 | 현재 활성 경로(Tiled, KTX2(Lossless), Sparse-ready, RGBA)는 모두 100% 무손실 |
| BC7 자동 폴백 (향후) | 손실 BC7 경로 도입 시 100% 이상 확대 또는 관측 모드 진입 시 무손실로 자동 전환 |
| Mip 0 강제 | GCP 관측, 타이포인트 매칭은 항상 원본 해상도(Mip 0) 사용 |
정밀 관측 체크리스트
- 상태바 IMG 필드가 다음 중 하나 (모두 무손실):
Sparse(L0)+gopy/raw(L1+)— Hybrid 모드, 거대 이미지 권장Tiled(.gopy/raw)— 무손실 .gopy 단독KTX2(Lossless)— 무손실 KTX2 컨테이너RGBA— WIC 직접 디코딩Tiled(.gopy/jpeg*)는 별표가 손실을 의미 — 정밀 관측 부적합- 줌 레벨이 100% 이상
- 관측 모드 활성화 (툴바
[관측]버튼) - 십자선 커서 중심이 관측 대상 픽셀에 위치
관련 문서
- 전역 설정: 뷰어 섹션에서 최대 줌, 줌 프리셋, 미니맵/툴바 위치 설정
- MCP 명령어: 이미지 뷰어 자동 제어 API
- 시스템 요구사항: GPU 지원 기능 확인