Update fit_auto for CameraModel and warn on underdetermined geometry fits
fit_auto now threads camera/camera_tolerance through to fit and _bic (whose parameter count must include any free camera/z unknowns). Emits a UserWarning, not an error, when free camera+z geometry parameters exceed the number of measurement planes -- a new documented degeneracy pitfall. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+53
-6
@@ -1,3 +1,5 @@
|
||||
import warnings
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
@@ -244,24 +246,69 @@ def test_fit_recovers_offset_z_within_tolerance():
|
||||
|
||||
def test_fit_auto_does_not_add_modes_for_pure_fundamental():
|
||||
basis = make_basis()
|
||||
gen = make_generator(basis)
|
||||
camera = make_camera()
|
||||
gen = make_generator(basis, camera)
|
||||
planes = gen.generate(
|
||||
coefficients={(0, 0): 1.0 + 0j}, z_list=Z_LIST, noise_std=1e-4, seed=4
|
||||
coefficients={(0, 0): 1.0 + 0j}, z_list=Z_LIST, image_shape=IMAGE_SHAPE, noise_std=1e-4, seed=4
|
||||
)
|
||||
|
||||
fitter = ModalFitter(basis)
|
||||
result = fitter.fit_auto(planes, max_order=2)
|
||||
result = fitter.fit_auto(planes, camera=camera, camera_tolerance=zero_tolerance(), max_order=2)
|
||||
|
||||
assert set(result.purity.keys()) == {(0, 0)}
|
||||
|
||||
|
||||
def test_fit_auto_grows_to_include_second_mode():
|
||||
basis = make_basis()
|
||||
gen = make_generator(basis)
|
||||
camera = make_camera()
|
||||
gen = make_generator(basis, camera)
|
||||
true_coeffs = {(0, 0): 0.9 + 0j, (0, 1): 0.4 + 0j}
|
||||
planes = gen.generate(coefficients=true_coeffs, z_list=Z_LIST, noise_std=1e-4, seed=5)
|
||||
planes = gen.generate(
|
||||
coefficients=true_coeffs, z_list=Z_LIST, image_shape=IMAGE_SHAPE, noise_std=1e-4, seed=5
|
||||
)
|
||||
|
||||
fitter = ModalFitter(basis)
|
||||
result = fitter.fit_auto(planes, max_order=2)
|
||||
result = fitter.fit_auto(planes, camera=camera, camera_tolerance=zero_tolerance(), max_order=2)
|
||||
|
||||
assert (0, 1) in result.purity or (0, -1) in result.purity
|
||||
|
||||
|
||||
def test_fit_auto_warns_when_free_geometry_params_exceed_plane_count():
|
||||
basis = make_basis()
|
||||
camera = make_camera()
|
||||
gen = make_generator(basis, camera)
|
||||
planes = gen.generate(
|
||||
coefficients={(0, 0): 1.0 + 0j},
|
||||
z_list=Z_LIST, # 4 planes
|
||||
image_shape=IMAGE_SHAPE,
|
||||
z_tolerance=0.05, # +4 free z params
|
||||
noise_std=1e-4,
|
||||
seed=11,
|
||||
)
|
||||
|
||||
# +7 free camera params (all but the 2 principal_point components) +
|
||||
# 4 free z params = 11 free geometry params > 4 planes.
|
||||
generous_tolerance = CameraModelTolerance(
|
||||
focal_length_px=camera.focal_length_px * 0.05,
|
||||
position=(0.01, 0.01, 0.01),
|
||||
orientation_deg=(2.0, 2.0, 2.0),
|
||||
principal_point=(0.0, 0.0),
|
||||
)
|
||||
|
||||
fitter = ModalFitter(basis)
|
||||
with pytest.warns(UserWarning, match="free camera/z geometry parameters"):
|
||||
fitter.fit_auto(planes, camera=camera, camera_tolerance=generous_tolerance, max_order=1)
|
||||
|
||||
|
||||
def test_fit_auto_does_not_warn_when_geometry_fully_fixed():
|
||||
basis = make_basis()
|
||||
camera = make_camera()
|
||||
gen = make_generator(basis, camera)
|
||||
planes = gen.generate(
|
||||
coefficients={(0, 0): 1.0 + 0j}, z_list=Z_LIST, image_shape=IMAGE_SHAPE, noise_std=1e-4, seed=12
|
||||
)
|
||||
|
||||
fitter = ModalFitter(basis)
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("error", UserWarning)
|
||||
fitter.fit_auto(planes, camera=camera, camera_tolerance=zero_tolerance(), max_order=1)
|
||||
|
||||
Reference in New Issue
Block a user