This commit exposes a `bundle_adjustment` function that has a similar
functionality to the command line utility `colmap bundle_adjuster`. One
use case it enables is to allow one use pycolmap to run bundle
adjustment after sparse reconstruction to refine the principal point, as
jointly refining the principal point[^principal_point_refinement] during
sparse reconstruction would make sparse reconstruction on some scenes to
fail.
Note that not all available members of `ceres::Solver::Options` are
exposed in this change, but only the ones modified by
`BundleAdjustmentOptions` in colmap[^modified_solver_options]. Besides,
the registered `CeresSolverOptions` uses `py::module_local()` to avoid
conflicts with downstream libraries.
Example usage
=============
```python
from pathlib import Path
import pycolmap
sparse_reconstruction_directory = Path("sparse/0") # or just `... = "sparse/0"`
ba_opts = {
"refine_principal_point": True,
"solver_options": {
"max_num_iterations": 256,
},
}
pycolmap.bundle_adjustment(
input_path=sparse_reconstruction_directory,
output_path=sparse_reconstruction_directory,
options=ba_opts,
)
```
Known problem
=============
Though creating a `pycolmap.BundleAdjustmentOptions` object with a
nested `dict` works as expected, i.e., the following examples work well:
```python
import pycolmap
ba_opts = pycolmap.BundleAdjustmentOptions({
"refine_principal_point": True,
"solver_options": {
"max_num_iterations": 256,
},
})
print(ba_opts.todict()) # runs successfully with user-specified option values
ba_opts = pycolmap.BundleAdjustmentOptions({
refine_principal_point=True,
solver_options={"max_num_iterations": 256},
})
print(ba_opts.todict()) # runs successfully with user-specified option values
```
it gives an error while trying to create a
`pycolmap.BundleAdjustmentOptions` object with its `solver_options` set
to a `pycolmap.CeresSolverOptions` object, i.e. the following gives an
error:
```python
import pycolmap
solver_options = pycolmap.CeresSolverOptions(max_num_iterations=256)
print(solver_options.todict()) # runs successfully with user-specified option values
ba_opts = pycolmap.BundleAdjustmentOptions({
"refine_principal_point": True,
"solver_options": solver_options
}) # RuntimeError: instance allocation failed: new instance has no pybind11-registered base types
```
The "new instance" mentioned in the RuntimeError message probably points
to the `CeresSolverOptions` added in this commit, but I'm not sure about
a way to solve this at the moment, plus, as reported above, using
`dict`s to specify the option values works properly and works around
this error.
[^principal_point_refinement]: <https://colmap.github.io/faq.html#frequently-asked-questions>
[^modified_solver_options]: <https://github.com/colmap/colmap/blob/3.8/src/optim/bundle_adjustment.h#L81-L89>
Signed-off-by: Gaoyang Zhang <gy@blurgy.xyz>