유한 깊이와 방향 관측이 섞인 두 시점 복원
기준은 main의 84d0a98이다. 기존 TypeCV E 분해·삼각측량·LM과 TypeShade
d36ac5027356b96e87a9ad900dfd8e6ea4ab55dc의 수학/컴퓨트를 재사용한다.
추가 라이브러리나 의미 기반 하늘 검출을 도입하지 않는다.
separateDirectionalObservations: true로 사용하는 실험 기능이다. 기본값은 false로
유지한다. 혼합 장면에서는 복원율이 높아지지만 큰 잡음의 오확정과 기존 정련 사례의
기하 오차 회귀가 남아, 기본 활성화 기준을 충족하지 못했다. CPU/scene/GPU mapper와
reconstructTwoView / reconstructTwoViewGpu에서 같은 옵션을 받는다.
문제와 변경 범위
기존 recoverTwoViewGeometry / chooseTwoViewPose는 모든 E 인라이어의 60%에
양의 유한 깊이를 요구했다. 유한점 60개와 무한원 방향 60개가 있는 정확한 대응도
Cheirality failed로 거절했다. 그 결과 지도 초기화와 관측 트랙 연결이 막혔다.
이번 단계는 보정된 두 시점에서 유한 깊이를 검사할 수 있는 대응과 방향만으로 설명되는 대응을 구분한다. 카메라와 희소 구조를 추정하는 단계이며 MVS, 3DGS, 동적 객체 복원이나 원본 게임 메시 복구가 아니다.
- E의 네 자세 후보마다 두 보정 광선의 양방향 회전 전이 오차를 계산한다. 각각
maxReprojectionErrorPx이내이고 광선이 양쪽 카메라 앞을 향할 때 방향 호환으로 보류한다. 기존 유한점 검사와 같은 픽셀 오차 예산을 사용하며 기본은 4px다. mapper에서는 기존maxErrorPx기본 3px를 전달한다. 병진은 이 판정에 들어가지 않는다. - 보류 관측은 유한 깊이의 부호 투표와 60% 분모에서 제외한다. 나머지에는 기존 양의 깊이 비율과 최소 6개를 요구한다. 방향만 있는 입력은 지도를 초기화할 수 없다. mapper의 최소 초기 점 수, H 지배성, 초기 시차 검사도 유지한다.
- 기존 두 시점 LM의 잔차·Schur/수치 미분 풀이를 재사용한다. 보류 관측은
[x, y, ρ=0]에서 시작하고R [x,y,1] + ρ t로 투영한다. ρ는 첫 카메라 Z의 역깊이다. 큰 XYZ나 0으로 나누는 근사를 넣지 않는다. ρ=0의 병진 자코비안은 0이고, 정련 중 후보 ρ를 바꿔 초기의 잘못된 방향/유한 분류도 재평가할 수 있다. 중간 후보의 부호는 확정 깊이로 공개하지 않으며 최종 유한점에는 별도 부호·시차·잔차 검사를 적용한다. 공개 전후 오차 진단은 초기 유한점 집합에 조건을 둔 값이다. - 최종 자세로 다시 분류한다. 방향 호환 관측은 유한점 시차 중앙값에서 제외하고
validPoints=0,points=NaN으로 반환한다. E 인라이어 마스크는 별도다. 활성 모드에서diagnostics.directionCompatibleCount와 mapper의 선택적 동명 쌍 진단을 추가했다. 비활성 모드는 기존 진단 형식을 유지한다. 이 수는 실제 무한원점 수나 전체 미삼각측량 트랙 수가 아니다.
directionCompatible는 현재 자세·보정·허용 오차에 조건을 둔 판정이다. 아주 먼 유한점도
포함될 수 있고, 잡음이나 잘못된 자세 때문에 실제 무한원점이 이 판정을 통과하지 못할 수도
있다. 하늘이라는 의미 분류, 카메라 중심의 정지, 정확히 무한한 깊이를 확정하지 않는다.
CPU와 scene/GPU mapper는 기존 E 인라이어 트랙을 보존한다. 깊이가 없는 관측은
tracking.untriangulatedTrackIds에 남는다. 먼 유한점은 다른 위치에서 다시 관측해
충분한 시차를 얻으면 기존 재삼각측량을 통해 유한점으로 승격된다. 카메라가 움직였어도
해당 점을 다시 보지 않았으면 깊이가 생기지 않는다.
실행·API와 전송 비용
- CPU:
src/sfm/two-view.ts::recoverTwoViewGeometry. - GPU:
src/gpu/recovery.shade.ts의 후보 분류, 자세 선택, LM 수락 검사, 최종 품질 검사. - GPU 호스트:
pair-estimation.ts는 기존 resident 좌표·픽셀·제어 uniform을 바인딩한다. 수치 결과를 CPU에서 다시 계산하는 경로를 추가하지 않는다. 모드가 다른 기존preVerified결과는 재사용하지 않고 현재 모드로 다시 검증한다. 공유 초점거리 탐색의 쌍 캐시에도 이 모드를 전달한다. - 기존 후보 버퍼의 상태 슬롯과 72개 원소 요약 버퍼의 예약 슬롯을 사용한다. N에 비례하는 GPU 배열이나 readback·동기화 단계를 추가하지 않는다. 모드는 기존 제어 uniform으로 전달하고, 기존 최종 요약을 읽을 때 방향 호환 수를 함께 가져온다. 기존 readback 비용·배치별 수치 결과의 회귀 검사도 실행한다.
- 좌표계는 그대로다:
q = R X + t, row-major world-to-camera, 두 시점 출력은 첫 카메라 좌표의 XYZ와 단위 병진 방향이다. 단안 지도 스케일은 임의이며 깊이는 카메라 Z다. 영상 영역·픽셀 좌표·K를 새로 변환하지 않는다.
재현과 평가
tests/fixtures/direction-observations.ts는 기존 일반 영상 기하 fixture에 방향 관측을
더한다. 입력은 알려진 합성 K, 잡음 있는 픽셀과 합성 ID 대응관계다. 정답 포즈·깊이는
생성과 평가에만 사용한다. RGB 특징 검출이나 실제 쇼츠의 성공률 벤치마크가 아니다.
추정 초점거리 실험은 입력 focal을 정답 480 대신 512로 바꾸고 기존 탐색을 사용한다.
npm run build
node --test tests/direction-only-observations.test.ts
node --test tests/typeshade/direction-observations.test.mjs
TYPESHADE_CHROMIUM=/usr/bin/chromium node --test tests/typeshade/direction-observations-webgpu.test.mjs
git worktree add --detach /tmp/typecv-direction-baseline 84d0a98
node scripts/benchmark-direction-depth.mjs \
--baseline=/tmp/typecv-direction-baseline --repeats=2
회귀 검사는 유한 60 + 방향 0/30/60/180, 0/0.2px 잡음, 서로 다른 K의 줌, 순수 방향, 양·음 깊이 50/50, 최소 유한점 부족을 포함한다. 먼 유한점의 재관측 승격과 미재관측 보류는 CPU/scene 양쪽에서 검사하고 GPU에도 공통 fixture로 연결한다. 정답 정렬은 연결된 지도 전체에 한 번 고정한 시드 자세·기준선 gauge를 사용한다.
이전 버전과의 수치 비교는 측정 JSON에 기록한다. 1px 잡음, 추정 K, 먼 유한점, 화면 고정 HUD, 틀린 대응도 실패를 숨기지 않고 포함한다. 시드 거부를 잘못된 유한점 0개라는 이유로 복원 성공으로 세지 않는다.
남은 조건
이 단계는 방향 관측을 전체 지도의 PnP/BA 요인으로 사용하지 않는다. 두 시점 LM의 역깊이 후보와 전체 지도 BA를 구분해야 한다. 방향만 보이는 프레임의 월드 위치·병진을 등록하거나, 큰 잡음에서 잘못 추정한 R/K를 교정하는 단계가 아니다. 1px 잡음의 혼합 장면에서는 자세 오류가 방향을 유한점으로 오확정하는 한계가 남는다. 화면 고정 HUD도 자동으로 월드 방향과 구분되지 않으므로 기존 마스크 정책이 필요하다.
다음 순서는 방향 요인의 회전 잔차와 0인 병진 자코비안을 기존 BA에 연결하고, 역깊이 0과 유한 깊이 사이의 가설을 관측 불확실성과 함께 재평가하는 것이다. 초점거리·줌·동적 전경이 섞인 경우는 별도의 검증 조건을 유지해야 한다. 제공된 실제 쇼츠의 전체 RGB 파이프라인이나 물리 GPU 성능이 개선됐다고 주장하지 않는다.
측정 결과와 기본값 결정
Node 24.19.0, AMD EPYC 9V74에서 기준 84d0a98과 활성 옵션을 비교했다. 26개 기하
시나리오에 버전별 워밍업 1회, 교대 순서의 측정 2회를 사용했고 입력 변경 여부를 해시로
확인했다. 숫자는 합성 장면 단위이며 실제 게임 영상의 성능 비율로 일반화할 수 없다.
| 입력 | 기존 → 활성 옵션 |
|---|---|
| 알려진 K, 유한 60 + 방향 0/30/60/180, 잡음 0/0.2px의 8종 | 등록 합계 24/40 → 40/40, 방향의 유한점 오확정 0개 |
| 유한 60 + 방향 60, 잡음 0 | 초기화 거부 → 카메라 5, 유한점 60, 깊이 보류 트랙 60 |
| 유한 60 + 방향 180, 잡음 0.2px | 초기화 거부 → 카메라 5, 유한점 60, 보류 180; 최대 회전 오차 0.055도 |
| 이동 + 알려진 줌, 잡음 0.2px | 잘못된 방향 깊이 60 → 0; 최대 회전 오차 1.50 → 0.039도 |
| 유한 60 + 방향 60, 잡음 1px | 잘못된 방향 깊이 60 → 1; 완전 해결로 판정하지 않음 |
| 유한 60 + 방향 180, 잡음 1px | 잘못된 방향 깊이 180 → 180, 개선 안 됨 |
| 추정 초점거리, 잡음 0.2px | 잘못된 방향 깊이 24 → 60, focal 상대 오차 10.5% → 28.2% |
| 기존 잡음 회전 시작 정련 사례 | 점 RMSE 0.0984 → 0.2384, 기존 합격선 0.15를 넘음 |
| 순수 방향·회전·회전 줌·아주 먼 유한점 | 양쪽 모두 초기화 보류 |
| 화면 고정 HUD | 양쪽 모두 잘못된 구조가 남음. 정답 출처의 제외 마스크를 쓴 별도 조건에서는 둘 다 유한 배경 60개만 복원 |
기하 복원이 성공한 조건과 실패한 조건의 실행 시간을 속도 비교로 해석하지 않는다.
전체 실행 시간은 JSON의 medianMs에 남겼으며 디코딩·특징 검출은 포함하지 않는다.
두 회의 작은 표본으로 일반 성능 개선을 주장하지 않는다.
실제 WGSL은 Chromium 154의 Google/SwiftShader, fallback=true에서 검사했다. 실행 기록은 6개 두 시점 사례와 혼합·순수 방향·먼 점의 재관측을 포함한다. 시작·컴파일을 포함한 약 54.3초 동안 application upload 10,446,780B, readback 159,512B, read 268회/119 waves를 기록했고 마지막 live buffer/byte는 0이다. 이는 소프트웨어 WebGPU 검사이며 물리 GPU 가속이나 RGB 전체 파이프라인의 시간·메모리 측정이 아니다. peak GPU 메모리는 측정하지 않았다.
코어 302개, 영향받는 TypeShade CPU 50개, 새 기능과 기존 품질 검사의 SwiftShader WebGPU 2개가 통과했다. TypeShade CPU에는 스트리밍 세션의 옵션 스냅샷·2D 보류 유지도 포함된다. 타입·포맷 검사와 코어/GPU 빌드도 통과했다. 비활성 모드의 기존 배치 결과 해시가 그대로 유지되는 검사를 포함하며, 원래 작업 디렉터리의 변경 25개는 별도 해시로 보존했다.
등록률 개선만으로 기본값을 바꾸지 않았다. 위 초점거리·잡음 회귀 때문에 false를 유지하며,
후속 단계의 목표는 미확정 깊이의 오확정을 줄이고 전체 지도 최적화와 보정 추정에 방향
관측을 연결하는 것이다. 웹 UI의 기본 설정도 이번 변경으로 자동 활성화하지 않는다.