When digitizing a complex industrial object—like a turbine or an architectural monument—traditional 3D scanning often faces budget and logistics constraints. We solve this with photogrammetry: reconstructing precise three-dimensional geometry from a series of ordinary photos. The output is a dense point cloud, polygonal mesh, and textured 3D model ready for CAD, VR, or rendering. Budget savings compared to laser scanning can reach 70% (for example, on a digital twin of a workshop, we saved a substantial sum). Over 5+ years, we've delivered 20+ such projects for industry, construction, and game dev. For instance, for a production hall digital twin, we processed 1500 drone shots in 8 hours, achieving 1.5 mm accuracy.
Problems we solve
Low quality source images. Clients often submit smartphone photos with uneven lighting and insufficient overlap, causing gaps in the point cloud and geometry distortions. We compensate with algorithmic filtering and adaptive SfM parameter tuning.
Unstable lighting and reflections. Glossy and mirror surfaces are a classic photogrammetry challenge. We use cross-polarization or multi-exposure during capture, and neural network-based reflection masking in processing.
Huge data volumes. For large objects (workshops, buildings), photo counts reach 2000–3000. Unoptimized processing takes days. We implement distributed computing (Ray, Dask) and point cloud quantization, reducing time to hours.
Why photogrammetry beats laser scanning?
Laser scanners are more accurate (down to 0.1 mm) but expensive and require site access. Photogrammetry delivers comparable accuracy (0.5–3 mm) for most tasks, with equipment costs 10x lower, and can be done with drones. For digital twins of buildings and monuments, it's the optimal choice.
How to set up an SfM pipeline?
Photogrammetry pipeline consists of two stages: SfM and MVS. Let's go through configuration using COLMAP and OpenMVS as an example.
Step 1: Feature extraction and matching Use SuperPoint instead of SIFT—it performs better on smooth surfaces:
import pycolmap from pathlib import Path def run_sfm_reconstruction(images_dir: str, output_dir: str) -> dict: """Full SfM pipeline via COLMAP""" image_path = Path(images_dir) output_path = Path(output_dir) output_path.mkdir(exist_ok=True) database_path = output_path / 'database.db' # Feature extraction pycolmap.extract_features(database_path, image_path, sift_options={'max_num_features': 8192}) # Feature matching pycolmap.match_exhaustive(database_path) # Incremental reconstruction maps = pycolmap.incremental_mapping( database_path=database_path, image_path=image_path, output_path=output_path ) reconstruction = maps[0] # largest map return { 'num_cameras': len(reconstruction.cameras), 'num_images': len(reconstruction.images), 'num_3d_points': len(reconstruction.points3D) } Step 2: Dense reconstruction OpenMVS strikes a balance between speed and quality:
import subprocess def run_dense_reconstruction(sfm_output: str, output_dir: str): """Dense reconstruction via OpenMVS""" subprocess.run([ 'InterfaceCOLMAP', '-i', sfm_output, '-o', f'{output_dir}/scene.mvs' ], check=True) subprocess.run([ 'DensifyPointCloud', '-i', f'{output_dir}/scene.mvs', '--resolution-level', '1', '--number-views', '5' ], check=True) subprocess.run([ 'ReconstructMesh', '-i', f'{output_dir}/scene_dense.mvs', '--quality', '3' ], check=True) subprocess.run([ 'TextureMesh', '-i', f'{output_dir}/scene_dense_mesh.mvs' ], check=True) Step 3: Post-processing in Open3D
import open3d as o3d import numpy as np def process_point_cloud(pcd_path: str) -> dict: pcd = o3d.io.read_point_cloud(pcd_path) pcd_clean, _ = pcd.remove_statistical_outlier(nb_neighbors=20, std_ratio=2.0) pcd_clean.estimate_normals( o3d.geometry.KDTreeSearchParamHybrid(radius=0.05, max_nn=30) ) mesh, densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson( pcd_clean, depth=9 ) density_threshold = np.quantile(densities, 0.05) mesh = mesh.select_by_index(np.where(densities > density_threshold)[0]) return { 'num_vertices': len(mesh.vertices), 'num_triangles': len(mesh.triangles), 'bounding_box': mesh.get_axis_aligned_bounding_box() } What affects reconstruction accuracy?
Accuracy depends on three factors: image quality, overlap, and algorithms. Even with perfect photos, COLMAP yields 1–3 mm, Reality Capture 0.5–2 mm. To push accuracy to 0.1 mm we use laser scanning, but photogrammetry suffices for 95% of tasks.
Which pipeline to choose: COLMAP or Reality Capture?
On a tight budget, we use open-source COLMAP + OpenMVS—free, flexible, but requires programming. For commercial projects with tight deadlines, Reality Capture delivers the same accuracy 2x faster. Many clients prefer a hybrid: COLMAP for preprocessing, Reality Capture for final assembly.
Photo requirements
Reconstruction quality depends directly on input photos:
| Parameter | Recommendation |
|---|---|
| Frame overlap | 70–80% |
| Minimum number of photos | 30–50 |
| Optimal number | 100–300 |
| Angle between shots | 15–30° |
| Lighting | Even, no harsh shadows |
| Resolution | 12 MP+ |
| Depth of field | Maximum (f/8–f/16) |
Accuracy and processing time
| Method | Accuracy (mm) | Time (100 photos, CPU) |
|---|---|---|
| COLMAP + OpenMVS | 1–3 | 30–120 min |
| Meshroom (AliceVision) | 1–5 | 45–90 min |
| Reality Capture | 0.5–2 | 15–30 min |
| Metashape (Agisoft) | 0.5–2 | 20–60 min |
What's included in the work
- Audit of source photos and shooting consultation.
- Pipeline design tailored to your task.
- SfM + MVS implementation with calibration and filtering.
- Mesh generation, texturing, export to required formats (PLY, OBJ, FBX, GLTF).
- Documentation for result reproducibility.
- Training your team on pipeline usage.
- 3 months of technical support.
Estimated timelines
| Application | Project duration |
|---|---|
| Photo session processing pipeline | 3–5 weeks |
| Automated photo studio with robot | 8–14 weeks |
| Inspection/documentation system | 6–10 weeks |
Exact cost is determined after auditing your data and requirements. Get in touch with us—we'll evaluate the project and propose the optimal solution.







