Skip to main content

ViewGraphForge

PyPI Python License: MIT Dataset: ViewGraphBench

ViewGraphForge generates view graphs for the global stage of Structure-from-Motion (SfM) and evaluates solvers on them: rotation averaging, translation averaging, SE(3) pose-graph optimisation and the detection of outlier edges. It is the generator of ViewGraphBench, a dataset of 45,495 view graphs with 12 to 100,000 cameras each.

A graph is made by simulating the front end of a global-SfM pipeline on a procedural 3-D scene. Cameras following one of seven capture patterns observe the scene points; edges come from co-visibility and an image-retrieval step; match counts and measurement errors follow from the viewing geometry; and repeated structure in the scene produces wrong relative poses that agree with each other around cycles. Every graph stores the ground truth of each camera and of each measurement, including the type of every outlier.

One graph of each of the seven camera-network families

The seven camera-network families (top views of bench_topology graphs): cameras in blue, inlier edges in grey, random outliers in red, pink and purple, gross errors of the noise model in yellow.

The package provides

  • the generator: YAML configurations, parallel and resumable generation with a memory-aware scheduler;
  • the .npz graph format with loaders, and g2o export and import;
  • baseline solvers for the four tasks, evaluation metrics and a benchmark runner with leaderboards;
  • validation, statistics, figures, and the tools that prepare a dataset for the Hugging Face Hub.

Contents

  1. Installation
  2. Quick start
  3. The ViewGraphBench dataset
  4. How a graph is generated
  5. What a graph contains
  6. Generating datasets
  7. Benchmark
  8. Figures
  9. Command reference
  10. Parameter reference
  11. Examples
  12. Tests and reproducibility
  13. Troubleshooting
  14. Citation
  15. References
  16. License

1. Installation

pip install viewgraphforge

ViewGraphForge needs Python 3.10 or newer and depends on numpy, scipy, pandas, matplotlib and PyYAML. It is pure Python and has been used on Linux and Windows. pip install "viewgraphforge[hub]" also installs huggingface_hub 1.0 or newer, whose hf command downloads the dataset. For development:

git clone https://github.com/Ezharjan/ViewGraphForge.git
cd ViewGraphForge
pip install -e ".[dev]"
python -m pytest

2. Quick start

Generate a graph, run two baselines and save it:

import viewgraphforge as vgf
from viewgraphforge.benchmark import metrics, rotation_averaging, translation_averaging

g = vgf.generate_graph({"family": "crowd", "n_nodes": 200, "outlier_ratio": 0.2}, seed=1)
print(g.n_nodes, g.n_edges, g.is_outlier.mean())         # cameras, edges, fraction of outlier edges

R = rotation_averaging.l1_irls(g)                         # global rotations from the relative ones
print(metrics.rotation_metrics(R, g.R_wc)["rot_median_deg"])
C = translation_averaging.lud(g, R)                       # camera centres up to a similarity
print(metrics.position_metrics(C, g.centers)["pos_median_rel"])

vgf.save_graph(g, "crowd.npz")                            # also writes crowd.json with the metadata
vgf.write_g2o(g, "crowd.g2o")

On the command line, generate a small dataset (53 graphs in 11 splits) and run the tools on it:

vgf generate --config smoke --out data_smoke --workers 2
vgf validate --data data_smoke
vgf stats --data data_smoke
vgf benchmark --data data_smoke --workers 2
vgf visualize --data data_smoke

vgf --help lists all commands and vgf <command> --help explains the options of one.

3. The ViewGraphBench dataset

ViewGraphBench was generated with the full configuration of this package (vgf config full prints it). It holds 45,495 graphs with about 15.2 million cameras and 291 million relative-pose measurements in 16 splits:

split graphs cameras requested what varies
train, val, test_wild 40,000, 2,000, 2,000 30 to 800 random draws of family, size, pixel noise, outlier ratio and type, retrieval depth, detection rate, noise model, heavy tails, scale noise and repeated structure
test_wild_large 200 800 to 5,000 the same with larger graphs (analytic noise model only)
bench_scale 231 50 to 100,000 number of cameras
bench_noise 147 300 pixel noise, 0.25 to 16 px
bench_outliers 168 300 fraction of random outliers, 0 to 0.6
bench_outlier_mode 63 300 what a random outlier replaces: rotation, direction or both
bench_symmetry 126 300 confusability of repeated structure, 0 to 0.75
bench_density 126 300 retrieval depth, 3 to 80 views kept per camera
bench_heavytail 84 300 Gaussian noise and Student-t noise with 5, 3 and 2 degrees of freedom
bench_matches 84 300 minimum number of matches per edge, 10 to 80
bench_topology 70 300 camera-network family, 10 repetitions
bench_twoview 63 100, 300, 1,000 number of cameras, with the simulated two-view estimator
bench_twoview_symmetry 28 300 repeated structure, with the simulated two-view estimator
bench_pgo 105 500 log-normal noise of the metric baseline lengths, 0.01 to 0.4

The benchmark splits vary one factor over the seven camera families (or over seven cases with repeated structure), mostly with three repetitions. All levels of a factor use the same seed in a repetition, so the scene, the cameras and the random numbers are the same up to the step where the factor acts. Download the parts you need with the hf command (* also matches /):

pip install "viewgraphforge[hub]"
hf download ezharjan/ViewGraphBench --repo-type dataset --local-dir ViewGraphBench --include "splits/bench_*" --include "*.csv" --include "*.json" --include "manifest_sha256.txt"
import viewgraphforge as vgf

index = vgf.read_index("ViewGraphBench")                  # one row per graph: split, family, size, settings
rows = index[(index.split == "bench_outliers") & (index.outlier_ratio_target == 0.3)]
graphs = [vgf.load_graph(f"ViewGraphBench/{p}") for p in rows.npz_path]

The dataset card describes the files, the complete array layout, the statistics of every split and the baseline results.

4. How a graph is generated

Global SfM back ends are usually evaluated on view graphs of real photo collections such as those of 1DSfM [17], whose reference poses are themselves reconstructions, or on synthetic graphs with random topology, isotropic noise and uniformly random outliers. ViewGraphForge simulates the front end instead: edges, match counts and errors follow from a simulated scene and camera network, and the ground truth is exact. Each graph is generated from its own seed:

  1. Scene: a procedural point cloud with surface normals (object, facade, corridor, urban block, room, terrain or plaza) whose extent grows with the number of cameras for the corridor, urban, terrain and facade scenes. Scenes with repeated structure contain identical copies of a module: the sectors of a symmetric object, facade columns, corridor segments, twin buildings, the faces of a tower or a monument.
  2. Cameras: exactly n pinhole cameras (1600 x 1200 px) of one family: orbit (a ring around an object), dome (several rings on a hemisphere), corridor (forward motion along a street), lawnmower (an aerial survey), crowd (unordered photos of a landmark with varying focal lengths), walk (hand-held motion in a room) or manhattan (a vehicle on a street grid, with loop closures).
  3. Visibility: a point is observed when it is within the depth range, projects into the image, faces the camera, passes a z-buffer occlusion test and is detected; at most 3,000 points per image are kept.
  4. Edges: in graphs of up to 400 cameras every camera pair is a candidate; larger graphs take the nearest neighbours in camera position and viewing direction, the three previous and next cameras in acquisition order and, for repeated structure, random pairs of cameras that see copies of the same module. Match counts are binomial in the number of co-visible points with a probability that falls with the angle between the views and the scale change. Repeated structure adds competing hypotheses, and the reported relative pose is drawn among those with enough matches, favouring the one with the most support. Each camera keeps its retrieval_k candidates with the most co-visible points and up to cand_sym_k identical-looking views, a candidate becomes an edge when its reported pose has at least min_matches matches, and the largest connected component is kept.
  5. Measurements: the reported relative pose with noise, from a closed-form model (analytic) or from a simulated two-view estimator (twoview: the normalised 8-point algorithm [1] on noisy correspondences, Levenberg-Marquardt refinements of the Sampson error [10] from its solution and from the reported pose, of which the one with the lower error is kept, and covariances from the Fisher information). The analytic model is calibrated against the estimator: its errors scale with the pixel noise over the focal length and with 1/sqrt(matches), depend on the baseline-to-depth ratio and carry a log-normal factor per edge, with optional Student-t tails (analytic model only).
  6. Outliers: a fraction of the edges, chosen with probability inversely proportional to their number of matches, receives random rotations and/or directions; the symmetry-induced outliers come from step 4. A metric translation is added for pose-graph optimisation, and every error is recorded.

5. What a graph contains

