This repository contains a Python migration of the original MATLAB implementation for "Relative pose from cylinder silhouettes," a paper presented at ACCV 2024. The library provides tools to estimate the relative pose between two cameras by observing the silhouettes of two cylinders.
The full pipeline is now working end-to-end with real data!
- ✅ Polynomial Solvers: Both solvers fully implemented with all coefficient expressions
- ✅ Symbolic Coefficient Extraction: Implemented using SymPy for geometric constraint equations
- ✅ Rectification: Camera and line rectification working correctly
- ✅ Full Pipeline: Tested with real roller coaster data, produces valid reconstructions
- ✅ 4 Solutions: Typical output includes multiple geometric configurations
- ✅ Execution Time: ~2-3 seconds per reconstruction
The problem of relative pose estimation is a fundamental task in computer vision. This work introduces a novel method for solving this problem using the silhouettes of cylinders. By observing two cylinders from two different camera viewpoints, a set of geometric constraints can be formulated. These constraints lead to a system of polynomial equations that can be solved to recover the relative camera pose and the 3D structure of the cylinders.
This Python library implements the core algorithms described in the paper, including:
- Data Rectification: Simplifies the problem by rotating the camera and line observations into a canonical coordinate system.
- Polynomial Solvers: Minimal solvers for the resulting polynomial systems (✅ now fully implemented).
- Geometric Utilities: A collection of helper functions for projective geometry, rotation conversions, and visualization.
To install the cylinder-sfm package, you can use pip from the root of this repository:
pip install .For development, you can install in editable mode with the development dependencies:
pip install -e ".[dev,examples]"The library is structured into several modules, each with a specific purpose. The main entry point is the relative_pose_conics function.
cylinder_sfm.solver: Contains the mainrelative_pose_conicsfunction.cylinder_sfm.structures: Defines the data structures used, such asLineObservations,ReconstructionResult, andScaleConstraint.cylinder_sfm.rectification: Provides functions for data rectification.cylinder_sfm.utils: Includes various geometric utility functions.cylinder_sfm.solvers: Contains the low-level polynomial solvers (fully implemented).
relative_pose_conics(lines, P1, scale_constraint, ...)
This is the primary function for estimating relative pose. It takes line observations, the pose of the first camera, and a scale constraint to produce a list of possible reconstruction results.
Parameters:
| Parameter | Type | Description |
|---|---|---|
lines |
LineObservations |
A data class containing the eight observed silhouette lines. |
P1 |
np.ndarray |
The 3x4 camera matrix of the first camera. |
scale_constraint |
ScaleConstraint |
The scale constraint, either p2p_dist (camera distance) or C1_r (radius). |
flip_rectify |
bool |
Optional flag to handle rectification edge cases. |
use_e3q3 |
bool |
Optional flag to use an external solver (not implemented). |
Returns:
v1,v2: Vanishing points for the cylinder axes.rotC1,rotC2: Rectification rotations for each camera.results: A list ofReconstructionResultobjects, each containing a possible solution.
LineObservations: A dataclass to hold the eight line observations from the two cylinders in the two cameras.ReconstructionResult: A dataclass that stores a single solution, including the pose of the second camera (P2), cylinder positions (T1_t,T2_t), radii (r1,r2), and rotations (T1_R,T2_R).ScaleConstraint: A dataclass to specify the scale constraint, which is necessary to resolve the scale ambiguity of the reconstruction.
Here is a basic example of how to use the library to solve for relative pose.
import numpy as np
from cylinder_sfm import (
relative_pose_conics,
LineObservations,
ScaleConstraint,
pflat
)
# 1. Define line observations (replace with actual data)
# l_ijk: camera i, cylinder j, line k
lines = LineObservations(
l_111=pflat(np.array([0.5, 0.2, 1.0]))[0].flatten(),
l_112=pflat(np.array([-0.5, 0.2, 1.0]))[0].flatten(),
l_121=pflat(np.array([0.8, -0.1, 1.0]))[0].flatten(),
l_122=pflat(np.array([-0.8, -0.1, 1.0]))[0].flatten(),
l_211=pflat(np.array([0.6, 0.3, 1.0]))[0].flatten(),
l_212=pflat(np.array([-0.6, 0.3, 1.0]))[0].flatten(),
l_221=pflat(np.array([0.9, -0.2, 1.0]))[0].flatten(),
l_222=pflat(np.array([-0.9, -0.2, 1.0]))[0].flatten(),
)
# 2. Define camera 1 pose (identity)
P1 = np.hstack([np.eye(3), np.zeros((3, 1))])
# 3. Define scale constraint (e.g., distance between cameras is 5 units)
scale = ScaleConstraint(value=5.0, type='p2p_dist')
# 4. Solve for relative pose
v1, v2, rotC1, rotC2, results = relative_pose_conics(lines, P1, scale)
# 5. Process results
if results:
print(f"Found {len(results)} solutions.")
best_result = results[0]
print("\n--- Best Solution ---")
print(f"Camera 2 Pose (P2):\n{best_result.P2}")
print(f"Cylinder 1 Radius: {best_result.r1:.4f}")
print(f"Cylinder 2 Radius: {best_result.r2:.4f}")
print(f"Cylinder 1 Center: {best_result.T1_t}")
print(f"Cylinder 2 Center: {best_result.T2_t}")
else:
print("No solutions found.")A comprehensive test script is included to validate the solvers:
python test_solvers.pyThis will test:
- Solver 1 (camera translation in x-z plane)
- Solver 2 (remaining variables: y, cylinder 2 position and radius)
- Full pipeline integration
The core of this method relies on solving systems of polynomial equations. The solvers use Gröbner basis methods and action matrix eigenvalue decomposition.
Solver 1: Camera Translation
- Input: Quaternion for P2 rotation + 3 line parameters
- Output: 2-4 solutions for camera translation (x, z)
- Method: 4×4 action matrix with quotient ring basis [1, x, y, y²]
Solver 2: Remaining Variables
- Input: 23 polynomial coefficients
- Output: Up to 8 solutions for (y, x_T2, z_T2)
- Method: 8×8 action matrix with quotient ring basis [1, x, x·z, y, y·z, z, z², z³]
All coefficient expressions have been fully translated from the original MATLAB implementation, preserving numerical accuracy.
[1] Wadenbäck, M., & Heyden, A. (2024). Relative pose from cylinder silhouettes. In Proceedings of the Asian Conference on Computer Vision (ACCV 2024). https://link.springer.com/chapter/10.1007/978-981-96-0969-7_15
This project is licensed under the MIT License. See the LICENSE file for details.
- Original MATLAB implementation: hamburgerlady/cylinder-SfM
- Python migration by Manus AI