mirror of
https://github.com/opencv/opencv.git
synced 2026-07-25 21:33:04 +04:00
86b9885c90
Fix empty intrinsics handling and minor calibration improvements #27651 Closes: #27650 Minor changes: * Filter out `""` in `files_with_intrinsics`. * Match the list length with the number of cameras. * Fix unpacking from `cv.calibrateMultiviewExtended`. * Adjust visualisation to ensure correct shapes. * Save `cam_ids` as a list in YAML. * Removed typos. <details> <summary>Pytest</summary> ```py import json import numpy as np import importlib.util import sys import pytest spec = importlib.util.spec_from_file_location("mv", "multiview_calibration.py") mv = importlib.util.module_from_spec(spec) sys.modules["mv"] = mv spec.loader.exec_module(mv) def test_insideImageMask_boundaries(): # w = 10, h = 10, valid x,y: [0..9] pts = np.array([[0, 5, 9, -1, 10], [0, 5, 9, 5, 5]], dtype=np.int32) m = mv.insideImageMask(pts, 10, 10) assert m.tolist() == [True, True, True, False, False] def test_intrinsics_empty_ignored_and_forwarded(tmp_path, monkeypatch): cam1 = tmp_path / "cam1.txt" cam2 = tmp_path / "cam2.txt" cam1.write_text("\n") cam2.write_text("\n") opened = [] class FakeNode: def __init__(self, name): self.name = name def mat(self): if self.name in ("camera_matrix", "cameraMatrix"): return np.eye(3, dtype=np.float32) if self.name in ("dist_coeffs", "distortion_coefficients"): return np.zeros((1, 5), dtype=np.float32) return None class FakeFS: def __init__(self, filename, flags=None): opened.append(filename) def getNode(self, key): return FakeNode(key) monkeypatch.setattr(mv.cv, "FileStorage", FakeFS) captured = {} def fake_calibrateFromPoints(pattern_points, image_points, image_sizes, models, image_names, find_intrinsics_in_python, Ks=None, distortions=None): captured["Ks"] = Ks captured["dist"] = distortions return dict( Rs=[np.zeros((3, 1)) for _ in models], Ts=[np.zeros((3, 1)) for _ in models], Ks=Ks, distortions=distortions, rvecs0=[np.zeros((3, 1))], tvecs0=[np.zeros((3, 1))], errors_per_frame=np.ones((len(models), 1), dtype=np.float32), output_pairs=[], image_points=image_points, models=models, image_sizes=image_sizes, pattern_points=pattern_points, detection_mask=np.ones((len(models), 1), np.uint8), image_names=image_names, ) monkeypatch.setattr(mv, "calibrateFromPoints", fake_calibrateFromPoints) out = mv.calibrateFromImages( files_with_images=[str(cam1), str(cam2)], grid_size=[3, 2], pattern_type="checkerboard", models=[0, 0], dist_m=0.1, winsize=(5, 5), points_json_file="", debug_corners=False, RESIZE_IMAGE=False, # <- match real signature name here find_intrinsics_in_python=False, is_parallel_detection=False, cam_ids=["1", "2"], files_with_intrinsics=["", str(tmp_path / "intr.yml")], board_dict_path=None, ) assert "" not in opened assert captured["Ks"] is not None and len(captured["Ks"]) == 2 assert captured["dist"] is not None and len(captured["dist"]) == 2 def test_visualize_converts_rvec_to_R_and_t_to_col(monkeypatch): called = {} def fake_plotCamerasPosition(Rs, Ts, image_sizes, pairs, pattern, frame_idx, cam_ids, detection_mask): called["R_shapes"] = [R.shape for R in Rs] called["T_shapes"] = [T.shape for T in Ts] def fake_plotProjection(points_2d, pattern_points, rvec0, tvec0, rvec1, tvec1, K, dist_coeff, model, *rest): called["tvec1_shape"] = tvec1.shape called["K_shape"] = K.shape monkeypatch.setattr(mv.plt, "savefig", lambda *a, **k: None) monkeypatch.setattr(mv.plt, "close", lambda *a, **k: None) monkeypatch.setattr(mv, "plotCamerasPosition", fake_plotCamerasPosition) monkeypatch.setattr(mv, "plotProjection", fake_plotProjection) detection_mask = np.ones((1, 1), np.uint8) Rs = [np.zeros((3, 1), dtype=np.float32)] # rvec Ts = [np.array([[0., 0., 0.]], dtype=np.float32)] # row 1x3 Ks = [np.eye(3, dtype=np.float32)] distortions = [np.zeros((1, 5), dtype=np.float32)] models = [0] image_points = [[np.array([[10., 10.]], dtype=np.float32)]] errors_per_frame = np.array([[1.0]], dtype=np.float32) rvecs0 = [np.zeros((3, 1), dtype=np.float32)] tvecs0 = [np.zeros((3, 1), dtype=np.float32)] pattern_points = np.array([[0., 0., 0.]], dtype=np.float32) image_sizes = [(100, 100)] output_pairs = [] image_names = None cam_ids = ["0"] mv.visualizeResults( detection_mask, Rs, Ts, Ks, distortions, models, image_points, errors_per_frame, rvecs0, tvecs0, pattern_points, image_sizes, output_pairs, image_names, cam_ids ) assert called["R_shapes"] == [(3, 3)] assert called["T_shapes"] == [(3, 1)] assert called["tvec1_shape"] == (3, 1) assert called["K_shape"] == (3, 3) ``` </details> ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake