Skip to content

Performance improvement on generalized absolute pose.#4037

Merged
ahojnnes merged 2 commits into
mainfrom
shaohui/perf_gp3p
Jan 21, 2026
Merged

Performance improvement on generalized absolute pose.#4037
ahojnnes merged 2 commits into
mainfrom
shaohui/perf_gp3p

Conversation

@B1ueber2y

@B1ueber2y B1ueber2y commented Jan 20, 2026

Copy link
Copy Markdown
Contributor

main (on the test case from #4023):

Running EstimateGeneralizedAbsolutePose with max_trials=1000
[RANSAC] 1000 trials, 2277 models, 58 inliers
  Estimation: 1.173 ms (11.5875%)
  Residuals:  8.005 ms (79.0773%)
  Scoring:    0.945 ms (9.33518%)
  Total:      10.123 ms
EstimateGeneralizedAbsolutePose RANSAC time: 0.010301 s, num_trials: 1000

After precomputing rig_from_world matrix:

Running EstimateGeneralizedAbsolutePose with max_trials=1000
[RANSAC] 1000 trials, 2277 models, 58 inliers
  Estimation: 1.14 ms (13.3834%)
  Residuals:  6.451 ms (75.7337%)
  Scoring:    0.927 ms (10.8828%)
  Total:      8.518 ms
EstimateGeneralizedAbsolutePose RANSAC time: 0.008678 s, num_trials: 1000

After precomputing cam_from_rig matrix:

Running EstimateGeneralizedAbsolutePose with max_trials=1000
[RANSAC] 1000 trials, 2279 models, 58 inliers
  Estimation: 1.196 ms (15.7431%)
  Residuals:  5.373 ms (70.7253%)
  Scoring:    1.028 ms (13.5317%)
  Total:      7.597 ms
EstimateGeneralizedAbsolutePose RANSAC time: 0.007778 s, num_trials: 1000

Will follow up another update on generalized relative pose.

Edit: In the generalized relative pose case poselib::gen_relpose_6pt is the bottleneck, so there is not much to optimize in the colmap side.

Edit: after switching to eigen expressions:

Running EstimateGeneralizedAbsolutePose with max_trials=1000
[RANSAC] 1000 trials, 2279 models, 58 inliers
  Estimation: 1.198 ms (18.0503%)
  Residuals:  4.455 ms (67.1237%)
  Scoring:    0.984 ms (14.826%)
  Total:      6.637 ms
EstimateGeneralizedAbsolutePose RANSAC time: 0.006816 s, num_trials: 1000

Comment thread src/colmap/estimators/generalized_absolute_pose.cc Outdated
Comment thread src/colmap/estimators/generalized_absolute_pose.cc Outdated
@ahojnnes

Copy link
Copy Markdown
Contributor

An alternative optimization could be to store the "camera_id" and then precompute the cam_from_world matrix once and reuse it for direct transformation from world to camera instead of doing two transformations. Probably depends a bit on the problem and how many cameras it contains on whether this is faster.

@ahojnnes ahojnnes merged commit 754b099 into main Jan 21, 2026
14 checks passed
@ahojnnes ahojnnes deleted the shaohui/perf_gp3p branch January 21, 2026 12:15
ahojnnes added a commit that referenced this pull request May 5, 2026
Hi

I was facing an error with
`pycolmap.estimate_and_refine_generalized_absolute_pose` when installing
pycolmap via pip.

Error:
```
*** Aborted at 1777983489 (unix time) try "date -d @1777983489" if you are using GNU date ***
PC: @     0x73ded64220cb (/local/home/akrishnan/lamar_env_fix/lib/python3.10/site-packages/pycolmap/_core.cpython-310-x86_64-linux-gnu.so+0x10220ca)
Segmentation fault
```

The breaking PR was #4041. I was able to validate this by installing
pycolmap from source on the commit before (#4037). The script below
should work on PR #4037.

Test data:
[Rig_pose_data](https://drive.google.com/file/d/125Y-Pap9FhQUVCayLVfU6iTWXQSHJaS7/view?usp=sharing)

Testing script:

```python
import pickle, time
import numpy as np
import pycolmap
from tqdm import tqdm

PKL = '/local/home/akrishnan/Downloads/rig_pose_debug.pkl'

with open(PKL, 'rb') as f:
    all_data = pickle.load(f)

print(f'pycolmap {pycolmap.__version__} from {pycolmap.__file__}')
print(f'{len(all_data)} entries')

n_ok = 0
n_none = 0
t0 = time.time()
for i, d in enumerate(tqdm(all_data, desc='rig PnP')):
    p2d_per_cam = d['p2d']
    p3d_per_cam = d['p3d']
    camera_dicts = d['camera_dicts']
    qvecs = d['qvecs'] 
    tvecs = d['tvecs']
    thresh = float(d['thresh'])

    p2d_flat = np.concatenate(p2d_per_cam, axis=0).astype(np.float64)
    p3d_flat = np.concatenate(p3d_per_cam, axis=0).astype(np.float64)
    camera_idxs = []
    for ci, p in enumerate(p2d_per_cam):
        camera_idxs.extend([ci] * len(p))

    cams_from_rig = []
    for q_wxyz, t in zip(qvecs, tvecs):
        q_xyzw = np.array([q_wxyz[1], q_wxyz[2], q_wxyz[3], q_wxyz[0]], dtype=np.float64)
        cams_from_rig.append(pycolmap.Rigid3d(
            pycolmap.Rotation3d(q_xyzw),
            np.asarray(t, dtype=np.float64),
        ))

    # Build cameras
    cameras = [
        pycolmap.Camera(
            model=cd['model'],
            width=int(cd['width']),
            height=int(cd['height']),
            params=list(cd['params']),
        )
        for cd in camera_dicts
    ]

    opts = pycolmap.RANSACOptions()
    opts.max_error = thresh

    ret = pycolmap.estimate_and_refine_generalized_absolute_pose(
        p2d_flat, p3d_flat, camera_idxs, cams_from_rig, cameras,
        estimation_options=opts, return_covariance=True,
    )
    if ret is None:
        n_none += 1
    else:
        n_ok += 1

dt = time.time() - t0
print(f'\ndone in {dt:.1f}s — succeeded: {n_ok}/{len(all_data)}, '
      f'returned None: {n_none}/{len(all_data)}')
```

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Johannes Schönberger <jsch@demuc.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants