Skip to content
Open
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
49 changes: 29 additions & 20 deletions instantsfm/processors/view_graph_calibration.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,39 @@
from bae.optim import LM
from bae.autograd.function import TrackingTensor

def SolveViewGraphCalibration(view_graph:ViewGraph, cameras, images, VIEW_GRAPH_CALIBRATOR_OPTIONS):
valid_image_pairs = {pair_id: image_pair for pair_id, image_pair in view_graph.image_pairs.items()
if image_pair.is_valid and image_pair.config in [ConfigurationType.CALIBRATED, ConfigurationType.UNCALIBRATED]}
focals = np.array([np.mean(cam.focal_length) for cam in cameras])

problem = pyceres.Problem()
options = pyceres.SolverOptions()
loss_function = pyceres.CauchyLoss(VIEW_GRAPH_CALIBRATOR_OPTIONS['thres_loss_function'])
def SolveViewGraphCalibration(view_graph:ViewGraph, cameras, images, VIEW_GRAPH_CALIBRATOR_OPTIONS):
valid_image_pairs = {pair_id: image_pair for pair_id, image_pair in view_graph.image_pairs.items()
if image_pair.is_valid and image_pair.config in [ConfigurationType.CALIBRATED, ConfigurationType.UNCALIBRATED]}
if len(valid_image_pairs) == 0:
print('No valid image pairs for view graph calibration; skipping.')
return
focals = np.array([np.mean(cam.focal_length) for cam in cameras])

problem = pyceres.Problem()
options = pyceres.SolverOptions()
loss_function = pyceres.CauchyLoss(VIEW_GRAPH_CALIBRATOR_OPTIONS['thres_loss_function'])
if len(cameras) < 50:
options.linear_solver_type = pyceres.LinearSolverType.DENSE_NORMAL_CHOLESKY
else:
options.linear_solver_type = pyceres.LinearSolverType.SPARSE_NORMAL_CHOLESKY

for image_pair in valid_image_pairs.values():
image1, image2 = images[image_pair.image_id1], images[image_pair.image_id2]
cam1, cam2 = cameras[image1.cam_id], cameras[image2.cam_id]
cam_id1, cam_id2 = image1.cam_id, image2.cam_id
if cam_id1 == cam_id2:
cost_function = FetzerFocalLengthSameCameraCostFunction(image_pair.F, cam1.principal_point)
problem.add_residual_block(cost_function, loss_function, [focals[cam_id1:cam_id1+1]])
else:
cost_function = FetzerFocalLengthCostFunction(image_pair.F, cam1.principal_point, cam2.principal_point)
problem.add_residual_block(cost_function, loss_function, [focals[cam_id1:cam_id1+1], focals[cam_id2:cam_id2+1]])
problem.set_parameter_lower_bound(focals, 0, 1e-3)
bounded_cam_ids = set()
for image_pair in valid_image_pairs.values():
image1, image2 = images[image_pair.image_id1], images[image_pair.image_id2]
cam1, cam2 = cameras[image1.cam_id], cameras[image2.cam_id]
cam_id1, cam_id2 = image1.cam_id, image2.cam_id
if cam_id1 == cam_id2:
cost_function = FetzerFocalLengthSameCameraCostFunction(image_pair.F, cam1.principal_point)
problem.add_residual_block(cost_function, loss_function, [focals[cam_id1:cam_id1+1]])
else:
cost_function = FetzerFocalLengthCostFunction(image_pair.F, cam1.principal_point, cam2.principal_point)
problem.add_residual_block(cost_function, loss_function, [focals[cam_id1:cam_id1+1], focals[cam_id2:cam_id2+1]])
if cam_id1 not in bounded_cam_ids:
problem.set_parameter_lower_bound(focals[cam_id1:cam_id1+1], 0, 1e-3)
bounded_cam_ids.add(cam_id1)
if cam_id2 not in bounded_cam_ids:
problem.set_parameter_lower_bound(focals[cam_id2:cam_id2+1], 0, 1e-3)
bounded_cam_ids.add(cam_id2)

options.max_num_iterations = VIEW_GRAPH_CALIBRATOR_OPTIONS['max_num_iterations']
options.function_tolerance = VIEW_GRAPH_CALIBRATOR_OPTIONS['function_tolerance']
Expand Down Expand Up @@ -179,4 +188,4 @@ def forward(self, ds, camera_indices1, camera_indices2):
if loss_sq[idx*2] > thres_two_view_error_sq:
invalid_counter += 1
view_graph.image_pairs[pair_id].is_valid = False
print(f'invalid / total number of two view geometry: {invalid_counter} / {len(valid_image_pairs)}')
print(f'invalid / total number of two view geometry: {invalid_counter} / {len(valid_image_pairs)}')
10 changes: 7 additions & 3 deletions instantsfm/scripts/feat.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
import time
from argparse import ArgumentParser

Expand All @@ -19,9 +20,12 @@ def run_feature_handler():
if not path_info:
print('Invalid data path, please check the provided path')
return
if path_info.database_exists:
print('Database path already exists')
return
force_rebuild = os.environ.get("INSTANTSFM_FORCE_REBUILD_DB", "1").lower() in ("1", "true", "yes")
if path_info.database_exists and not force_rebuild:
if os.path.getsize(path_info.database_path) > 0:
print('Database already exists; skipping feature extraction/matching.')
return
print('Database file exists but is empty; rebuilding.')

start_time = time.time()
config = Config(handler_args.feature_handler, handler_args.manual_config_name)
Expand Down