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
6 changes: 4 additions & 2 deletions conda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ dependencies:
- libxcrypt
- libopencv *=headless*
- py-opencv
- conda-forge::libvorbis
- ceres-solver=2.1
- anaconda::ceres-solver=2.2
- conda-forge::llvm-openmp
- conda-forge::cxx-compiler
- anaconda::suitesparse
- conda-forge::openblas
- anaconda::metis
5 changes: 3 additions & 2 deletions opensfm/actions/create_tracks.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ def run_dataset(data: DataSetBase) -> None:
data, data.images()
)
features_end = timer()
matches = tracking.load_matches(data, data.images())
matches = lambda: tracking.load_matches(data, data.images())
matches_end = timer()
tracks_manager = tracking.create_tracks_manager(

tracks_manager = tracking.create_tracks_manager_from_matches_iter(
features,
colors,
segmentations,
Expand Down
6 changes: 5 additions & 1 deletion opensfm/actions/reconstruct.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# pyre-strict
import gc
from opensfm import io, reconstruction
from opensfm.dataset_base import DataSetBase

Expand All @@ -21,5 +22,8 @@ def run_dataset(
else:
raise RuntimeError(f"Unsupported algorithm for reconstruction {algorithm}")

data.save_reconstruction(reconstructions)
del tracks_manager
gc.collect()

data.save_report(io.json_dumps(report), "reconstruction.json")
data.save_reconstruction(reconstructions)
6 changes: 6 additions & 0 deletions opensfm/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,8 @@ class OpenSfMConfig:
# Maximum optimizer iterations.
bundle_max_iterations: int = 100

# Ratio of (resection candidates / total tracks) of a given image so that it is culled at resection and resected later
resect_redundancy_threshold: float = 0.7
# Retriangulate all points from time to time
retriangulation: bool = True
# Retriangulate when the number of points grows by this ratio
Expand All @@ -299,6 +301,10 @@ class OpenSfMConfig:
local_bundle_min_common_points: int = 20
# Max number of shots to optimize during local bundle adjustment
local_bundle_max_shots: int = 30
# Number of grid division for seleccting tracks in local bundle adjustment
local_bundle_grid: int = 8
# Number of grid division for selecting tracks in final bundle adjustment
final_bundle_grid: int = 32

# Remove uncertain and isolated points from the final point cloud
filter_final_point_cloud: bool = False
Expand Down
5 changes: 3 additions & 2 deletions opensfm/dense.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def compute_depthmaps(
num_neighbors = config["depthmap_num_neighbors"]

neighbors = {}
common_tracks = common_tracks_double_dict(graph)
common_tracks = common_tracks_double_dict(graph, processes)
for shot in reconstruction.shots.values():
neighbors[shot.id] = find_neighboring_images(
shot, common_tracks, reconstruction, num_neighbors
Expand Down Expand Up @@ -394,13 +394,14 @@ def compute_depth_range(

def common_tracks_double_dict(
tracks_manager: pymap.TracksManager,
processes: int,
) -> Dict[str, Dict[str, List[str]]]:
"""List of track ids observed by each image pair.

Return a dict, ``res``, such that ``res[im1][im2]`` is the list of
common tracks between ``im1`` and ``im2``.
"""
common_tracks_per_pair = tracking.all_common_tracks_without_features(tracks_manager)
common_tracks_per_pair = tracking.all_common_tracks_without_features(tracks_manager, processes=processes)
res = {image: {} for image in tracks_manager.get_shot_ids()}
for (im1, im2), v in common_tracks_per_pair.items():
res[im1][im2] = v
Expand Down
46 changes: 43 additions & 3 deletions opensfm/exif.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import datetime
import logging
from codecs import decode, encode
import math
from typing import Any, BinaryIO, Callable, Dict, List, Optional, Tuple, Union

import exifread
Expand Down Expand Up @@ -60,22 +61,52 @@ def get_tag_as_float(tags: Dict[str, Any], key: str, index: int = 0) -> Optional
return None


def focal35_to_focal_ratio(
focal35_or_ratio: float, width: int, height: int, inverse=False
) -> float:
"""
Convert focal length in 35mm film equivalent to focal ratio (and vice versa).
We follow https://en.wikipedia.org/wiki/35_mm_equivalent_focal_length
"""
image_ratio = float(max(width, height)) / min(width, height)
is_32 = math.fabs(image_ratio - 3.0 / 2.0)
is_43 = math.fabs(image_ratio - 4.0 / 3.0)
if is_32 < is_43:
# 3:2 aspect ratio : use 36mm for 35mm film
film_width = 36.0
if inverse:
return focal35_or_ratio * film_width
else:
return focal35_or_ratio / film_width
else:
# 4:3 aspect ratio : use 34mm for 35mm film
film_width = 34
if inverse:
return focal35_or_ratio * film_width
else:
return focal35_or_ratio / film_width


def compute_focal(
pixel_width: int,
pixel_height: int,
focal_35: Optional[float],
focal: Optional[float],
sensor_width: Optional[float],
sensor_string: Optional[str],
) -> Tuple[float, float]:
if focal_35 is not None and focal_35 > 0:
focal_ratio = focal_35 / 36.0 # 35mm film produces 36x24mm pictures.
focal_ratio = focal35_to_focal_ratio(focal_35, pixel_width, pixel_height)
else:
if not sensor_width:
sensor_width = (
sensor_data().get(sensor_string, None) if sensor_string else None
)
if sensor_width and focal:
focal_ratio = focal / sensor_width
focal_35 = 36.0 * focal_ratio
focal_35 = focal35_to_focal_ratio(
focal, pixel_width, pixel_height, inverse=True
)
else:
focal_35 = 0.0
focal_ratio = 0.0
Expand Down Expand Up @@ -248,7 +279,10 @@ def extract_projection_type(self) -> str:

def extract_focal(self) -> Tuple[float, float]:
make, model = self.extract_make(), self.extract_model()
width, height = self.extract_image_size()
focal_35, focal_ratio = compute_focal(
width,
height,
get_tag_as_float(self.tags, "EXIF FocalLengthIn35mmFilm"),
get_tag_as_float(self.tags, "EXIF FocalLength"),
self.extract_sensor_width(),
Expand Down Expand Up @@ -636,7 +670,13 @@ def extract_exif(self) -> Dict[str, Any]:

def hard_coded_calibration(exif: Dict[str, Any]) -> Optional[Dict[str, Any]]:
focal = exif["focal_ratio"]
fmm35 = int(round(focal * 36.0))
fmm35 = int(
round(
focal35_to_focal_ratio(
focal, int(exif["width"]), int(exif["height"]), inverse=True
)
)
)
make = exif["make"].strip().lower()
model = exif["model"].strip().lower()
raw_calibrations = camera_calibration()[0]
Expand Down
Loading
Loading