Download code/run_colmap.py from acd23/cad-3d-reconstruction: direct link, hf CLI and curl.
- Browser
- Download file 3.98 kB
-
https://huggingface.co/acd23/cad-3d-reconstruction/resolve/main/code/run_colmap.py
- Command line
-
hf download hf://acd23/cad-3d-reconstruction/code/run_colmap.py
-
curl -L -o run_colmap.py https://huggingface.co/acd23/cad-3d-reconstruction/resolve/main/code/run_colmap.py
3.98 kB
| """ | |
| Run COLMAP SfM (Structure-from-Motion) on each part's images using pycolmap. | |
| Produces sparse reconstruction (cameras, images, points3D) needed for 3DGS. | |
| If COLMAP fails (likely for textureless CAD parts with few views), we report | |
| the failure clearly so we can fall back to an alternative pose estimator. | |
| """ | |
| import os | |
| import sys | |
| import shutil | |
| import pycolmap | |
| import numpy as np | |
| from PIL import Image | |
| PARTS_DIR = "data/parts" | |
| PARTS = sorted(os.listdir(PARTS_DIR)) | |
| def run_colmap_for_part(part): | |
| input_dir = os.path.abspath(os.path.join(PARTS_DIR, part, "input")) | |
| workspace = os.path.abspath(os.path.join(PARTS_DIR, part, "colmap_ws")) | |
| db_path = os.path.join(workspace, "database.db") | |
| image_dir = os.path.join(workspace, "images") | |
| sparse_dir = os.path.join(workspace, "sparse", "0") | |
| # fresh workspace | |
| if os.path.exists(workspace): | |
| shutil.rmtree(workspace) | |
| os.makedirs(image_dir, exist_ok=True) | |
| os.makedirs(os.path.join(workspace, "sparse"), exist_ok=True) | |
| # copy images | |
| images = sorted([f for f in os.listdir(input_dir) if f.endswith(".png")]) | |
| for img in images: | |
| shutil.copy(os.path.join(input_dir, img), os.path.join(image_dir, img)) | |
| print(f"\n{'='*60}") | |
| print(f"PART: {part} ({len(images)} images)") | |
| print(f"{'='*60}") | |
| # Step 1: feature extraction | |
| print("[1/4] Feature extraction...") | |
| try: | |
| pycolmap.extract_features(database_path=db_path, image_path=image_dir) | |
| except Exception as e: | |
| print(f" FAILED: {e}") | |
| return False, str(e) | |
| # Step 2: matching | |
| print("[2/4] Feature matching (exhaustive)...") | |
| try: | |
| pycolmap.match_exhaustive(database_path=db_path) | |
| except Exception as e: | |
| print(f" FAILED: {e}") | |
| return False, str(e) | |
| # Step 3: incremental SfM | |
| print("[3/4] Incremental mapping (SfM)...") | |
| try: | |
| os.makedirs(sparse_dir, exist_ok=True) | |
| reconstruction = pycolmap.incremental_mapping( | |
| database_path=db_path, | |
| image_path=image_dir, | |
| output_path=os.path.join(workspace, "sparse"), | |
| options=pycolmap.IncrementalPipelineOptions( | |
| min_num_matches=5, | |
| min_focal_length_ratio=0.1, | |
| max_focal_length_ratio=10.0, | |
| ), | |
| ) | |
| except Exception as e: | |
| print(f" FAILED: {e}") | |
| return False, str(e) | |
| if reconstruction is None or len(reconstruction) == 0: | |
| print(" FAILED: No reconstruction produced (0 models)") | |
| return False, "No reconstruction produced" | |
| # Check how many images were registered | |
| rec = reconstruction[0] | |
| n_reg = sum(1 for im in rec.images.values() if im.has_pose) | |
| n_points = len(rec.points3D) | |
| n_cameras = len(rec.cameras) | |
| print(f"[4/4] Reconstruction: {n_reg}/{len(images)} images registered, {n_points} 3D points, {n_cameras} cameras") | |
| if n_reg < 3: | |
| print(f" WARNING: only {n_reg} images registered - insufficient for 3DGS") | |
| return False, f"Only {n_reg} images registered" | |
| # Export to text + PLY for inspection | |
| rec.write(sparse_dir) | |
| pycolmap.export_points3D( | |
| output_path=os.path.join(workspace, "sparse", "points3D.ply"), | |
| database_path=db_path, | |
| image_path=image_dir, | |
| output_type=pycolmap.OutputType.PLY, | |
| ) | |
| print(f" Saved sparse model to {sparse_dir}") | |
| return True, f"{n_reg}/{len(images)} registered, {n_points} points" | |
| def main(): | |
| results = {} | |
| for part in PARTS: | |
| input_dir = os.path.join(PARTS_DIR, part, "input") | |
| if not os.path.isdir(input_dir): | |
| continue | |
| ok, msg = run_colmap_for_part(part) | |
| results[part] = (ok, msg) | |
| print(f"\n{'='*60}") | |
| print("COLMAP SUMMARY") | |
| print(f"{'='*60}") | |
| for part, (ok, msg) in results.items(): | |
| status = "OK" if ok else "FAILED" | |
| print(f" {part}: {status} - {msg}") | |
| return results | |
| if __name__ == "__main__": | |
| main() | |