A graph is one compressed numpy archive (.npz) with n cameras and m edges, every camera pair stored once with i < j. The main arrays:

arrays content
node_q_wc, node_t_wc, node_center, node_K ground-truth world-to-camera rotation (quaternion) and translation, camera centre, intrinsics
edge_i, edge_j the two cameras of an edge
edge_q_meas, edge_tdir_meas, edge_t_meas measured relative rotation, unit translation direction and metric translation
edge_q_true, edge_tdir_true, edge_t_true the same quantities from the ground-truth poses
edge_sigma_rot, edge_sigma_dir, edge_sigma_trans noise levels reported for the measurement: standard deviations, or the Student-t scale with heavy tails
edge_n_matches, edge_n_covis, edge_baseline, edge_median_depth, edge_view_angle_deg, edge_overlap_i, edge_overlap_j covariates of the edge
edge_err_rot_deg, edge_err_dir_deg, edge_err_trans true errors of the measurement
edge_outlier_type, edge_is_outlier, edge_symmetry_id outlier labels (table below) and the symmetry behind a symmetry-induced outlier
points_*, obs_*, sym_* scene points, image observations of the points and the symmetry transforms behind the symmetry-induced outliers
meta_json generation parameters, seed and statistics
edge_outlier_type the measurement is
0 none the true relative pose with noise (inlier)
1 random_both a random rotation and a random direction
2 random_rotation a random rotation with the noisy true direction
3 random_translation a random direction with the noisy true rotation
4 symmetry the relative pose, with noise, to a virtual camera: the true camera moved by a symmetry of the scene (for the translational symmetries of periodic facades and corridors, only the direction is wrong)
5 gross_error not injected: a measurement of the noise model with an error above 10 deg in rotation or 20 deg in direction

Conventions: metres, world frame with +z up, OpenCV camera frame (x right, y down, z forward), world-to-camera poses x_cam = R_i X + t_i, relative poses R_ij = R_j R_i^T and t_ij = t_j - R_ij t_i, quaternions (qx, qy, qz, qw) with qw >= 0. The .g2o files hold camera-to-world vertices (R_i^T, C_i) initialised from a spanning tree of the measurements and EDGE_SE3:QUAT measurements Z_ij = (R_ij^T, -R_ij^T t_ij) with their information matrices, whose rotation block refers to the rotation vector. GTSAM [15] reads them as they are; for SE-Sync [16], remove the first line, a comment. g2o [14] reads them too, but its EdgeSE3 applies the rotation block to the vector part of the quaternion, about half the rotation vector, which weights rotations four times less (multiply the block by 4 to match). vgf.load_graph returns a PoseGraph with the arrays and convenience properties (R_wc, centers, edges, R_meas, tdir_meas, t_meas, is_outlier, meta).

Overview of one graph

One graph in detail (vgf visualize): top and 3-D view, adjacency in acquisition order, the errors of each edge class and the direction error against the baseline-to-depth ratio.

Repeated structure is what makes the symmetry-induced outliers (type 4) hard: every edge corrupted by the same symmetry agrees with the same moved camera, so the corrupted edges close their own cycles.

Symmetry-induced outliers in the seven cases with repeated structure

The seven cases with repeated structure of bench_symmetry at confusability 0.5; the orange edges are symmetry-induced outliers. The twin buildings of the urban case produce few of them.

6. Generating datasets

A dataset is described by a YAML configuration. Two are shipped: smoke (53 graphs, for trying things out) and full (the published dataset). vgf config lists them and vgf config full prints one.

vgf generate --config full --out data_full --workers 16

Each graph is written to <out>/splits/<split>/<graph_id>.npz together with a .json copy of its metadata and, for graphs of up to 20,000 cameras (write_g2o_max_nodes), a .g2o file; splits of more than 3,000 graphs are divided into folders of 1,000 graphs (splits/train/01000-01999/). At the end the command writes index.csv, manifest_sha256.txt and dataset_info.json, and the log goes to <out>/logs/.

  • Resuming. Running the same command again continues an interrupted run: finished graphs are kept, partial files are removed and missing .json or .g2o files are exported again from the .npz. All files are written atomically. A lock file keeps a second generator away from the same folder.
  • Memory. Graphs run in --workers processes, largest first. A graph is started only when its estimated peak memory fits into the budget (--max-memory-gb, 70 % of the RAM by default) next to the memory the workers actually use, which the scheduler measures. The estimates are corrected by the peaks measured during the run, and a graph that still fails with MemoryError is retried. Workers are replaced after 50 graphs or when they hold more than 1 GB, and the BLAS libraries run single-threaded inside them (through threadpoolctl when it is installed; without it, thread counts already set in the environment are kept).
  • Time and disk. The published dataset took about 13 hours with 16 workers on a laptop with 16 logical CPUs and 32 GB of RAM, and takes 139 GB with the .json and .g2o files. Most graphs of up to 1,000 cameras take seconds (the twoview graphs up to a few minutes); the 100,000-camera graphs of bench_scale take from 3 minutes to 2.7 hours each and up to 4.3 GB of memory. The generator uses the CPU only.
  • Subsets and previews. --splits train val generates some splits, --limit N the next N graphs, and --dry-run (with --list for every graph) prints the plan without writing anything.

A configuration lists splits of three kinds. sweep varies one factor over a list of values, grid repeats fixed settings and wild draws the settings at random:

dataset: {name: MyViewGraphs, version: "1.0.0", seed: 7}
generation: {workers: 8, write_g2o: true, write_json: true}
defaults: {store_points: true}                 # applied to every graph
splits:
  outliers:
    kind: sweep
    factor: outlier_ratio
    values: [0.0, 0.2, 0.4]
    families: all                              # or a list, e.g. [orbit, crowd]
    seeds: 5
    params: {n_nodes: 500}
  corridors:
    kind: grid
    families: [corridor]
    seeds: 20
    params: {n_nodes: 1000, noise_model: twoview}
  training:
    kind: wild
    count: 5000
    sampler: {n_nodes: {min: 50, max: 2000, log: true, int: true}}

Every graph parameter of section 10 can appear in defaults, params or as the factor of a sweep. In a wild split the values drawn by the sampler take precedence over defaults and params; their ranges are set in sampler (the keys of DEFAULT_WILD_SAMPLER in viewgraphforge/splits.py). vgf config smoke > my.yaml writes a complete example to start from.

7. Benchmark

vgf benchmark --data ViewGraphBench --splits "bench_*" "test_*" --workers 16

runs the baselines on the graphs of the selected splits and writes benchmarks/results.csv (one row per graph and method), leaderboard.csv and leaderboard.md (means per split and method), sweeps.csv (metrics against the swept factor) and run.json (settings and software versions). Results are cached, so an interrupted run continues where it stopped. By default graphs with more than 3,000 cameras are skipped (--max-nodes), and pose-graph optimisation runs on graphs of up to 1,500 cameras (--max-nodes-pgo).

task baselines metrics
rotation averaging spanning tree; chordal L2 [2]; chordal L2 with Cauchy IRLS; L1 averaging followed by IRLS [4, 5, 6], with and without information weights rotation errors after robust alignment of the global rotation: median, mean, RMS, 90th percentile, fraction below 1, 3, 5 and 10 deg
translation averaging linear cross-product method [3]; least unsquared deviations, LUD [7]; each with ground-truth and with L1-IRLS rotations position errors after a similarity alignment [11], absolute and relative to the extent of the cameras
pose-graph optimisation spanning-tree initialisation; Levenberg-Marquardt with exact SE(3) Jacobians [12, 13] and L2, Huber and Cauchy losses; Cauchy from a rotation-averaging initialisation [9] position and rotation errors after a rigid alignment, iterations
outlier detection triangle cycle consistency [8]; rotation residuals after L1-IRLS; direction residuals after LUD precision, recall, F1 and average precision against the edges with a rotation error above 5 deg (direction error above 10 deg); graphs without such edges are left out of the means

Rotation averaging against the fraction of random outliers Outlier detection against the confusability of repeated structure

Left: median rotation error against the fraction of random outliers (bench_outliers). Right: F1 score of outlier detection against the confusability of repeated structure (bench_symmetry); cycle consistency misses almost all symmetry-induced outliers. Each point is the mean over the 21 graphs of one level (for F1, over those with labelled outliers).

A method of your own is a function of the graph; registered in the method table, it is evaluated with the same protocol by the serial runner (the worker processes of vgf benchmark know only the built-in methods):

from viewgraphforge.benchmark import rotation_averaging, runner

rotation_averaging.ROTATION_METHODS["rot/my_method"] = my_solver    # graph -> (n,3,3) world-to-camera rotations
results = runner.run_dataset("ViewGraphBench", splits=["bench_outliers"], tasks=["rotation"])

examples/09_benchmark_and_leaderboard.py does this and builds the leaderboard and the sweep plots.

8. Figures

vgf visualize --data <folder> renders into <folder>/figures/: the family and symmetry overviews above, per-graph overviews, a gallery of every split, the dataset summary, the sweep curves and leaderboard bars of the benchmark, and solver results on a few graphs such as the one below. The plotting functions are available in viewgraphforge.viz (see examples/10_visualize_dataset.py).

Rotation and translation averaging on a dome graph

A dome graph with 300 cameras and 10 % outliers: per-camera rotation errors of four rotation-averaging methods, the errors of L1-IRLS on the cameras, and the camera centres from LUD after a similarity alignment.

9. Command reference

command purpose
vgf generate --config <name or file> --out <folder> generate a dataset; parallel, resumable, memory-aware
vgf validate --data <folder> check every graph, its .g2o file, index.csv and the SHA-256 manifest
vgf stats --data <folder> statistics per graph and per split in stats/
vgf benchmark --data <folder> run the baselines and write the leaderboards in benchmarks/
vgf visualize --data <folder> render the figures in figures/
vgf export --data <folder> --out <folder> assemble the files published on the Hugging Face Hub (hard links, sub-folders for large splits)
vgf card --data <folder> write the dataset card (README.md) from the files of a folder
vgf config [name] list the shipped configurations or print one
vgf calibrate fit the analytic noise model to the simulated two-view estimator
vgf clean remove caches, build files and the leftovers of interrupted runs; --data <folder> --wipe deletes a dataset

validate, stats and benchmark accept --workers N. The --splits option of generate, validate, benchmark and visualize takes split names or shell-style patterns such as "bench_*", as does --g2o of export; stats reads every split. python -m viewgraphforge is the same as vgf.

10. Parameter reference

Graph parameters with their defaults (viewgraphforge/config.py):

parameters defaults meaning
family, scene orbit, family default camera family (orbit, dome, corridor, lawnmower, crowd, walk, manhattan) and scene (object, facade, corridor, urban, room, terrain, plaza)
n_nodes 200 number of cameras; the largest connected component of the view graph is kept
n_points, scene_params scale-aware scene points and options of the scene builder, e.g. {symmetry_fold: 4}, {periodic: true}, {tower_fold: 4}, {twin_buildings: 4}, {monument_fold: 2}
camera_params, image_size, hfov_deg family defaults, 1600 x 1200 options of the camera family and intrinsics
detection_prob, max_depth_factor, max_depth_abs, max_incidence_deg, zbuffer_tol, max_visible_per_camera 0.8, 6, 400 m, 80 deg, 0.08, 3000 visibility model
retrieval_k, min_matches, p_match_max, theta0_deg, s0, hypothesis_sharpness 20, 20, 0.7, 40 deg, 0.7, 4 retrieval depth, minimum matches of an edge, match probability model and selection among competing hypotheses
symmetry_confusability, cand_sym_k 0, 4 factor on the match probability of correspondences between two copies of repeated structure (0: never confused); retrieval budget per camera for identical-looking views
noise_model, pixel_noise, heavy_tail_df, scale_noise analytic, 1 px, 0 (Gaussian), 0.05 measurement noise; twoview runs the simulated two-view estimator
outlier_ratio, outlier_mode, outlier_weighting 0.1, mixed, inverse_matches random outliers: fraction, type (rotation, translation, both, or mixed: both, translation and rotation with probabilities 0.4, 0.4 and 0.2) and choice of the edges
gross_rot_deg, gross_dir_deg 10 deg, 20 deg thresholds of the gross_error label
store_points, max_points_stored, store_observations, store_observations_max_nodes, max_observations_per_camera true, 20000, true, 2000, 300 stored scene points and image observations

The constants of the analytic noise model (viewgraphforge/noise.py) were fitted with vgf calibrate --n-nodes 100 --seeds 3 on a development version of the generator (28,715 inlier edges; viewgraphforge/configs/noise_calibration.json holds the fit). The released version gives similar but not identical constants (about 29,800 edges, a_rot 35.4 instead of 36.7); the shipped constants are kept so that ViewGraphBench can be regenerated.

11. Examples

The examples run on the smoke dataset (vgf generate --config smoke --out data_smoke --workers 2) or on a downloaded copy of ViewGraphBench:

example shows
01_load_and_inspect.py the arrays of a graph and a check of the pose, relative-pose and epipolar conventions
02_rotation_averaging.py rotation averaging by spanning tree, chordal L2, chordal L2 with Huber IRLS and L1-IRLS, and the error distributions
03_translation_averaging.py world-frame baseline directions, the linear method and LUD with true and estimated rotations
04_pose_graph_optimization_g2o.py robust SE(3) pose-graph optimisation from a .g2o file, with an optional GTSAM cross-check
05_outlier_filtering.py cycle-consistency and residual filters, and why symmetry-induced outliers defeat cycle consistency
06_learning_edge_classifier.py per-edge features, conversion to PyTorch Geometric and a learned outlier classifier on the wild splits
07_point_based_global_positioning.py camera positions from the image observations, the idea behind the global positioning of GLOMAP [18], where camera-only translation averaging fails
08_custom_generation.py generating graphs from Python with repeated structure, two-view noise and heavy tails
09_benchmark_and_leaderboard.py the benchmark runner with a method of your own, a leaderboard and a sweep plot
10_visualize_dataset.py the plotting functions of viewgraphforge.viz

12. Tests and reproducibility

python -m pytest runs the test suite: the exponential and logarithm maps of SO(3) and SE(3), the SE(3) Jacobians against finite differences, the pose, quaternion and projection conventions, the invariants of generated graphs of every family, the exact consistency of noise-free graphs, the virtual-camera relation of symmetry-induced outliers, the two-view estimator, the npz and g2o round trips, determinism, the solvers and metrics on clean and corrupted graphs, and the commands (parallel generation, resume, lock, scheduler, validation, export, dataset card, clean).

Every graph has a 32-bit seed, SeedSequence(dataset_seed, spawn_key=key).generate_state(1, uint32)[0] with key = (crc32(s), k) for graph k of a wild split s and key = (crc32(s), c, r) for case c and repetition r of a sweep or grid split (column seed of index.csv), whatever the number of workers and the order of execution. The graph is generated with numpy.random.default_rng(seed); the settings of a wild graph are drawn before, with default_rng(SeedSequence(seed, spawn_key=(7,))). On the same platform and library versions a configuration therefore reproduces every array of every graph exactly; only the generation times stored in each file differ, so the file hashes of two runs differ. Elsewhere the last bits of floating-point results can differ, which can change a visibility or matching decision and then the rest of that graph. Regenerated on Linux with numpy 2.5.3 and scipy 1.18.1, 61 benchmark graphs of the published copy (made on Windows) kept their edges, match counts and labels, while 3 of 7 training graphs (two with the twoview estimator, one with more than 400 cameras) did not; with numpy 2.4.4 and scipy 1.17.1, 26 of the 61 benchmark graphs differed as well. Datasets from different machines are therefore compared with vgf validate and vgf stats rather than by file hash.

13. Troubleshooting

  • MemoryError in the log. The machine ran out of memory, often because other programs use a large share of it. Lower the budget (--max-memory-gb) or the number of workers and run the same command again; failed graphs are generated on the next run.
  • Generation is slow and the CPU is mostly idle. The machine is swapping. Check the memory figures in the log lines and lower --max-memory-gb, --workers or --recycle-gb. High CPU use with slow progress points to OMP_NUM_THREADS or MKL_NUM_THREADS set above 1 in the environment, which the workers keep when threadpoolctl is not installed.
  • another vgf generate (pid ...) is writing to .... A second vgf generate on the same folder was refused. A lock left by a crashed run is removed automatically; --force overrides the check.
  • A graph has fewer cameras than its id says. Only the largest connected component is kept; the id records the requested number, index.csv and the metadata the actual one.
  • Rows of two tables do not match. A graph id is unique within its split but can occur in several splits, so tables are joined on (split, graph_id).
  • Translation averaging fails on corridor and manhattan graphs. The directions between cameras on a straight path do not determine their spacing. The image observations do (examples/07_point_based_global_positioning.py).

14. Citation

If you use the dataset or the software, please cite the dataset:

