# 1) Create venv and install GPU PyTorch
python -m venv venv && source venv/bin/activate
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121
# 2) Install deps (includes scikit-learn for DBSCAN)
pip install -r requirements/base.txt
pip install -r requirements/gpu.txt
# 3) (Optional) Resize images to 1024px for speed
python resize_images.py --max-size 1024 --input examples/original --output examples/original/resized
# 4) Run measurement (depth-only)
python main.py measure examples/original/resized/*.jpg
# 5) Calibrate post-scale with known object (W=21, H=14, D=7.8 cm)
python calibrate_depth_scale.py
# Set suggested value to configs/rtx2060_config.py โ ScaleRecoveryConfig(depth_only_calibration=...)
# 6) Re-run measurement and check output/results.json
DBSCAN outlier removal is automatically enabled when scikit-learn is installed (already in requirements). Logs will include a line about DBSCAN outliers removed when active.
# Requires NVIDIA Container Toolkit on host
docker compose -f docker-compose.gpu.yml up --build
# Then POST images to http://localhost:8000/measure
Update (Oct 2025): RTX 2060 optimization and Depth-Only scaling mode enabled by default. See Quick Start and Scale Recovery sections.
Update (Feb 2026): Accuracy Stage 11 complete. Added confidence-aware depth/SfM fusion, report-driven auto-tuning, API OOM circuit breaker, and final go/no-go acceptance gate.
Update (Feb 2026): Depth-Only Accuracy Enhancement Complete. Major accuracy improvements via multi-model architecture, uncertainty estimation, and geometric priors. Targets: Dimension MAPE <2-3%, Volume MAPE <5%.
This repository is currently optimized for image-only depth measurement (no marker/IMU dependency required for core workflow).
Core Pipeline:
ScaleRecoveryConfig (marker_weight=0, depth_weight=1).Accuracy Enhancement (NEW):
Production Features:
warn / skip / fail)benchmark_depth_only_accuracy.py - Accuracy benchmarking against ground truthanalyze_capture_quality.py - Image quality analysiscompare_accuracy_reports.py - Regression comparison between runsauto_tune_accuracy_config.py - Report-driven config optimizationsoak_test_api.py - Long-running API stability testfinal_acceptance_gate.py - Go/no-go deployment gatevalidate_accuracy_implementation.py - Validation of accuracy enhancement modulesconfigs/enhanced_accuracy_config.py - Full accuracy features enabled (recommended)configs/rtx2060_config.py - RTX 2060 6GB optimizedconfigs/depth_only_config.py - Minimal depth-only modeconfigs/gtx1650_config.py - 4GB GPU memory constrainedauto_tune_accuracy_config.py on baseline report.compare_accuracy_reports.py.soak_test_api.py.final_acceptance_gate.py.For full staged details and command templates, use DEPTH_ONLY_ACCURACY_PLAN.md.
For LLM handoff/context transfer, use README_LLM_CONTEXT.md.
Industrial-Grade 3D Dimensional Analysis System
Combining Structure-from-Motion, Deep Learning Depth Estimation, and Multi-Source Scale Recovery
This system takes multiple 2D photographs of an object and produces accurate 3D measurements (width, height, depth, volume) in real-world units (centimeters/meters). It's designed for industrial applications like:
Unlike traditional photogrammetry systems that produce relative 3D models, our system recovers metric scale (absolute real-world dimensions) through a novel multi-source fusion approach combining:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ INPUT LAYER โ
โ Multiple Images (3-50) + Optional (IMU, Markers, Metadata) โ
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ GPU TRANSFER & PREPROCESSING โ
โ โข Pinned Memory Transfer (Non-blocking) โ
โ โข Batch Normalization to [0,1] โ
โ โข Adaptive Resizing (max 2048px, preserving aspect ratio) โ
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 3D RECONSTRUCTION (COLMAP - GPU) โ
โ โข SIFT Feature Extraction (16,384 features/image) โ
โ โข Exhaustive Feature Matching (GPU-accelerated) โ
โ โข Incremental Structure-from-Motion โ
โ โข Bundle Adjustment (Focal length + principal point + distort) โ
โ OUTPUT: Sparse 3D point cloud + Camera poses + Intrinsics โ
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ DEPTH ESTIMATION (Metric3D - GPU) โ
โ โข Vision Transformer (ViT-Large backbone) โ
โ โข Dense Pixel-wise Depth Prediction โ
โ โข Mixed Precision (FP16) for memory efficiency โ
โ โข Batched Processing (2 images at a time for 4GB GPU) โ
โ OUTPUT: Dense depth maps per image โ
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ MULTI-SOURCE SCALE RECOVERY (GPU) โ
โ Method 1: ArUco Marker Detection (40% weight) โ
โ Method 2: IMU Motion Integration (25% weight) โ
โ Method 3: Depth Map Alignment (20% weight) โ
โ Method 4: Known Object Detection (15% weight) โ
โ OUTPUT: Metric scale factor + Confidence score โ
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 3D GEOMETRY PROCESSING โ
โ โข Statistical Outlier Removal (2ฯ threshold) โ
โ โข DBSCAN Clustering (keep largest cluster) โ
โ โข PCA-based Oriented Bounding Box (OBB) โ
โ โข Scale Application: points_scaled = points ร scale_factor โ
โ OUTPUT: Scaled point cloud + OBB dimensions โ
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ MEASUREMENT & OUTPUT โ
โ โข Dimensions: Width ร Height ร Depth (cm) โ
โ โข Volume: W ร H ร D (cmยณ) โ
โ โข Error Bounds: ยฑ% based on confidence โ
โ โข Point Cloud Export: PLY format โ
โ โข JSON Results with full pipeline statistics โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
What it does: Reconstructs 3D scene structure from 2D images
Technical Details:
Why COLMAP?
Output Format:
Reconstruction3D {
points: Tensor[N, 3] # 3D points in world coordinates (arbitrary scale)
colors: Tensor[N, 3] # RGB colors (0-1)
camera_poses: List[Tensor[4,4]] # Camera extrinsics (world-to-camera)
camera_intrinsics: List[CameraIntrinsics] # fx, fy, cx, cy
point_errors: Tensor[N] # Reprojection errors (pixels)
num_observations: Tensor[N] # Track lengths
}
What it does: Predicts dense metric-scale depth from single images
Model Architecture:
Input Image (HรWร3)
โ
Vision Transformer Encoder (ViT-Large)
โโ Patch Embedding (16ร16 patches)
โโ Positional Encoding
โโ 24 Transformer Layers
โ โโ Multi-Head Self-Attention (16 heads)
โ โโ Layer Normalization
โ โโ MLP (4096 โ 1024)
โโ Feature Maps [384 channels]
โ
Dense Prediction Transformer (DPT) Decoder
โโ Multi-scale Feature Fusion
โโ Convolutional Refinement
โโ Upsampling to Full Resolution
โ
Output: Dense Depth Map (HรWร1)
Technical Specifications:
Why Metric3D?
Depth Processing Pipeline:
# 1. Preprocessing
image = F.interpolate(image, size=(518, 518), mode='bilinear')
image = (image - mean) / std # ImageNet normalization
# 2. Inference (Mixed Precision)
with torch.cuda.amp.autocast():
depth = model(image) # [B, 1, H, W]
# 3. Post-processing
depth = F.interpolate(depth, size=original_size)
depth = depth * scale_factor # Convert to meters
depth = torch.clamp(depth, min_depth=0.1, max_depth=100.0)
Model Registry Pattern: Supports multiple depth estimation models with unified interface.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ MODEL REGISTRY โ
โ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โ
โ โ DPT-Large โ โ Depth Pro โ โDepth Anythingโ โ MiDaS โ
โ โ (Default) โ โ (Apple) โ โ V2 โ โ v3.1 โ
โ โโโโโโโโฌโโโโโโโโ โโโโโโโโฌโโโโโโโโ โโโโโโโโฌโโโโโโโโ โโโโโโฌโโโโโโ
โ โ โ โ โ โ
โ โโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโผโโโโโโโโโโ โ
โ โ DepthModelAdapter โ โ
โ โ (Abstract Base) โ โ
โ โโโโโโโโโโโฌโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโผโโโโโโโโโโ โ
โ โ DepthOutput โ โ
โ โ depth + confidenceโ โ
โ โโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Supported Models:
| Model | Backbone | Memory | Accuracy | Speed |
|---|---|---|---|---|
| DPT-Large | ViT-Large | ~1.2GB | High | Medium |
| Depth Pro | Foundation Model | ~2.0GB | Very High | Slow |
| Depth Anything V2 | ViT-L/G | ~1.5GB | High | Fast |
| MiDaS v3.1 | Multiple | ~0.8GB | Medium | Fast |
Why Uncertainty? Knowing how confident the depth prediction is enables:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ UNCERTAINTY ESTIMATION โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โ
โ โ MC Dropout โ โ Flip Consistency โ โ
โ โ (Epistemic) โ โ (Stability) โ โ
โ โ โ โ โ โ
โ โ N forward passesโ โ depth(img) vs โ โ
โ โ with dropout โ โ flip(depth(flip))โ โ
โ โ โ variance map โ โ โ consistency โ โ
โ โโโโโโโโโโฌโโโโโโโโโโ โโโโโโโโโโฌโโโโโโโโโโ โ
โ โ โ โ
โ โโโโโโโโโโโโโฌโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโผโโโโโโโโโ โ
โ โ Uncertainty โ โ
โ โ Fusion โ โ
โ โ (weighted_avg) โ โ
โ โโโโโโโโโโฌโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโผโโโโโโโโโ โ
โ โ Combined โ โ
โ โ Uncertainty Map โ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Configuration:
UncertaintyConfig(
enable_mc_dropout=True,
mc_dropout_passes=10, # 10 forward passes
dropout_rate=0.1,
enable_flip_consistency=True,
fusion_method="weighted_average" # or "max", "learned"
)
Why Geometric Priors? For box-shaped objects, we can enforce:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ GEOMETRIC PRIORS โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โ
โ โ RANSAC Plane โ โ Rectangular โ โ
โ โ Detection โ โ Prism Fitting โ โ
โ โ โ โ โ โ
โ โ โข Detect planes โ โ โข PCA initial โ โ
โ โ โข Up to 6 faces โ โ โข Iterative โ โ
โ โ โข Inlier ratio โ โ refinement โ โ
โ โโโโโโโโโโฌโโโโโโโโโโ โโโโโโโโโโฌโโโโโโโโโโ โ
โ โ โ โ
โ โโโโโโโโโโผโโโโโโโโโโ โโโโโโโโโโผโโโโโโโโโโ โ
โ โ Box Topology โ โ Epipolar โ โ
โ โ Validation โ โ Consistency โ โ
โ โ โ โ โ โ
โ โ โข Orthogonality โ โ โข Multi-view โ โ
โ โ โข Parallelism โ โ reprojection โ โ
โ โ โข 6 faces check โ โ โข Depth coherenceโ โ
โ โโโโโโโโโโฌโโโโโโโโโโ โโโโโโโโโโฌโโโโโโโโโโ โ
โ โ โ โ
โ โโโโโโโโโโโโโฌโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโผโโโโโโโโโ โ
โ โ Geometric โ โ
โ โ Validator โ โ
โ โ โ โ
โ โ โ refined_bbox โ โ
โ โ โ fit_score โ โ
โ โ โ corrections โ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Configuration:
GeometricPriorsConfig(
enable_prism_fitting=True,
prism_fitting_iterations=100,
enable_plane_detection=True,
ransac_iterations=1000,
ransac_threshold=0.01,
enable_box_topology=True,
orthogonality_tolerance_degrees=5.0,
enable_epipolar_check=True,
epipolar_threshold=2.0
)
Default: Depth-only scaling is enabled out-of-the-box (no markers/IMU/object required). To use markers later, see the notes below.
The Core Challenge: Both COLMAP and Metric3D produce reconstructions in arbitrary scale. We must recover the metric scale to get real-world measurements.
Concept: Detect fiducial markers with known physical size
Process:
Marker Detection:
# Try 4 different ArUco dictionaries
- DICT_4X4_50
- DICT_5X5_50
- DICT_6X6_250
- DICT_ARUCO_ORIGINAL
# Sub-pixel corner refinement
cornerRefinementMethod = CORNER_REFINE_SUBPIX
cornerRefinementWinSize = 5
cornerRefinementMaxIterations = 30
Scale Calculation:
# Measure marker size in pixels
perimeter_pixels = sum(edge_lengths)
marker_size_pixels = perimeter_pixels / 4
# Known real-world size (e.g., 100mm)
marker_size_mm = 100.0
# Scale factor: mm/pixel
scale = marker_size_mm / marker_size_pixels
Multi-marker Fusion:
# Remove outliers (>2ฯ from median)
scales = [s for s in scales if abs(s - median(scales)) < 2*std(scales)]
# Weighted average by detection confidence
final_scale = np.average(scales, weights=confidences)
Advantages:
Limitations:
Concept: Integrate accelerometer data to get real camera translation, compare with COLMAP camera motion
Process:
IMU Integration:
total_motion_real = 0
for i in range(1, len(imu_data)):
dt = (imu_data[i].timestamp - imu_data[i-1].timestamp) / 1000.0
accel = imu_data[i].accelerometer - gravity_vector
displacement = 0.5 * norm(accel) * dtยฒ
total_motion_real += displacement
COLMAP Motion:
total_motion_colmap = 0
for i in range(1, len(camera_poses)):
translation = norm(camera_poses[i][:3, 3] - camera_poses[i-1][:3, 3])
total_motion_colmap += translation
Scale Estimation:
scale = total_motion_real / total_motion_colmap
# Confidence based on motion magnitude
if motion_real < 0.05m: confidence = 0.3 # Too little motion
elif motion_real > 5.0m: confidence = 0.5 # Too much motion
else: confidence = 0.8 # Good motion range
Advantages:
Limitations:
Concept: Align Metric3D absolute depth with COLMAP relative depth
Process:
Compute Median Depths:
# From Metric3D (absolute scale)
median_depth_metric3d = torch.median(depth_maps[depth_maps > 0])
# From COLMAP (arbitrary scale)
distances = norm(points_3d, axis=1) # Distance from origin
median_distance_colmap = np.median(distances)
Scale Calculation:
scale = median_depth_metric3d / median_distance_colmap
Confidence Estimation:
depth_std = torch.std(depth_maps)
consistency = 1.0 - min(depth_std / median_depth, 1.0)
confidence = consistency * depth_weight
Advantages:
Limitations:
If you have printed ArUco/AprilTag markers and want higher accuracy:
src/core/config.py, set:marker_weight=0.6; depth_weight=0.2; imu_weight=0.15; object_weight=0.05 # must sum to 1.0
marker_types=["aruco", "apriltag"]
marker_size_mm=<YOUR_MARKER_SIZE_MM>
min_confidence=0.3
min_methods_required=1
Concept: Detect objects with known dimensions (doors, A4 paper, credit cards)
Process (Placeholder - future implementation):
# 1. Object Detection (YOLO/Faster R-CNN)
objects = detect_objects(image)
# 2. Match with known database
for obj in objects:
if obj.class_name in KNOWN_OBJECTS:
known_size = KNOWN_OBJECTS[obj.class_name]['size']
detected_size = obj.bbox_height_pixels
scale = known_size / detected_size
Advantages:
Limitations:
Weighted Optimization:
def optimize_scale(estimates: List[ScaleEstimate]) -> float:
"""
Fuses multiple scale estimates using weighted averaging.
Weights are pre-defined based on method reliability:
- Marker: 40%
- IMU: 25%
- Depth: 20%
- Object: 15%
"""
# Filter by minimum confidence threshold (0.5)
valid = [e for e in estimates if e.confidence >= 0.5]
# Normalize weights to sum to 1.0
weights = np.array([e.confidence for e in valid])
weights = weights / weights.sum()
# Weighted average
scales = np.array([e.scale_factor for e in valid])
final_scale = np.average(scales, weights=weights)
# Disagreement penalty
scale_std = np.std(scales)
disagreement = min(scale_std / final_scale, 0.5)
final_confidence = np.sum(weights * [e.confidence for e in valid])
final_confidence *= (1.0 - disagreement)
return final_scale, final_confidence
def measure(images: List[np.ndarray]) -> MeasurementResult:
"""
Complete measurement pipeline with detailed steps.
"""
# ========== STEP 1: GPU TRANSFER ==========
# Transfer images to GPU using pinned memory for speed
images_np = np.stack(images) # [N, H, W, 3]
images_pinned = torch.from_numpy(images_np).pin_memory()
images_gpu = images_pinned.to('cuda:0', non_blocking=True)
# Time: ~50ms for 24 images
# ========== STEP 2: 3D RECONSTRUCTION ==========
# COLMAP: Structure-from-Motion
reconstruction = colmap_reconstructor.reconstruct(images_gpu)
# Sub-steps:
# 2.1 Feature Extraction: ~20s (GPU)
# 2.2 Feature Matching: ~30s (GPU)
# 2.3 Sparse Reconstruction: ~45s (CPU + GPU)
# 2.4 Bundle Adjustment: ~20s (CPU)
# Total: ~115s
# Output:
# - points_3d: [707, 3] sparse point cloud (arbitrary scale)
# - camera_poses: [24, 4, 4] camera positions
# ========== STEP 3: MEMORY MANAGEMENT ==========
# Critical: Free GPU memory before Metric3D (4GB constraint)
images_cpu = images_gpu.cpu()
del images_gpu
torch.cuda.empty_cache()
# Memory freed: ~2GB
# ========== STEP 4: DEPTH ESTIMATION ==========
# Metric3D: Dense depth prediction
images_gpu = images_cpu.to('cuda:0', non_blocking=True)
depth_maps = []
# Process in batches of 2 to avoid OOM
for i in range(0, len(images_gpu), 2):
batch = images_gpu[i:i+2]
with torch.cuda.amp.autocast():
depths = metric3d_model(batch)
depth_maps.extend(depths)
# Clear cache after each batch
torch.cuda.empty_cache()
# Time: ~12s for 24 images
# Output: [24, H, W] dense depth maps in meters
# ========== STEP 5: SCALE RECOVERY ==========
scale_result = scale_optimizer.recover_scale(
images=images_gpu,
reconstruction={'points': reconstruction.points,
'camera_poses': reconstruction.camera_poses},
depth_maps=torch.stack(depth_maps),
imu_data=imu_data,
metadata=metadata
)
# Sub-steps:
# 5.1 Marker Detection: ~2s
# 5.2 IMU Integration: ~0.1s
# 5.3 Depth Alignment: ~0.5s
# 5.4 Scale Fusion: ~0.01s
# Total: ~2.6s
# Output: scale_factor (e.g., 0.0253 m/unit), confidence (0-1)
# ========== STEP 6: SCALE APPLICATION ==========
# Apply scale to arbitrary reconstruction
points_scaled = reconstruction.points * scale_result.scale_factor
# Points now in METERS
# ========== STEP 7: OUTLIER REMOVAL ==========
# Statistical outlier removal (2ฯ)
centroid = points_scaled.mean(axis=0)
distances = np.linalg.norm(points_scaled - centroid, axis=1)
threshold = distances.mean() + 2 * distances.std()
points_filtered = points_scaled[distances < threshold]
# Removed: ~15% outliers
# DBSCAN clustering (keep largest cluster)
from sklearn.cluster import DBSCAN
clustering = DBSCAN(eps=0.05, min_samples=10).fit(points_filtered)
largest_cluster = np.argmax(np.bincount(clustering.labels_[clustering.labels_ >= 0]))
points_clean = points_filtered[clustering.labels_ == largest_cluster]
# Removed: ~10% more outliers
# Final: ~530 clean points
# ========== STEP 8: ORIENTED BOUNDING BOX ==========
# PCA-based oriented bounding box for accurate dimensions
mean = points_clean.mean(axis=0)
centered = points_clean - mean
# Compute principal components
cov = np.cov(centered.T)
eigenvalues, eigenvectors = np.linalg.eig(cov)
# Sort by variance (largest first)
idx = eigenvalues.argsort()[::-1]
eigenvectors = eigenvectors[:, idx]
# Project to principal axes
transformed = centered @ eigenvectors
# Get dimensions along principal axes
mins = transformed.min(axis=0)
maxs = transformed.max(axis=0)
dimensions = maxs - mins
# Sort: width >= height >= depth
width, height, depth = sorted(dimensions, reverse=True)
# Convert to centimeters
width_cm = width * 100
height_cm = height * 100
depth_cm = depth * 100
volume_cm3 = width_cm * height_cm * depth_cm
# ========== STEP 9: ERROR ESTIMATION ==========
# Estimate measurement uncertainty
if scale_result.confidence > 0.9:
error_percent = 2.0 # ยฑ2%
elif scale_result.confidence > 0.7:
error_percent = 5.0 # ยฑ5%
elif scale_result.confidence > 0.5:
error_percent = 10.0 # ยฑ10%
else:
error_percent = 25.0 # ยฑ25%
error_bounds = {
'width_error': width_cm * (error_percent / 100),
'height_error': height_cm * (error_percent / 100),
'depth_error': depth_cm * (error_percent / 100),
'relative_error_percent': error_percent
}
# ========== STEP 10: OUTPUT GENERATION ==========
measurements = {
'width': width_cm,
'height': height_cm,
'depth': depth_cm,
'volume_cm3': volume_cm3,
'surface_area_cm2': 2*(width_cm*height_cm + height_cm*depth_cm + depth_cm*width_cm),
'center_x': mean[0] * 100,
'center_y': mean[1] * 100,
'center_z': mean[2] * 100,
'num_points': len(points_clean)
}
# Save point cloud (PLY format)
save_pointcloud(points_clean, colors, 'output/pointcloud.ply')
return MeasurementResult(
measurements=measurements,
confidence=scale_result.confidence,
error_bounds=error_bounds,
scale_result=scale_result,
reconstruction=reconstruction,
total_time=total_time,
gpu_time=gpu_time
)
Challenge: GTX 1650 has only 4GB VRAM, but Metric3D needs ~2GB per image
Solution: Intelligent memory lifecycle management
# PATTERN 1: Staged Processing
# Run COLMAP first (uses 2GB)
reconstruction = colmap_reconstructor.reconstruct(images_gpu)
# CRITICAL: Free memory before Metric3D
torch.cuda.synchronize() # Wait for COLMAP to finish
images_cpu = images_gpu.cpu() # Move to CPU
del images_gpu # Delete GPU reference
torch.cuda.empty_cache() # Force garbage collection
# Memory freed: ~2GB
# Now run Metric3D (needs 2GB)
images_gpu = images_cpu.to('cuda:0')
depth_maps = metric3d_model(images_gpu)
# PATTERN 2: Batch Processing
# Process images in batches to stay under memory limit
batch_size = 2 # 2 images ร 1GB = 2GB
for i in range(0, len(images), batch_size):
batch = images[i:i+batch_size]
depths = model(batch)
all_depths.extend(depths)
# Clear after each batch
torch.cuda.empty_cache()
Concept: Use 16-bit floats instead of 32-bit for ~50% memory reduction
# Enable automatic mixed precision
with torch.cuda.amp.autocast():
depth = model(image) # Automatically uses FP16 where safe
# Manual precision control
image_fp16 = image.half() # Convert to FP16
output = model(image_fp16)
output_fp32 = output.float() # Convert back to FP32 for stability
Benefits:
Concept: Overlap CPU and GPU work using async execution
# Create multiple streams for parallel work
stream1 = torch.cuda.Stream()
stream2 = torch.cuda.Stream()
# Execute operations in parallel
with torch.cuda.stream(stream1):
# COLMAP feature extraction
features = extract_features(images)
with torch.cuda.stream(stream2):
# Metric3D preprocessing (can run in parallel)
images_preprocessed = preprocess(images)
# Wait for both to finish
torch.cuda.synchronize()
Concept: Use page-locked memory for faster DMA transfers
# Slow: Regular numpy array
images_np = np.stack(images)
images_gpu = torch.from_numpy(images_np).to('cuda:0')
# Transfer time: ~200ms
# Fast: Pinned memory
images_tensor = torch.from_numpy(images_np).pin_memory()
images_gpu = images_tensor.to('cuda:0', non_blocking=True)
# Transfer time: ~50ms (4x faster)
Concept: Fuse multiple operations into single kernels
# Compile model for optimized execution
model = torch.compile(model, mode='reduce-overhead')
# Benefits:
# - Operator fusion (fewer kernel launches)
# - Better memory access patterns
# - ~20% speedup on RTX GPUs
# FastAPI server with async processing
from fastapi import FastAPI, File, UploadFile
from typing import List
app = FastAPI(title="3D Measurement API")
@app.post("/measure")
async def measure_endpoint(files: List[UploadFile] = File(...)):
"""
Upload images and get 3D measurements.
Request:
- files: List of image files (JPEG/PNG)
- imu_data: Optional JSON string with IMU data
- metadata: Optional JSON string with EXIF data
Response:
{
"measurements": {
"width": 45.3,
"height": 67.8,
"depth": 23.1,
"volume_cm3": 71256.4
},
"confidence": 0.85,
"error_bounds": {
"width_error": 2.3,
"height_error": 3.4,
"depth_error": 1.8,
"relative_error_percent": 5.0
},
"processing_times": {
"total_time": 127.3,
"gpu_time": 115.2
},
"pointcloud_path": "output/pointcloud.ply"
}
"""
# Load images
images = [await load_image(file) for file in files]
# Run measurement
result = measurement_system.measure(images)
return result.to_dict()
// Flutter client for mobile upload
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart';
Future<Map<String, dynamic>> measureObject() async {
// 1. Capture images
final images = await ImagePicker().pickMultiImage();
// 2. Prepare multipart request
var request = http.MultipartRequest(
'POST',
Uri.parse('http://server:8000/measure')
);
for (var image in images) {
request.files.add(await http.MultipartFile.fromPath(
'files',
image.path
));
}
// 3. Send request
var response = await request.send();
var responseData = await response.stream.bytesToString();
// 4. Parse result
return jsonDecode(responseData);
}
| GPU | VRAM | Images | COLMAP | Metric3D | Total Time |
|---|---|---|---|---|---|
| GTX 1650 | 4GB | 24 | 95s | 15s | 115s |
| RTX 3060 | 12GB | 24 | 45s | 8s | 55s |
| RTX 3090 | 24GB | 24 | 28s | 5s | 35s |
| RTX 4090 | 24GB | 24 | 16s | 4s | 21s |
| Method | MAE (cm) | RMSE (cm) | Relative Error | Confidence |
|---|---|---|---|---|
| ArUco Markers | 0.8 | 1.2 | ยฑ1-2% | 85-95% |
| Manual Calibration | 3.5 | 5.2 | ยฑ5-10% | 70-80% |
| Depth Only (Basic) | 12.3 | 18.7 | ยฑ20-30% | 30-50% |
| Depth Only (Enhanced) | 2.5 | 4.0 | ยฑ2-5% | 70-85% |
| Metric | Target | Description |
|---|---|---|
| Dimension MAPE (W/H) | < 2% | Width and height accuracy |
| Dimension MAPE (D) | < 3% | Depth accuracy |
| Volume MAPE | < 5% | Combined volume accuracy |
| Confidence ECE | < 0.05 | Calibrated confidence scores |
| Geometric Fit Score | > 0.85 | Box topology validation |
| Component | CPU Usage | GPU Usage | RAM | VRAM |
|---|---|---|---|---|
| COLMAP | 60% | 95% | 4GB | 2GB |
| Metric3D | 10% | 100% | 2GB | 2GB |
| Scale Recovery | 30% | 40% | 1GB | 0.5GB |
| Total Peak | 60% | 100% | 8GB | 2.5GB |
# System Requirements
- OS: Windows 10/11, Linux (Ubuntu 20.04+)
- GPU: NVIDIA with CUDA Compute Capability 7.0+ (GTX 1650 or better)
- VRAM: 4GB minimum, 8GB recommended
- RAM: 8GB minimum, 16GB recommended
- Storage: 10GB free space
# Software Requirements
- Python 3.8+
- CUDA Toolkit 12.1+
- cuDNN 8.9+
# 1. Clone repository
git clone https://github.com/Ahir7/3d-measurement.git
cd 3d-measurement
# 2. Create virtual environment
python -m venv venv
source venv/bin/activate # Linux/Mac
# venv\Scripts\activate # Windows
# 3. Install PyTorch with CUDA
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121
# 4. Install dependencies (base + GPU)
pip install -r requirements/base.txt
pip install -r requirements/gpu.txt
# 5. Install COLMAP (system)
# Ubuntu:
sudo apt install colmap
# Windows:
# Download from https://github.com/colmap/colmap/releases
# 6. Verify installation
python main.py info
# 1. (Optional) Resize images for speed
python resize_images.py --max-size 1024 --input examples/original --output examples/original/resized
# 2. Depth-only measurement (no markers)
python main.py measure examples/original/resized/*.jpg
# 3. View results
cat output/results.json
# 4. Start API server
python main.py serve --port 8000
# 5. Test API
curl -X POST "http://localhost:8000/measure" \
-F "[email protected]" \
-F "[email protected]" \
-F "[email protected]"
# After a run, compute post-scale calibration using your known object
python calibrate_depth_scale.py
# Set the printed value to:
# configs/rtx2060_config.py โ ScaleRecoveryConfig(depth_only_calibration = ...)
# Keep pre-scale at 1.0:
# Metric3DConfig.depth_scale_factor = 1.0
# Re-run measurement
python main.py measure examples/original/resized/*.jpg
# Python API
from src.core.measurement_system_gpu import MeasurementSystemGPU
from src.core.config import SystemConfig
import cv2
# Initialize system
config = SystemConfig()
system = MeasurementSystemGPU(config)
# Load images
images = [cv2.imread(f"image{i}.jpg") for i in range(1, 25)]
images = [cv2.cvtColor(img, cv2.COLOR_BGR2RGB) for img in images]
# Run measurement
result = system.measure(images)
# Access results
print(f"Dimensions: {result.measurements['width']} ร "
f"{result.measurements['height']} ร "
f"{result.measurements['depth']} cm")
print(f"Volume: {result.measurements['volume_cm3']} cmยณ")
print(f"Confidence: {result.confidence:.1%}")
print(f"Error: ยฑ{result.error_bounds['relative_error_percent']:.1f}%")
# Use enhanced accuracy configuration
from configs.enhanced_accuracy_config import get_enhanced_config
# Get config with all accuracy features enabled
config = get_enhanced_config()
# Or customize specific features
config.metric3d.uncertainty.enable_mc_dropout = True
config.metric3d.uncertainty.mc_dropout_passes = 10
config.geometric_priors.enable_prism_fitting = True
config.geometric_priors.enable_plane_detection = True
# Initialize and run
system = MeasurementSystemGPU(config)
result = system.measure(images)
# Access new accuracy fields
print(f"Model used: {result.model_used}")
print(f"Geometric fit score: {result.geometric_fit_score:.2f}")
print(f"Uncertainty bounds: {result.uncertainty_bounds}")
print(f"Planes detected: {len(result.plane_detections)}")
# Access per-depth uncertainty
for depth_est in result.depth_estimations:
print(f" MC variance mean: {depth_est.mc_variance.mean():.4f}")
print(f" Flip consistency: {depth_est.flip_consistency.mean():.4f}")
DepthEstimation (with uncertainty):
@dataclass
class DepthEstimation:
depth_map: torch.Tensor # [H, W] depth in meters
confidence_map: torch.Tensor # [H, W] confidence scores
uncertainty_map: torch.Tensor # [H, W] combined uncertainty (NEW)
mc_variance: torch.Tensor # [H, W] MC Dropout variance (NEW)
flip_consistency: torch.Tensor # [H, W] flip consistency (NEW)
model_name: str # Model identifier (NEW)
scale_factor: float
processing_time: float
MeasurementResult (with geometric priors):
@dataclass
class MeasurementResult:
measurements: Dict[str, float]
confidence: float
error_bounds: Dict[str, float]
# ... existing fields ...
uncertainty_bounds: Dict[str, float] # Per-dimension uncertainty (NEW)
geometric_fit_score: float # Box fit quality (NEW)
plane_detections: List[Dict] # Detected planes (NEW)
model_used: str # Depth model identifier (NEW)
Ground Truth:
Width: 30.0 cm
Height: 20.0 cm
Depth: 15.0 cm
Measured:
Width: 29.8 ยฑ 0.6 cm
Height: 19.9 ยฑ 0.5 cm
Depth: 15.2 ยฑ 0.4 cm
Error: ยฑ2.0%
Confidence: 87%
Processing Time: 115s (GTX 1650)
Ground Truth:
Width: 90.0 cm
Height: 210.0 cm
Depth: 5.0 cm
Measured:
Width: 88.5 ยฑ 4.5 cm
Height: 205.3 ยฑ 10.5 cm
Depth: 5.3 ยฑ 0.4 cm
Error: ยฑ5.0%
Confidence: 72%
Processing Time: 108s (GTX 1650)
Insufficient Texture:
Insufficient Overlap:
Motion Blur:
Scale Ambiguity:
Extreme Lighting:
Answer:
Answer:
Answer:
Answer:
Answer: 5-stage memory management:
torch.cuda.empty_cache() after each stageAnswer:
| Feature | Our System | Matterport |
|---|---|---|
| Hardware | Any GPU | Proprietary camera ($500+) |
| Setup Time | 2 min | 10 min |
| Processing | 2 min | 5-20 min (cloud) |
| Accuracy | ยฑ2-5% | ยฑ1-2% |
| Cost | Free | $10-50 per scan |
| Offline | Yes | No (requires cloud) |
Answer: Not currently, but roadmap:
Answer:
Answer: 3-run averaging mode:
python main.py measure --num-runs 3 images/*.jpg
Answer: The accuracy enhancement system uses four pillars:
Multi-Model Architecture: Different depth models excel in different scenarios. The registry allows switching between DPT-Large, Depth Pro, Depth Anything V2, and MiDaS based on scene characteristics.
Uncertainty Estimation:
Geometric Priors: For box-shaped objects:
Epipolar Consistency: Multi-view validation ensures depth maps are consistent when reprojected across views.
Result: 15-50% accuracy improvement, dimension MAPE <2-3%
Answer: Memory budget for RTX 2060 6GB:
| Component | Memory |
|---|---|
| DPT-Large model | ~1.2 GB |
| Image batch (3 @ 518x518) | ~0.02 GB |
| MC Dropout (10 passes) | ~0.1 GB |
| Geometric processing | ~0.1 GB |
| COLMAP reconstruction | ~0.5 GB |
| Total | ~2.5 GB |
| Safety margin | ~3.5 GB |
All features fit comfortably within 6GB. For 4GB GPUs, use get_memory_constrained_config() which reduces MC Dropout passes and disables ensemble methods.
COLMAP:
Schรถnberger, J.L. and Frahm, J.M., 2016.
Structure-from-motion revisited.
In CVPR (pp. 4104-4113).
Metric3D (DPT):
Ranftl, R., Bochkovskiy, A. and Koltun, V., 2021.
Vision transformers for dense prediction.
In ICCV (pp. 12179-12188).
Vision Transformers:
Dosovitskiy, A., et al., 2021.
An image is worth 16x16 words: Transformers for image recognition at scale.
In ICLR.
SIFT Features:
Lowe, D.G., 2004.
Distinctive image features from scale-invariant keypoints.
International Journal of Computer Vision, 60(2), pp.91-110.
3D-measurement/
โโโ src/ # Core system
โ โโโ core/
โ โ โโโ measurement_system_gpu.py # Main pipeline orchestration
โ โ โโโ config.py # Configuration (incl. new dataclasses)
โ โ โโโ calibration.py # Camera intrinsics calibration
โ โโโ reconstruction/
โ โ โโโ colmap_gpu.py # COLMAP wrapper (SfM)
โ โโโ depth/
โ โ โโโ metric3d_gpu.py # Metric3D depth estimation
โ โ โโโ model_registry.py # Multi-model management (NEW)
โ โ โโโ uncertainty.py # Uncertainty estimation (NEW)
โ โ โโโ model_adapters/ # Model adapters (NEW)
โ โ โโโ dpt_adapter.py # DPT-Large wrapper
โ โ โโโ depth_pro_adapter.py # Apple Depth Pro
โ โ โโโ depth_anything_adapter.py # Depth Anything V2
โ โ โโโ midas_adapter.py # MiDaS v3.1
โ โโโ geometry/ # Geometric priors (NEW)
โ โ โโโ plane_detection.py # RANSAC plane detection
โ โ โโโ prism_fitting.py # Rectangular prism fitting
โ โ โโโ epipolar_consistency.py # Multi-view validation
โ โ โโโ geometric_validator.py # Combined validator
โ โโโ scale/
โ โ โโโ marker_detection.py # ArUco/QR/AprilTag detection
โ โ โโโ scale_optimizer.py # Multi-source scale fusion
โ โโโ utils/
โ โ โโโ geometry.py # 3D geometry utilities
โ โ โโโ capture_quality.py # Image quality assessment
โ โ โโโ auto_tuning.py # Config auto-tuning
โ โโโ data/ # Data infrastructure (NEW)
โ โ โโโ synthetic_pipeline.py # Synthetic data generation
โ โโโ training/ # Training support (NEW)
โ โ โโโ fine_tuning.py # Model fine-tuning
โ โโโ api/
โ โโโ rest_api.py # FastAPI REST endpoints
โ โโโ reliability.py # OOM circuit breaker
โโโ configs/ # Configuration presets
โ โโโ enhanced_accuracy_config.py # Full accuracy features (NEW)
โ โโโ rtx2060_config.py # RTX 2060 6GB optimized
โ โโโ depth_only_config.py # Minimal depth-only
โ โโโ gtx1650_config.py # 4GB GPU constrained
โโโ tests/ # Unit tests
โ โโโ test_model_registry.py # Model registry tests (NEW)
โ โโโ test_uncertainty.py # Uncertainty tests (NEW)
โ โโโ test_plane_detection.py # Plane detection tests (NEW)
โ โโโ test_prism_fitting.py # Prism fitting tests (NEW)
โ โโโ test_geometric_validator.py # Validator tests (NEW)
โ โโโ ... # Other tests
โโโ requirements/ # Dependencies
โ โโโ base.txt # Core dependencies
โ โโโ gpu.txt # GPU-specific (CUDA)
โ โโโ dev.txt # Development tools
โโโ main.py # CLI interface
โโโ benchmark_depth_only_accuracy.py # Accuracy benchmarking
โโโ validate_accuracy_implementation.py # Implementation validation
โโโ README.md # This file
We welcome contributions! Areas of interest:
/docs folderMIT License - See LICENSE file
This system builds upon cutting-edge research and open-source projects:
Status: โ Production-Ready | Version: 3.0.0 | Last Updated: February 2026
# 1) Create venv and install GPU PyTorch
python -m venv venv && source venv/bin/activate
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121
# 2) Install deps (includes scikit-learn for DBSCAN)
pip install -r requirements/base.txt
pip install -r requirements/gpu.txt
# 3) (Optional) Resize images to 1024px for speed
python resize_images.py --max-size 1024 --input examples/original --output examples/original/resized
# 4) Run measurement (depth-only)
python main.py measure examples/original/resized/*.jpg
# 5) Calibrate post-scale with known object (W=21, H=14, D=7.8 cm)
python calibrate_depth_scale.py
# Set suggested value to configs/rtx2060_config.py โ ScaleRecoveryConfig(depth_only_calibration=...)
# 6) Re-run measurement and check output/results.json
DBSCAN outlier removal is automatically enabled when scikit-learn is installed (already in requirements). Logs will include a line about DBSCAN outliers removed when active.
# Requires NVIDIA Container Toolkit on host
docker compose -f docker-compose.gpu.yml up --build
# Then POST images to http://localhost:8000/measure
Update (Oct 2025): RTX 2060 optimization and Depth-Only scaling mode enabled by default. See Quick Start and Scale Recovery sections.
Update (Feb 2026): Accuracy Stage 11 complete. Added confidence-aware depth/SfM fusion, report-driven auto-tuning, API OOM circuit breaker, and final go/no-go acceptance gate.
Update (Feb 2026): Depth-Only Accuracy Enhancement Complete. Major accuracy improvements via multi-model architecture, uncertainty estimation, and geometric priors. Targets: Dimension MAPE <2-3%, Volume MAPE <5%.
This repository is currently optimized for image-only depth measurement (no marker/IMU dependency required for core workflow).
Core Pipeline:
ScaleRecoveryConfig (marker_weight=0, depth_weight=1).Accuracy Enhancement (NEW):
Production Features:
warn / skip / fail)benchmark_depth_only_accuracy.py - Accuracy benchmarking against ground truthanalyze_capture_quality.py - Image quality analysiscompare_accuracy_reports.py - Regression comparison between runsauto_tune_accuracy_config.py - Report-driven config optimizationsoak_test_api.py - Long-running API stability testfinal_acceptance_gate.py - Go/no-go deployment gatevalidate_accuracy_implementation.py - Validation of accuracy enhancement modulesconfigs/enhanced_accuracy_config.py - Full accuracy features enabled (recommended)configs/rtx2060_config.py - RTX 2060 6GB optimizedconfigs/depth_only_config.py - Minimal depth-only modeconfigs/gtx1650_config.py - 4GB GPU memory constrainedauto_tune_accuracy_config.py on baseline report.compare_accuracy_reports.py.soak_test_api.py.final_acceptance_gate.py.For full staged details and command templates, use DEPTH_ONLY_ACCURACY_PLAN.md.
For LLM handoff/context transfer, use README_LLM_CONTEXT.md.
Industrial-Grade 3D Dimensional Analysis System
Combining Structure-from-Motion, Deep Learning Depth Estimation, and Multi-Source Scale Recovery
This system takes multiple 2D photographs of an object and produces accurate 3D measurements (width, height, depth, volume) in real-world units (centimeters/meters). It's designed for industrial applications like:
Unlike traditional photogrammetry systems that produce relative 3D models, our system recovers metric scale (absolute real-world dimensions) through a novel multi-source fusion approach combining:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ INPUT LAYER โ
โ Multiple Images (3-50) + Optional (IMU, Markers, Metadata) โ
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ GPU TRANSFER & PREPROCESSING โ
โ โข Pinned Memory Transfer (Non-blocking) โ
โ โข Batch Normalization to [0,1] โ
โ โข Adaptive Resizing (max 2048px, preserving aspect ratio) โ
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 3D RECONSTRUCTION (COLMAP - GPU) โ
โ โข SIFT Feature Extraction (16,384 features/image) โ
โ โข Exhaustive Feature Matching (GPU-accelerated) โ
โ โข Incremental Structure-from-Motion โ
โ โข Bundle Adjustment (Focal length + principal point + distort) โ
โ OUTPUT: Sparse 3D point cloud + Camera poses + Intrinsics โ
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ DEPTH ESTIMATION (Metric3D - GPU) โ
โ โข Vision Transformer (ViT-Large backbone) โ
โ โข Dense Pixel-wise Depth Prediction โ
โ โข Mixed Precision (FP16) for memory efficiency โ
โ โข Batched Processing (2 images at a time for 4GB GPU) โ
โ OUTPUT: Dense depth maps per image โ
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ MULTI-SOURCE SCALE RECOVERY (GPU) โ
โ Method 1: ArUco Marker Detection (40% weight) โ
โ Method 2: IMU Motion Integration (25% weight) โ
โ Method 3: Depth Map Alignment (20% weight) โ
โ Method 4: Known Object Detection (15% weight) โ
โ OUTPUT: Metric scale factor + Confidence score โ
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 3D GEOMETRY PROCESSING โ
โ โข Statistical Outlier Removal (2ฯ threshold) โ
โ โข DBSCAN Clustering (keep largest cluster) โ
โ โข PCA-based Oriented Bounding Box (OBB) โ
โ โข Scale Application: points_scaled = points ร scale_factor โ
โ OUTPUT: Scaled point cloud + OBB dimensions โ
โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ MEASUREMENT & OUTPUT โ
โ โข Dimensions: Width ร Height ร Depth (cm) โ
โ โข Volume: W ร H ร D (cmยณ) โ
โ โข Error Bounds: ยฑ% based on confidence โ
โ โข Point Cloud Export: PLY format โ
โ โข JSON Results with full pipeline statistics โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
What it does: Reconstructs 3D scene structure from 2D images
Technical Details:
Why COLMAP?
Output Format:
Reconstruction3D {
points: Tensor[N, 3] # 3D points in world coordinates (arbitrary scale)
colors: Tensor[N, 3] # RGB colors (0-1)
camera_poses: List[Tensor[4,4]] # Camera extrinsics (world-to-camera)
camera_intrinsics: List[CameraIntrinsics] # fx, fy, cx, cy
point_errors: Tensor[N] # Reprojection errors (pixels)
num_observations: Tensor[N] # Track lengths
}
What it does: Predicts dense metric-scale depth from single images
Model Architecture:
Input Image (HรWร3)
โ
Vision Transformer Encoder (ViT-Large)
โโ Patch Embedding (16ร16 patches)
โโ Positional Encoding
โโ 24 Transformer Layers
โ โโ Multi-Head Self-Attention (16 heads)
โ โโ Layer Normalization
โ โโ MLP (4096 โ 1024)
โโ Feature Maps [384 channels]
โ
Dense Prediction Transformer (DPT) Decoder
โโ Multi-scale Feature Fusion
โโ Convolutional Refinement
โโ Upsampling to Full Resolution
โ
Output: Dense Depth Map (HรWร1)
Technical Specifications:
Why Metric3D?
Depth Processing Pipeline:
# 1. Preprocessing
image = F.interpolate(image, size=(518, 518), mode='bilinear')
image = (image - mean) / std # ImageNet normalization
# 2. Inference (Mixed Precision)
with torch.cuda.amp.autocast():
depth = model(image) # [B, 1, H, W]
# 3. Post-processing
depth = F.interpolate(depth, size=original_size)
depth = depth * scale_factor # Convert to meters
depth = torch.clamp(depth, min_depth=0.1, max_depth=100.0)
Model Registry Pattern: Supports multiple depth estimation models with unified interface.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ MODEL REGISTRY โ
โ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โ
โ โ DPT-Large โ โ Depth Pro โ โDepth Anythingโ โ MiDaS โ
โ โ (Default) โ โ (Apple) โ โ V2 โ โ v3.1 โ
โ โโโโโโโโฌโโโโโโโโ โโโโโโโโฌโโโโโโโโ โโโโโโโโฌโโโโโโโโ โโโโโโฌโโโโโโ
โ โ โ โ โ โ
โ โโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโผโโโโโโโโโโ โ
โ โ DepthModelAdapter โ โ
โ โ (Abstract Base) โ โ
โ โโโโโโโโโโโฌโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโผโโโโโโโโโโ โ
โ โ DepthOutput โ โ
โ โ depth + confidenceโ โ
โ โโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Supported Models:
| Model | Backbone | Memory | Accuracy | Speed |
|---|---|---|---|---|
| DPT-Large | ViT-Large | ~1.2GB | High | Medium |
| Depth Pro | Foundation Model | ~2.0GB | Very High | Slow |
| Depth Anything V2 | ViT-L/G | ~1.5GB | High | Fast |
| MiDaS v3.1 | Multiple | ~0.8GB | Medium | Fast |
Why Uncertainty? Knowing how confident the depth prediction is enables:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ UNCERTAINTY ESTIMATION โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โ
โ โ MC Dropout โ โ Flip Consistency โ โ
โ โ (Epistemic) โ โ (Stability) โ โ
โ โ โ โ โ โ
โ โ N forward passesโ โ depth(img) vs โ โ
โ โ with dropout โ โ flip(depth(flip))โ โ
โ โ โ variance map โ โ โ consistency โ โ
โ โโโโโโโโโโฌโโโโโโโโโโ โโโโโโโโโโฌโโโโโโโโโโ โ
โ โ โ โ
โ โโโโโโโโโโโโโฌโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโผโโโโโโโโโ โ
โ โ Uncertainty โ โ
โ โ Fusion โ โ
โ โ (weighted_avg) โ โ
โ โโโโโโโโโโฌโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโผโโโโโโโโโ โ
โ โ Combined โ โ
โ โ Uncertainty Map โ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Configuration:
UncertaintyConfig(
enable_mc_dropout=True,
mc_dropout_passes=10, # 10 forward passes
dropout_rate=0.1,
enable_flip_consistency=True,
fusion_method="weighted_average" # or "max", "learned"
)
Why Geometric Priors? For box-shaped objects, we can enforce:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ GEOMETRIC PRIORS โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โ
โ โ RANSAC Plane โ โ Rectangular โ โ
โ โ Detection โ โ Prism Fitting โ โ
โ โ โ โ โ โ
โ โ โข Detect planes โ โ โข PCA initial โ โ
โ โ โข Up to 6 faces โ โ โข Iterative โ โ
โ โ โข Inlier ratio โ โ refinement โ โ
โ โโโโโโโโโโฌโโโโโโโโโโ โโโโโโโโโโฌโโโโโโโโโโ โ
โ โ โ โ
โ โโโโโโโโโโผโโโโโโโโโโ โโโโโโโโโโผโโโโโโโโโโ โ
โ โ Box Topology โ โ Epipolar โ โ
โ โ Validation โ โ Consistency โ โ
โ โ โ โ โ โ
โ โ โข Orthogonality โ โ โข Multi-view โ โ
โ โ โข Parallelism โ โ reprojection โ โ
โ โ โข 6 faces check โ โ โข Depth coherenceโ โ
โ โโโโโโโโโโฌโโโโโโโโโโ โโโโโโโโโโฌโโโโโโโโโโ โ
โ โ โ โ
โ โโโโโโโโโโโโโฌโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโผโโโโโโโโโ โ
โ โ Geometric โ โ
โ โ Validator โ โ
โ โ โ โ
โ โ โ refined_bbox โ โ
โ โ โ fit_score โ โ
โ โ โ corrections โ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Configuration:
GeometricPriorsConfig(
enable_prism_fitting=True,
prism_fitting_iterations=100,
enable_plane_detection=True,
ransac_iterations=1000,
ransac_threshold=0.01,
enable_box_topology=True,
orthogonality_tolerance_degrees=5.0,
enable_epipolar_check=True,
epipolar_threshold=2.0
)
Default: Depth-only scaling is enabled out-of-the-box (no markers/IMU/object required). To use markers later, see the notes below.
The Core Challenge: Both COLMAP and Metric3D produce reconstructions in arbitrary scale. We must recover the metric scale to get real-world measurements.
Concept: Detect fiducial markers with known physical size
Process:
Marker Detection:
# Try 4 different ArUco dictionaries
- DICT_4X4_50
- DICT_5X5_50
- DICT_6X6_250
- DICT_ARUCO_ORIGINAL
# Sub-pixel corner refinement
cornerRefinementMethod = CORNER_REFINE_SUBPIX
cornerRefinementWinSize = 5
cornerRefinementMaxIterations = 30
Scale Calculation:
# Measure marker size in pixels
perimeter_pixels = sum(edge_lengths)
marker_size_pixels = perimeter_pixels / 4
# Known real-world size (e.g., 100mm)
marker_size_mm = 100.0
# Scale factor: mm/pixel
scale = marker_size_mm / marker_size_pixels
Multi-marker Fusion:
# Remove outliers (>2ฯ from median)
scales = [s for s in scales if abs(s - median(scales)) < 2*std(scales)]
# Weighted average by detection confidence
final_scale = np.average(scales, weights=confidences)
Advantages:
Limitations:
Concept: Integrate accelerometer data to get real camera translation, compare with COLMAP camera motion
Process:
IMU Integration:
total_motion_real = 0
for i in range(1, len(imu_data)):
dt = (imu_data[i].timestamp - imu_data[i-1].timestamp) / 1000.0
accel = imu_data[i].accelerometer - gravity_vector
displacement = 0.5 * norm(accel) * dtยฒ
total_motion_real += displacement
COLMAP Motion:
total_motion_colmap = 0
for i in range(1, len(camera_poses)):
translation = norm(camera_poses[i][:3, 3] - camera_poses[i-1][:3, 3])
total_motion_colmap += translation
Scale Estimation:
scale = total_motion_real / total_motion_colmap
# Confidence based on motion magnitude
if motion_real < 0.05m: confidence = 0.3 # Too little motion
elif motion_real > 5.0m: confidence = 0.5 # Too much motion
else: confidence = 0.8 # Good motion range
Advantages:
Limitations:
Concept: Align Metric3D absolute depth with COLMAP relative depth
Process:
Compute Median Depths:
# From Metric3D (absolute scale)
median_depth_metric3d = torch.median(depth_maps[depth_maps > 0])
# From COLMAP (arbitrary scale)
distances = norm(points_3d, axis=1) # Distance from origin
median_distance_colmap = np.median(distances)
Scale Calculation:
scale = median_depth_metric3d / median_distance_colmap
Confidence Estimation:
depth_std = torch.std(depth_maps)
consistency = 1.0 - min(depth_std / median_depth, 1.0)
confidence = consistency * depth_weight
Advantages:
Limitations:
If you have printed ArUco/AprilTag markers and want higher accuracy:
src/core/config.py, set:marker_weight=0.6; depth_weight=0.2; imu_weight=0.15; object_weight=0.05 # must sum to 1.0
marker_types=["aruco", "apriltag"]
marker_size_mm=<YOUR_MARKER_SIZE_MM>
min_confidence=0.3
min_methods_required=1
Concept: Detect objects with known dimensions (doors, A4 paper, credit cards)
Process (Placeholder - future implementation):
# 1. Object Detection (YOLO/Faster R-CNN)
objects = detect_objects(image)
# 2. Match with known database
for obj in objects:
if obj.class_name in KNOWN_OBJECTS:
known_size = KNOWN_OBJECTS[obj.class_name]['size']
detected_size = obj.bbox_height_pixels
scale = known_size / detected_size
Advantages:
Limitations:
Weighted Optimization:
def optimize_scale(estimates: List[ScaleEstimate]) -> float:
"""
Fuses multiple scale estimates using weighted averaging.
Weights are pre-defined based on method reliability:
- Marker: 40%
- IMU: 25%
- Depth: 20%
- Object: 15%
"""
# Filter by minimum confidence threshold (0.5)
valid = [e for e in estimates if e.confidence >= 0.5]
# Normalize weights to sum to 1.0
weights = np.array([e.confidence for e in valid])
weights = weights / weights.sum()
# Weighted average
scales = np.array([e.scale_factor for e in valid])
final_scale = np.average(scales, weights=weights)
# Disagreement penalty
scale_std = np.std(scales)
disagreement = min(scale_std / final_scale, 0.5)
final_confidence = np.sum(weights * [e.confidence for e in valid])
final_confidence *= (1.0 - disagreement)
return final_scale, final_confidence
def measure(images: List[np.ndarray]) -> MeasurementResult:
"""
Complete measurement pipeline with detailed steps.
"""
# ========== STEP 1: GPU TRANSFER ==========
# Transfer images to GPU using pinned memory for speed
images_np = np.stack(images) # [N, H, W, 3]
images_pinned = torch.from_numpy(images_np).pin_memory()
images_gpu = images_pinned.to('cuda:0', non_blocking=True)
# Time: ~50ms for 24 images
# ========== STEP 2: 3D RECONSTRUCTION ==========
# COLMAP: Structure-from-Motion
reconstruction = colmap_reconstructor.reconstruct(images_gpu)
# Sub-steps:
# 2.1 Feature Extraction: ~20s (GPU)
# 2.2 Feature Matching: ~30s (GPU)
# 2.3 Sparse Reconstruction: ~45s (CPU + GPU)
# 2.4 Bundle Adjustment: ~20s (CPU)
# Total: ~115s
# Output:
# - points_3d: [707, 3] sparse point cloud (arbitrary scale)
# - camera_poses: [24, 4, 4] camera positions
# ========== STEP 3: MEMORY MANAGEMENT ==========
# Critical: Free GPU memory before Metric3D (4GB constraint)
images_cpu = images_gpu.cpu()
del images_gpu
torch.cuda.empty_cache()
# Memory freed: ~2GB
# ========== STEP 4: DEPTH ESTIMATION ==========
# Metric3D: Dense depth prediction
images_gpu = images_cpu.to('cuda:0', non_blocking=True)
depth_maps = []
# Process in batches of 2 to avoid OOM
for i in range(0, len(images_gpu), 2):
batch = images_gpu[i:i+2]
with torch.cuda.amp.autocast():
depths = metric3d_model(batch)
depth_maps.extend(depths)
# Clear cache after each batch
torch.cuda.empty_cache()
# Time: ~12s for 24 images
# Output: [24, H, W] dense depth maps in meters
# ========== STEP 5: SCALE RECOVERY ==========
scale_result = scale_optimizer.recover_scale(
images=images_gpu,
reconstruction={'points': reconstruction.points,
'camera_poses': reconstruction.camera_poses},
depth_maps=torch.stack(depth_maps),
imu_data=imu_data,
metadata=metadata
)
# Sub-steps:
# 5.1 Marker Detection: ~2s
# 5.2 IMU Integration: ~0.1s
# 5.3 Depth Alignment: ~0.5s
# 5.4 Scale Fusion: ~0.01s
# Total: ~2.6s
# Output: scale_factor (e.g., 0.0253 m/unit), confidence (0-1)
# ========== STEP 6: SCALE APPLICATION ==========
# Apply scale to arbitrary reconstruction
points_scaled = reconstruction.points * scale_result.scale_factor
# Points now in METERS
# ========== STEP 7: OUTLIER REMOVAL ==========
# Statistical outlier removal (2ฯ)
centroid = points_scaled.mean(axis=0)
distances = np.linalg.norm(points_scaled - centroid, axis=1)
threshold = distances.mean() + 2 * distances.std()
points_filtered = points_scaled[distances < threshold]
# Removed: ~15% outliers
# DBSCAN clustering (keep largest cluster)
from sklearn.cluster import DBSCAN
clustering = DBSCAN(eps=0.05, min_samples=10).fit(points_filtered)
largest_cluster = np.argmax(np.bincount(clustering.labels_[clustering.labels_ >= 0]))
points_clean = points_filtered[clustering.labels_ == largest_cluster]
# Removed: ~10% more outliers
# Final: ~530 clean points
# ========== STEP 8: ORIENTED BOUNDING BOX ==========
# PCA-based oriented bounding box for accurate dimensions
mean = points_clean.mean(axis=0)
centered = points_clean - mean
# Compute principal components
cov = np.cov(centered.T)
eigenvalues, eigenvectors = np.linalg.eig(cov)
# Sort by variance (largest first)
idx = eigenvalues.argsort()[::-1]
eigenvectors = eigenvectors[:, idx]
# Project to principal axes
transformed = centered @ eigenvectors
# Get dimensions along principal axes
mins = transformed.min(axis=0)
maxs = transformed.max(axis=0)
dimensions = maxs - mins
# Sort: width >= height >= depth
width, height, depth = sorted(dimensions, reverse=True)
# Convert to centimeters
width_cm = width * 100
height_cm = height * 100
depth_cm = depth * 100
volume_cm3 = width_cm * height_cm * depth_cm
# ========== STEP 9: ERROR ESTIMATION ==========
# Estimate measurement uncertainty
if scale_result.confidence > 0.9:
error_percent = 2.0 # ยฑ2%
elif scale_result.confidence > 0.7:
error_percent = 5.0 # ยฑ5%
elif scale_result.confidence > 0.5:
error_percent = 10.0 # ยฑ10%
else:
error_percent = 25.0 # ยฑ25%
error_bounds = {
'width_error': width_cm * (error_percent / 100),
'height_error': height_cm * (error_percent / 100),
'depth_error': depth_cm * (error_percent / 100),
'relative_error_percent': error_percent
}
# ========== STEP 10: OUTPUT GENERATION ==========
measurements = {
'width': width_cm,
'height': height_cm,
'depth': depth_cm,
'volume_cm3': volume_cm3,
'surface_area_cm2': 2*(width_cm*height_cm + height_cm*depth_cm + depth_cm*width_cm),
'center_x': mean[0] * 100,
'center_y': mean[1] * 100,
'center_z': mean[2] * 100,
'num_points': len(points_clean)
}
# Save point cloud (PLY format)
save_pointcloud(points_clean, colors, 'output/pointcloud.ply')
return MeasurementResult(
measurements=measurements,
confidence=scale_result.confidence,
error_bounds=error_bounds,
scale_result=scale_result,
reconstruction=reconstruction,
total_time=total_time,
gpu_time=gpu_time
)
Challenge: GTX 1650 has only 4GB VRAM, but Metric3D needs ~2GB per image
Solution: Intelligent memory lifecycle management
# PATTERN 1: Staged Processing
# Run COLMAP first (uses 2GB)
reconstruction = colmap_reconstructor.reconstruct(images_gpu)
# CRITICAL: Free memory before Metric3D
torch.cuda.synchronize() # Wait for COLMAP to finish
images_cpu = images_gpu.cpu() # Move to CPU
del images_gpu # Delete GPU reference
torch.cuda.empty_cache() # Force garbage collection
# Memory freed: ~2GB
# Now run Metric3D (needs 2GB)
images_gpu = images_cpu.to('cuda:0')
depth_maps = metric3d_model(images_gpu)
# PATTERN 2: Batch Processing
# Process images in batches to stay under memory limit
batch_size = 2 # 2 images ร 1GB = 2GB
for i in range(0, len(images), batch_size):
batch = images[i:i+batch_size]
depths = model(batch)
all_depths.extend(depths)
# Clear after each batch
torch.cuda.empty_cache()
Concept: Use 16-bit floats instead of 32-bit for ~50% memory reduction
# Enable automatic mixed precision
with torch.cuda.amp.autocast():
depth = model(image) # Automatically uses FP16 where safe
# Manual precision control
image_fp16 = image.half() # Convert to FP16
output = model(image_fp16)
output_fp32 = output.float() # Convert back to FP32 for stability
Benefits:
Concept: Overlap CPU and GPU work using async execution
# Create multiple streams for parallel work
stream1 = torch.cuda.Stream()
stream2 = torch.cuda.Stream()
# Execute operations in parallel
with torch.cuda.stream(stream1):
# COLMAP feature extraction
features = extract_features(images)
with torch.cuda.stream(stream2):
# Metric3D preprocessing (can run in parallel)
images_preprocessed = preprocess(images)
# Wait for both to finish
torch.cuda.synchronize()
Concept: Use page-locked memory for faster DMA transfers
# Slow: Regular numpy array
images_np = np.stack(images)
images_gpu = torch.from_numpy(images_np).to('cuda:0')
# Transfer time: ~200ms
# Fast: Pinned memory
images_tensor = torch.from_numpy(images_np).pin_memory()
images_gpu = images_tensor.to('cuda:0', non_blocking=True)
# Transfer time: ~50ms (4x faster)
Concept: Fuse multiple operations into single kernels
# Compile model for optimized execution
model = torch.compile(model, mode='reduce-overhead')
# Benefits:
# - Operator fusion (fewer kernel launches)
# - Better memory access patterns
# - ~20% speedup on RTX GPUs
# FastAPI server with async processing
from fastapi import FastAPI, File, UploadFile
from typing import List
app = FastAPI(title="3D Measurement API")
@app.post("/measure")
async def measure_endpoint(files: List[UploadFile] = File(...)):
"""
Upload images and get 3D measurements.
Request:
- files: List of image files (JPEG/PNG)
- imu_data: Optional JSON string with IMU data
- metadata: Optional JSON string with EXIF data
Response:
{
"measurements": {
"width": 45.3,
"height": 67.8,
"depth": 23.1,
"volume_cm3": 71256.4
},
"confidence": 0.85,
"error_bounds": {
"width_error": 2.3,
"height_error": 3.4,
"depth_error": 1.8,
"relative_error_percent": 5.0
},
"processing_times": {
"total_time": 127.3,
"gpu_time": 115.2
},
"pointcloud_path": "output/pointcloud.ply"
}
"""
# Load images
images = [await load_image(file) for file in files]
# Run measurement
result = measurement_system.measure(images)
return result.to_dict()
// Flutter client for mobile upload
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart';
Future<Map<String, dynamic>> measureObject() async {
// 1. Capture images
final images = await ImagePicker().pickMultiImage();
// 2. Prepare multipart request
var request = http.MultipartRequest(
'POST',
Uri.parse('http://server:8000/measure')
);
for (var image in images) {
request.files.add(await http.MultipartFile.fromPath(
'files',
image.path
));
}
// 3. Send request
var response = await request.send();
var responseData = await response.stream.bytesToString();
// 4. Parse result
return jsonDecode(responseData);
}
| GPU | VRAM | Images | COLMAP | Metric3D | Total Time |
|---|---|---|---|---|---|
| GTX 1650 | 4GB | 24 | 95s | 15s | 115s |
| RTX 3060 | 12GB | 24 | 45s | 8s | 55s |
| RTX 3090 | 24GB | 24 | 28s | 5s | 35s |
| RTX 4090 | 24GB | 24 | 16s | 4s | 21s |
| Method | MAE (cm) | RMSE (cm) | Relative Error | Confidence |
|---|---|---|---|---|
| ArUco Markers | 0.8 | 1.2 | ยฑ1-2% | 85-95% |
| Manual Calibration | 3.5 | 5.2 | ยฑ5-10% | 70-80% |
| Depth Only (Basic) | 12.3 | 18.7 | ยฑ20-30% | 30-50% |
| Depth Only (Enhanced) | 2.5 | 4.0 | ยฑ2-5% | 70-85% |
| Metric | Target | Description |
|---|---|---|
| Dimension MAPE (W/H) | < 2% | Width and height accuracy |
| Dimension MAPE (D) | < 3% | Depth accuracy |
| Volume MAPE | < 5% | Combined volume accuracy |
| Confidence ECE | < 0.05 | Calibrated confidence scores |
| Geometric Fit Score | > 0.85 | Box topology validation |
| Component | CPU Usage | GPU Usage | RAM | VRAM |
|---|---|---|---|---|
| COLMAP | 60% | 95% | 4GB | 2GB |
| Metric3D | 10% | 100% | 2GB | 2GB |
| Scale Recovery | 30% | 40% | 1GB | 0.5GB |
| Total Peak | 60% | 100% | 8GB | 2.5GB |
# System Requirements
- OS: Windows 10/11, Linux (Ubuntu 20.04+)
- GPU: NVIDIA with CUDA Compute Capability 7.0+ (GTX 1650 or better)
- VRAM: 4GB minimum, 8GB recommended
- RAM: 8GB minimum, 16GB recommended
- Storage: 10GB free space
# Software Requirements
- Python 3.8+
- CUDA Toolkit 12.1+
- cuDNN 8.9+
# 1. Clone repository
git clone https://github.com/Ahir7/3d-measurement.git
cd 3d-measurement
# 2. Create virtual environment
python -m venv venv
source venv/bin/activate # Linux/Mac
# venv\Scripts\activate # Windows
# 3. Install PyTorch with CUDA
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121
# 4. Install dependencies (base + GPU)
pip install -r requirements/base.txt
pip install -r requirements/gpu.txt
# 5. Install COLMAP (system)
# Ubuntu:
sudo apt install colmap
# Windows:
# Download from https://github.com/colmap/colmap/releases
# 6. Verify installation
python main.py info
# 1. (Optional) Resize images for speed
python resize_images.py --max-size 1024 --input examples/original --output examples/original/resized
# 2. Depth-only measurement (no markers)
python main.py measure examples/original/resized/*.jpg
# 3. View results
cat output/results.json
# 4. Start API server
python main.py serve --port 8000
# 5. Test API
curl -X POST "http://localhost:8000/measure" \
-F "[email protected]" \
-F "[email protected]" \
-F "[email protected]"
# After a run, compute post-scale calibration using your known object
python calibrate_depth_scale.py
# Set the printed value to:
# configs/rtx2060_config.py โ ScaleRecoveryConfig(depth_only_calibration = ...)
# Keep pre-scale at 1.0:
# Metric3DConfig.depth_scale_factor = 1.0
# Re-run measurement
python main.py measure examples/original/resized/*.jpg
# Python API
from src.core.measurement_system_gpu import MeasurementSystemGPU
from src.core.config import SystemConfig
import cv2
# Initialize system
config = SystemConfig()
system = MeasurementSystemGPU(config)
# Load images
images = [cv2.imread(f"image{i}.jpg") for i in range(1, 25)]
images = [cv2.cvtColor(img, cv2.COLOR_BGR2RGB) for img in images]
# Run measurement
result = system.measure(images)
# Access results
print(f"Dimensions: {result.measurements['width']} ร "
f"{result.measurements['height']} ร "
f"{result.measurements['depth']} cm")
print(f"Volume: {result.measurements['volume_cm3']} cmยณ")
print(f"Confidence: {result.confidence:.1%}")
print(f"Error: ยฑ{result.error_bounds['relative_error_percent']:.1f}%")
# Use enhanced accuracy configuration
from configs.enhanced_accuracy_config import get_enhanced_config
# Get config with all accuracy features enabled
config = get_enhanced_config()
# Or customize specific features
config.metric3d.uncertainty.enable_mc_dropout = True
config.metric3d.uncertainty.mc_dropout_passes = 10
config.geometric_priors.enable_prism_fitting = True
config.geometric_priors.enable_plane_detection = True
# Initialize and run
system = MeasurementSystemGPU(config)
result = system.measure(images)
# Access new accuracy fields
print(f"Model used: {result.model_used}")
print(f"Geometric fit score: {result.geometric_fit_score:.2f}")
print(f"Uncertainty bounds: {result.uncertainty_bounds}")
print(f"Planes detected: {len(result.plane_detections)}")
# Access per-depth uncertainty
for depth_est in result.depth_estimations:
print(f" MC variance mean: {depth_est.mc_variance.mean():.4f}")
print(f" Flip consistency: {depth_est.flip_consistency.mean():.4f}")
DepthEstimation (with uncertainty):
@dataclass
class DepthEstimation:
depth_map: torch.Tensor # [H, W] depth in meters
confidence_map: torch.Tensor # [H, W] confidence scores
uncertainty_map: torch.Tensor # [H, W] combined uncertainty (NEW)
mc_variance: torch.Tensor # [H, W] MC Dropout variance (NEW)
flip_consistency: torch.Tensor # [H, W] flip consistency (NEW)
model_name: str # Model identifier (NEW)
scale_factor: float
processing_time: float
MeasurementResult (with geometric priors):
@dataclass
class MeasurementResult:
measurements: Dict[str, float]
confidence: float
error_bounds: Dict[str, float]
# ... existing fields ...
uncertainty_bounds: Dict[str, float] # Per-dimension uncertainty (NEW)
geometric_fit_score: float # Box fit quality (NEW)
plane_detections: List[Dict] # Detected planes (NEW)
model_used: str # Depth model identifier (NEW)
Ground Truth:
Width: 30.0 cm
Height: 20.0 cm
Depth: 15.0 cm
Measured:
Width: 29.8 ยฑ 0.6 cm
Height: 19.9 ยฑ 0.5 cm
Depth: 15.2 ยฑ 0.4 cm
Error: ยฑ2.0%
Confidence: 87%
Processing Time: 115s (GTX 1650)
Ground Truth:
Width: 90.0 cm
Height: 210.0 cm
Depth: 5.0 cm
Measured:
Width: 88.5 ยฑ 4.5 cm
Height: 205.3 ยฑ 10.5 cm
Depth: 5.3 ยฑ 0.4 cm
Error: ยฑ5.0%
Confidence: 72%
Processing Time: 108s (GTX 1650)
Insufficient Texture:
Insufficient Overlap:
Motion Blur:
Scale Ambiguity:
Extreme Lighting:
Answer:
Answer:
Answer:
Answer:
Answer: 5-stage memory management:
torch.cuda.empty_cache() after each stageAnswer:
| Feature | Our System | Matterport |
|---|---|---|
| Hardware | Any GPU | Proprietary camera ($500+) |
| Setup Time | 2 min | 10 min |
| Processing | 2 min | 5-20 min (cloud) |
| Accuracy | ยฑ2-5% | ยฑ1-2% |
| Cost | Free | $10-50 per scan |
| Offline | Yes | No (requires cloud) |
Answer: Not currently, but roadmap:
Answer:
Answer: 3-run averaging mode:
python main.py measure --num-runs 3 images/*.jpg
Answer: The accuracy enhancement system uses four pillars:
Multi-Model Architecture: Different depth models excel in different scenarios. The registry allows switching between DPT-Large, Depth Pro, Depth Anything V2, and MiDaS based on scene characteristics.
Uncertainty Estimation:
Geometric Priors: For box-shaped objects:
Epipolar Consistency: Multi-view validation ensures depth maps are consistent when reprojected across views.
Result: 15-50% accuracy improvement, dimension MAPE <2-3%
Answer: Memory budget for RTX 2060 6GB:
| Component | Memory |
|---|---|
| DPT-Large model | ~1.2 GB |
| Image batch (3 @ 518x518) | ~0.02 GB |
| MC Dropout (10 passes) | ~0.1 GB |
| Geometric processing | ~0.1 GB |
| COLMAP reconstruction | ~0.5 GB |
| Total | ~2.5 GB |
| Safety margin | ~3.5 GB |
All features fit comfortably within 6GB. For 4GB GPUs, use get_memory_constrained_config() which reduces MC Dropout passes and disables ensemble methods.
COLMAP:
Schรถnberger, J.L. and Frahm, J.M., 2016.
Structure-from-motion revisited.
In CVPR (pp. 4104-4113).
Metric3D (DPT):
Ranftl, R., Bochkovskiy, A. and Koltun, V., 2021.
Vision transformers for dense prediction.
In ICCV (pp. 12179-12188).
Vision Transformers:
Dosovitskiy, A., et al., 2021.
An image is worth 16x16 words: Transformers for image recognition at scale.
In ICLR.
SIFT Features:
Lowe, D.G., 2004.
Distinctive image features from scale-invariant keypoints.
International Journal of Computer Vision, 60(2), pp.91-110.
3D-measurement/
โโโ src/ # Core system
โ โโโ core/
โ โ โโโ measurement_system_gpu.py # Main pipeline orchestration
โ โ โโโ config.py # Configuration (incl. new dataclasses)
โ โ โโโ calibration.py # Camera intrinsics calibration
โ โโโ reconstruction/
โ โ โโโ colmap_gpu.py # COLMAP wrapper (SfM)
โ โโโ depth/
โ โ โโโ metric3d_gpu.py # Metric3D depth estimation
โ โ โโโ model_registry.py # Multi-model management (NEW)
โ โ โโโ uncertainty.py # Uncertainty estimation (NEW)
โ โ โโโ model_adapters/ # Model adapters (NEW)
โ โ โโโ dpt_adapter.py # DPT-Large wrapper
โ โ โโโ depth_pro_adapter.py # Apple Depth Pro
โ โ โโโ depth_anything_adapter.py # Depth Anything V2
โ โ โโโ midas_adapter.py # MiDaS v3.1
โ โโโ geometry/ # Geometric priors (NEW)
โ โ โโโ plane_detection.py # RANSAC plane detection
โ โ โโโ prism_fitting.py # Rectangular prism fitting
โ โ โโโ epipolar_consistency.py # Multi-view validation
โ โ โโโ geometric_validator.py # Combined validator
โ โโโ scale/
โ โ โโโ marker_detection.py # ArUco/QR/AprilTag detection
โ โ โโโ scale_optimizer.py # Multi-source scale fusion
โ โโโ utils/
โ โ โโโ geometry.py # 3D geometry utilities
โ โ โโโ capture_quality.py # Image quality assessment
โ โ โโโ auto_tuning.py # Config auto-tuning
โ โโโ data/ # Data infrastructure (NEW)
โ โ โโโ synthetic_pipeline.py # Synthetic data generation
โ โโโ training/ # Training support (NEW)
โ โ โโโ fine_tuning.py # Model fine-tuning
โ โโโ api/
โ โโโ rest_api.py # FastAPI REST endpoints
โ โโโ reliability.py # OOM circuit breaker
โโโ configs/ # Configuration presets
โ โโโ enhanced_accuracy_config.py # Full accuracy features (NEW)
โ โโโ rtx2060_config.py # RTX 2060 6GB optimized
โ โโโ depth_only_config.py # Minimal depth-only
โ โโโ gtx1650_config.py # 4GB GPU constrained
โโโ tests/ # Unit tests
โ โโโ test_model_registry.py # Model registry tests (NEW)
โ โโโ test_uncertainty.py # Uncertainty tests (NEW)
โ โโโ test_plane_detection.py # Plane detection tests (NEW)
โ โโโ test_prism_fitting.py # Prism fitting tests (NEW)
โ โโโ test_geometric_validator.py # Validator tests (NEW)
โ โโโ ... # Other tests
โโโ requirements/ # Dependencies
โ โโโ base.txt # Core dependencies
โ โโโ gpu.txt # GPU-specific (CUDA)
โ โโโ dev.txt # Development tools
โโโ main.py # CLI interface
โโโ benchmark_depth_only_accuracy.py # Accuracy benchmarking
โโโ validate_accuracy_implementation.py # Implementation validation
โโโ README.md # This file
We welcome contributions! Areas of interest:
/docs folderMIT License - See LICENSE file
This system builds upon cutting-edge research and open-source projects:
Status: โ Production-Ready | Version: 3.0.0 | Last Updated: February 2026