Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .github/workflows/format.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Check code formatting (Ubuntu)

on:
push:
branches:
- main
pull_request:
types: [ assigned, opened, synchronize, reopened ]
release:
types: [ published, edited ]

jobs:
build:
name: ${{ matrix.config.os }} formatting
runs-on: ${{ matrix.config.os }}
strategy:
matrix:
config: [
{
os: ubuntu-22.04,
checkCodeFormat: true,
},
]

env:
COMPILER_CACHE_VERSION: 1
COMPILER_CACHE_DIR: ${{ github.workspace }}/compiler-cache
CCACHE_DIR: ${{ github.workspace }}/compiler-cache/ccache
CCACHE_BASEDIR: ${{ github.workspace }}
CTCACHE_DIR: ${{ github.workspace }}/compiler-cache/ctcache

steps:
- uses: actions/checkout@v4
- name: Check code format
run: |
if [ "${{ matrix.config.checkCodeFormat }}" != "true" ]; then
exit 0
fi
set +x -euo pipefail
python -m pip install ruff==0.6.7 clang-format==19.1.0
./scripts/format/clang_format.sh
./scripts/format/python.sh
git diff --name-only
git diff --exit-code || (echo "Code formatting failed" && exit 1)
78 changes: 50 additions & 28 deletions examples/calibrated.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import json
import os
import numpy as np

import cv2
import numpy as np

import madpose
from madpose.utils import get_depths, compute_pose_error
from madpose.utils import compute_pose_error, get_depths

sample_path = 'examples/image_pairs/0_scannet'
sample_path = "examples/image_pairs/0_scannet"

# Thresholds for reprojection and epipolar errors
reproj_pix_thres = 8.0
Expand All @@ -21,35 +22,35 @@
options.final_least_squares = True
options.threshold_multiplier = 5.0
options.num_lo_steps = 4
options.squared_inlier_thresholds = [reproj_pix_thres ** 2, epipolar_pix_thres ** 2]
options.squared_inlier_thresholds = [reproj_pix_thres**2, epipolar_pix_thres**2]
options.data_type_weights = [1.0, epipolar_weight]
options.random_seed = 0
options.random_seed = 0

est_config = madpose.EstimatorConfig()
est_config.min_depth_constraint = True
est_config.use_shift = True

# Read the image pair
image0 = cv2.imread(os.path.join(sample_path, 'image0.png'))
image1 = cv2.imread(os.path.join(sample_path, 'image1.png'))
image0 = cv2.imread(os.path.join(sample_path, "image0.png"))
image1 = cv2.imread(os.path.join(sample_path, "image1.png"))

# Read info
with open(os.path.join(sample_path, 'info.json'), 'r') as f:
with open(os.path.join(sample_path, "info.json")) as f:
info = json.load(f)

# Load camera intrinsics
K0 = np.array(info['K0'])
K1 = np.array(info['K1'])
K0 = np.array(info["K0"])
K1 = np.array(info["K1"])

# Load pre-computed keypoints (you can also run keypoint detectors of your choice)
matches_0_file = os.path.join(sample_path, info['matches_0_file'])
matches_1_file = os.path.join(sample_path, info['matches_1_file'])
matches_0_file = os.path.join(sample_path, info["matches_0_file"])
matches_1_file = os.path.join(sample_path, info["matches_1_file"])
mkpts0 = np.load(matches_0_file)
mkpts1 = np.load(matches_1_file)

# Load pre-computed depth maps (you can also run Monocular Depth models of your choice)
depth_0_file = os.path.join(sample_path, info['depth_0_file'])
depth_1_file = os.path.join(sample_path, info['depth_1_file'])
depth_0_file = os.path.join(sample_path, info["depth_0_file"])
depth_1_file = os.path.join(sample_path, info["depth_1_file"])
depth_map0 = np.load(depth_0_file)
depth_map1 = np.load(depth_1_file)