@misc{viewgraphbench2026,
  title        = {ViewGraphBench: Physically-Grounded Synthetic View Graphs for Global Structure-from-Motion Calibration},
  author       = {Aiersilan, Aizierjiang},
  year         = {2026},
  note         = {Dataset version 1.0.0, generated with ViewGraphForge},
  howpublished = {Hugging Face Hub},
  url          = {https://huggingface.co/datasets/ezharjan/ViewGraphBench}
}

To refer to the software itself:

@software{viewgraphforge2026,
  title        = {ViewGraphForge},
  author       = {Aiersilan, Aizierjiang},
  year         = {2026},
  url          = {https://github.com/Ezharjan/ViewGraphForge}
}

CITATION.cff holds the same information in the Citation File Format.

15. References

  1. R. Hartley. In defense of the eight-point algorithm. IEEE TPAMI 19(6):580-593, 1997.
  2. D. Martinec and T. Pajdla. Robust rotation and translation estimation in multiview reconstruction. CVPR 2007.
  3. V. M. Govindu. Combining two-view constraints for motion estimation. CVPR 2001.
  4. V. M. Govindu. Lie-algebraic averaging for globally consistent motion estimation. CVPR 2004.
  5. A. Chatterjee and V. M. Govindu. Efficient and robust large-scale rotation averaging. ICCV 2013.
  6. A. Chatterjee and V. M. Govindu. Robust relative rotation averaging. IEEE TPAMI 40(4):958-972, 2018.
  7. O. Özyeşil and A. Singer. Robust camera location estimation by convex programming. CVPR 2015.
  8. C. Zach, M. Klopschitz and M. Pollefeys. Disambiguating visual relations using loop constraints. CVPR 2010.
  9. L. Carlone, R. Tron, K. Daniilidis and F. Dellaert. Initialization techniques for 3D SLAM: a survey on rotation estimation and its use in pose graph optimization. ICRA 2015.
  10. P. D. Sampson. Fitting conic sections to "very scattered" data: an iterative refinement of the Bookstein algorithm. Computer Graphics and Image Processing 18(1):97-108, 1982.
  11. S. Umeyama. Least-squares estimation of transformation parameters between two point patterns. IEEE TPAMI 13(4):376-380, 1991.
  12. T. D. Barfoot. State Estimation for Robotics. Cambridge University Press, 2017.
  13. J. Solà, J. Deray and D. Atchuthan. A micro Lie theory for state estimation in robotics. arXiv:1812.01537, 2018.
  14. R. Kümmerle, G. Grisetti, H. Strasdat, K. Konolige and W. Burgard. g2o: a general framework for graph optimization. ICRA 2011.
  15. F. Dellaert. Factor graphs and GTSAM: a hands-on introduction. Technical report GT-RIM-CP&R-2012-002, Georgia Institute of Technology, 2012.
  16. D. M. Rosen, L. Carlone, A. S. Bandeira and J. J. Leonard. SE-Sync: a certifiably correct algorithm for synchronization over the special Euclidean group. International Journal of Robotics Research 38(2-3):95-125, 2019.
  17. K. Wilson and N. Snavely. Robust global translations with 1DSfM. ECCV 2014.
  18. L. Pan, D. Baráth, M. Pollefeys and J. L. Schönberger. Global structure-from-motion revisited. ECCV 2024.

16. License

MIT License, Copyright (c) 2026 Aizierjiang Aiersilan. See LICENSE.

Release files for viewgraphforge 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for viewgraphforge 1.0.0
File Size Uploaded
viewgraphforge-1.0.0.tar.gz 228.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for viewgraphforge 1.0.0
File Interpreter ABI Platform
viewgraphforge-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 427.3 kB

Release files / viewgraphforge-1.0.0.tar.gz

Download URL viewgraphforge-1.0.0.tar.gz
Size 228.6 kB
Tags Source
SHA-256 checksum
How to use checksums
55321c5043d7c4e44d1b96789b8d853ffacef5aea8762a088790608a61843113
BLAKE2b-256 checksum
How to use checksums
6e09f1af07a3ce99c14af5d1ab18f72fa8aa7f9bc34b1f44b67c717a7aa3370e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.12

Release files / viewgraphforge-1.0.0-py3-none-any.whl

Download URL viewgraphforge-1.0.0-py3-none-any.whl
Size 198.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0d73307021c8dd0b4a7f83b787b34bcf1eae85cff889277e82c6796ba41bd1c9
BLAKE2b-256 checksum
How to use checksums
035c1c219d1c0b49f8a5d431509b97e5db1b87a2b74a9c7eb307cb1450e1bd35
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.12

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page