Repository navigation
Fix OpenCV fisheye unprojection beyond ~80° (pixels at the lens edge render empty) - #1095
Open
nicolaihaeni wants to merge 1 commit into
Open
nicolaihaeni wants to merge 1 commit into
nicolaihaeni wants to merge 1 commit into
Conversation
OpenCVFisheyeCameraModel::image_point_to_camera_ray inverts the forward polynomial theta_d(theta) with Newton iterations started from a linear guess theta0 = theta_d * max_angle / max_normalized_dist. Near the edge of the field of view that guess overshoots; for common ~180 deg fisheye coefficients (k2 < 0) it lands past the forward polynomial's maximum, Newton converges to the second, non-physical root (e.g. ~116 deg), and the max_angle check rejects the ray. All pixels beyond ~80 deg then get no Gaussians and render empty, in both the forward pass and training. Start Newton from the equidistant model theta0 = theta_d instead (CUDA and the PyTorch reference). Adds a regression test with two real fisheye lenses that unprojects points along the image diagonals up to 85 deg. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
With
camera_model="fisheye"and 3DGUT (with_ut=True, with_eval3d=True), pixels whose ray is more than ~80° offthe optical axis render empty (alpha 0) for many real ~180° fisheye lenses. They also get no gradient, so they are
never trained.
Synthetic shell of Gaussians around the camera, OpenCV fisheye camera with the coefficients of a real ~180° lens,
k = [0.0056, −0.0405, 0.0273, −0.0055], 1224×1024, f ≈ 482 px. Left: current
main/ 1.5.3, everything outside thedashed 80° circle is empty. Right: with this PR.
Cause
OpenCVFisheyeCameraModel::image_point_to_camera_ray(gsplat/cuda/include/Cameras.cuh) inverts the forwardpolynomial θ_d(θ) = θ(1 + k1θ² + k2θ⁴ + k3θ⁶ + k4θ⁸) with Newton iterations started from
approx_backward_poly = {0, max_angle / max_normalized_dist}, a linear guess that the code itself calls very crude.For these coefficients
max_angle= 95.1° (capped by the image diagonal) andmax_normalized_dist= 1.27, so theguess overshoots the true angle by ~30 % near the edge. Beyond ~80° the start lies past the forward polynomial's
maximum (~103°), Newton converges to the second, non-physical root (~116°), and
theta >= max_anglemarks the rayinvalid. The cut-off depends on the coefficients and on the image size (it moves with
max_normalized_dist): 80.0°for the lens above, 84.2° for the same lens refined by COLMAP, 84.7° for the first lens on a 1400×1400 image. The
PyTorch reference
_OpenCVFisheyeCameraModeluses the same start and has the same behaviour.The ZipNeRF fisheye scenes used in
examples/benchmarks/3dgut/are not affected: their coefficients are positiveand small, so Newton finds the right root everywhere in the image (out to ~88° in the corners). The existing camera
tests draw coefficients as
0.01 * randn, which doesn't reach this regime either.Reproduce (run once on main and once on this branch)
Fix
Start Newton from the equidistant model, θ0 = θ_d (
approx_backward_poly = {0, 1}), in CUDA and in the PyTorchreference. For fisheye lenses θ_d/θ stays close to 1 (0.97–0.98 at 80–85° for the lenses above), so Newton converges
to the right root; in a float32 simulation of the CUDA code the error is < 1e-4° for all angles up to 95°.
Tests
test_fisheye_unprojection_valid_up_to_lens_edge(CUDA and reference, two real lenses): projects rays at 1–85°along the image diagonals and checks that
image_point_to_camera_rayreturns them as valid rays at the same angle.Without the fix it fails with "44 of 676 in-image points rejected; first rejected angle 80.0 deg".
tests/test_cameras.py: 134 passed (CUDA 13.0, PyTorch 2.9.1, RTX 5090,BUILD_CAMERA_WRAPPERS=1).clang-format22.1.5 reports no changes forCameras.cuh.