Expand All @@ -59,18 +60,23 @@

# Run hybrid estimation
pose, stats = madpose.HybridEstimatePoseScaleOffset(
mkpts0, mkpts1,
depth0, depth1,
[depth_map0.min(), depth_map1.min()],
K0, K1, options, est_config
)
mkpts0,
mkpts1,
depth0,
depth1,
[depth_map0.min(), depth_map1.min()],
K0,
K1,
options,
est_config,
)
# rotation and translation of the estimated pose
R_est, t_est = pose.R(), pose.t()
# scale and offsets of the affine corrected depth maps
s_est, o0_est, o1_est = pose.scale, pose.offset0, pose.offset1

# Load the GT Pose
T_0to1 = np.array(info['T_0to1'])
T_0to1 = np.array(info["T_0to1"])

# Compute the pose error
err_t, err_R = compute_pose_error(T_0to1, R_est, t_est)
Expand All @@ -81,18 +87,34 @@
print(f"Estimated scale, offset0, offset1: {s_est:.4f}, {o0_est:.4f}, {o1_est:.4f}")

# Run point-based estimation using PoseLib
import poselib

ransac_opt_dict = {'max_epipolar_error': epipolar_pix_thres, 'min_iterations': 1000, 'max_iterations': 10000}
cam0_dict = {'model': 'PINHOLE', 'width': image0.shape[1], 'height': image0.shape[0], 'params': [K0[0, 0], K0[1, 1], K0[0, 2], K0[1, 2]]}
cam1_dict = {'model': 'PINHOLE', 'width': image1.shape[1], 'height': image1.shape[0], 'params': [K1[0, 0], K1[1, 1], K1[0, 2], K1[1, 2]]}

pose_ponly, stats = poselib.estimate_relative_pose(mkpts0, mkpts1, cam0_dict, cam1_dict, ransac_opt_dict)
import poselib # noqa: E402

ransac_opt_dict = {
"max_epipolar_error": epipolar_pix_thres,
"min_iterations": 1000,
"max_iterations": 10000,
}
cam0_dict = {
"model": "PINHOLE",
"width": image0.shape[1],
"height": image0.shape[0],
"params": [K0[0, 0], K0[1, 1], K0[0, 2], K0[1, 2]],
}
cam1_dict = {
"model": "PINHOLE",
"width": image1.shape[1],
"height": image1.shape[0],
"params": [K1[0, 0], K1[1, 1], K1[0, 2], K1[1, 2]],
}

pose_ponly, stats = poselib.estimate_relative_pose(
mkpts0, mkpts1, cam0_dict, cam1_dict, ransac_opt_dict
)
R_ponly, t_ponly = pose_ponly.R, pose_ponly.t

# Compute the pose error
err_t, err_R = compute_pose_error(T_0to1, R_ponly, t_ponly)

print("--- Point-based Estimation Results ---")
print(f"Rotation Error: {err_R:.4f} degrees")
print(f"Translation Error: {err_t:.4f} degrees")
print(f"Translation Error: {err_t:.4f} degrees")
56 changes: 33 additions & 23 deletions examples/shared_focal.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import json
import os
import numpy as np

import cv2
import numpy as np

import madpose
from madpose.utils import get_depths, compute_pose_error
from madpose.utils import compute_pose_error, get_depths

sample_path = 'examples/image_pairs/1_eth3d'
sample_path = "examples/image_pairs/1_eth3d"

# Thresholds for reprojection and epipolar errors
reproj_pix_thres = 8.0
Expand All @@ -21,35 +22,35 @@
options.final_least_squares = True
options.threshold_multiplier = 5.0
options.num_lo_steps = 4
options.squared_inlier_thresholds = [reproj_pix_thres ** 2, epipolar_pix_thres ** 2]
options.squared_inlier_thresholds = [reproj_pix_thres**2, epipolar_pix_thres**2]
options.data_type_weights = [1.0, epipolar_weight]
options.random_seed = 0
options.random_seed = 0

est_config = madpose.EstimatorConfig()
est_config.min_depth_constraint = True
est_config.use_shift = True

# Read the image pair
image0 = cv2.imread(os.path.join(sample_path, 'image0.png'))
image1 = cv2.imread(os.path.join(sample_path, 'image1.png'))
image0 = cv2.imread(os.path.join(sample_path, "image0.png"))
image1 = cv2.imread(os.path.join(sample_path, "image1.png"))

# Read info
with open(os.path.join(sample_path, 'info.json'), 'r') as f:
with open(os.path.join(sample_path, "info.json")) as f:
info = json.load(f)

# Load camera intrinsics
K0 = np.array(info['K0'])
K1 = np.array(info['K1'])
K0 = np.array(info["K0"])
K1 = np.array(info["K1"])

# Load pre-computed keypoints (you can also run keypoint detectors of your choice)
matches_0_file = os.path.join(sample_path, info['matches_0_file'])
matches_1_file = os.path.join(sample_path, info['matches_1_file'])
matches_0_file = os.path.join(sample_path, info["matches_0_file"])
matches_1_file = os.path.join(sample_path, info["matches_1_file"])
mkpts0 = np.load(matches_0_file)
mkpts1 = np.load(matches_1_file)

# Load pre-computed depth maps (you can also run Monocular Depth models of your choice)
depth_0_file = os.path.join(sample_path, info['depth_0_file'])
depth_1_file = os.path.join(sample_path, info['depth_1_file'])
depth_0_file = os.path.join(sample_path, info["depth_0_file"])
depth_1_file = os.path.join(sample_path, info["depth_1_file"])
depth_map0 = np.load(depth_0_file)
depth_map1 = np.load(depth_1_file)

Expand All @@ -64,11 +65,16 @@

# Run hybrid estimation
pose, stats = madpose.HybridEstimatePoseScaleOffsetSharedFocal(
mkpts0, mkpts1,
depth0, depth1,
[depth_map0.min(), depth_map1.min()],
pp0, pp1, options, est_config
)
mkpts0,
mkpts1,
depth0,
depth1,
[depth_map0.min(), depth_map1.min()],
pp0,
pp1,
options,
est_config,
)
# rotation and translation of the estimated pose
R_est, t_est = pose.R(), pose.t()
# scale and offsets of the affine corrected depth maps
Expand All @@ -77,7 +83,7 @@
f_est = pose.focal

# Load the GT Pose
T_0to1 = np.array(info['T_0to1'])
T_0to1 = np.array(info["T_0to1"])

# Compute the pose error
err_t, err_R = compute_pose_error(T_0to1, R_est, t_est)
Expand All @@ -93,9 +99,13 @@
print(f"Estimated scale, offset0, offset1: {s_est:.4f}, {o0_est:.4f}, {o1_est:.4f}")

# Run point-based estimation using PoseLib
import poselib
import poselib # noqa: E402

ransac_opt_dict = {'max_epipolar_error': epipolar_pix_thres, 'min_iterations': 1000, 'max_iterations': 10000}
ransac_opt_dict = {
"max_epipolar_error": epipolar_pix_thres,
"min_iterations": 1000,
"max_iterations": 10000,
}
img_pair, stats = poselib.estimate_shared_focal_relative_pose(mkpts0, mkpts1, pp, ransac_opt_dict)
shared_focal_pose = img_pair.pose
R_ponly, t_ponly = shared_focal_pose.R, shared_focal_pose.t
Expand All @@ -110,4 +120,4 @@
print("--- Point-based Estimation Results ---")
print(f"Rotation Error: {err_R:.4f} degrees")
print(f"Translation Error: {err_t:.4f} degrees")
print(f"Focal Error: {(err_f * 100):.2f}%")
print(f"Focal Error: {(err_f * 100):.2f}%")
Loading