콘텐츠로 이동

이미지 뷰어

GeoOmni 이미지 뷰어는 항공사진측량을 위한 Vulkan GPU 가속 고성능 뷰어입니다. 대용량 원본 영상(16k+ 픽셀)을 실시간으로 렌더링하며, 서브픽셀 관측 정밀도를 보장합니다.


상태바 엔진 모드 표시

뷰어 하단 상태바에는 현재 운영 중인 세 가지 엔진의 모드가 실시간으로 표시됩니다:

IMG:Tiled(.gopy) VEC:MDI+GPU STEREO:Multiview(6)

이 정보는 현재 하드웨어가 지원하는 최상위 기능을 자동으로 선택하여 표시합니다. 관측 정밀도와 성능은 각 모드에 따라 달라지므로, 작업 전에 반드시 확인하시기 바랍니다.


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+=피라미드현재 동작 가능 (자동 활성).

사용 조건 (모두 만족 시 자동 진입)

  1. .gopy 사이드카가 raw 압축(compression == None) — JPEG .gopy는 현재 미지원
  2. 원본 이미지와 .gopy의 origW/H가 일치 (sourceModifiedMs 검증)
  3. GPU가 sparseResidencyImage2D 지원 (현재 AMD Radeon 8060S, NVIDIA RTX 등 데스크탑 대부분)
  4. 위 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 폴백 체인

  1. Sparse + 원본 + .gopy 모두 사용 가능 + raw → Sparse(L0)+gopy/raw(L1+) (전 mip 무손실)
  2. Sparse + 원본 + .gopy(JPEG) → Sparse(L0)+gopy/jpeg*(L1+) (mip 0 무손실, mip 1+ 손실)
  3. Sparse 미지원 또는 원본 부재 → Tiled(.gopy/raw) 또는 Tiled(.gopy/jpeg*)
  4. .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 경로는 코드에 골격만 존재하며 현재 빌드에서는 비활성화되어 자동 폴백 체인에 등장하지 않습니다. 대규모 정사영상 검증 단계에서 옵트인으로 재개됩니다.

벡터 엔진

multiDrawIndirect + drawIndirectCount?
    → MDI+GPU
multiDrawIndirect만?
    → MDI
그 외
    → Basic

스테레오 엔진

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) 사용

정밀 관측 체크리스트

  1. 상태바 IMG 필드가 다음 중 하나 (모두 무손실):
  2. Sparse(L0)+gopy/raw(L1+) — Hybrid 모드, 거대 이미지 권장
  3. Tiled(.gopy/raw) — 무손실 .gopy 단독
  4. KTX2(Lossless) — 무손실 KTX2 컨테이너
  5. RGBA — WIC 직접 디코딩
  6. Tiled(.gopy/jpeg*)는 별표가 손실을 의미 — 정밀 관측 부적합
  7. 줌 레벨이 100% 이상
  8. 관측 모드 활성화 (툴바 [관측] 버튼)
  9. 십자선 커서 중심이 관측 대상 픽셀에 위치

관련 문서

  • 전역 설정: 뷰어 섹션에서 최대 줌, 줌 프리셋, 미니맵/툴바 위치 설정
  • MCP 명령어: 이미지 뷰어 자동 제어 API
  • 시스템 요구사항: GPU 지원 기능 확인