Spaces:
Running on Zero
Running on Zero
Download import_colmap.py from junaid-simamdigital/Simam3D-GPU: direct link, hf CLI and curl.
- Browser
- Download file 4.6 kB
-
https://huggingface.co/spaces/junaid-simamdigital/Simam3D-GPU/resolve/main/import_colmap.py
- Command line
-
hf download hf://spaces/junaid-simamdigital/Simam3D-GPU/import_colmap.py
-
curl -L -o import_colmap.py https://huggingface.co/spaces/junaid-simamdigital/Simam3D-GPU/resolve/main/import_colmap.py
4.6 kB
| """Convert COLMAP text-model cameras/images into a Simam3D capture manifest.""" | |
| from __future__ import annotations | |
| import argparse | |
| import hashlib | |
| import json | |
| from pathlib import Path | |
| import numpy as np | |
| def _records(path: Path): | |
| for raw in path.read_text(encoding="utf-8").splitlines(): | |
| line = raw.strip() | |
| if line and not line.startswith("#"): | |
| yield line.split() | |
| def _image_records(path: Path): | |
| """Yield COLMAP image headers while skipping each observation line.""" | |
| lines = path.read_text(encoding="utf-8").splitlines() | |
| index = 0 | |
| while index < len(lines): | |
| line = lines[index].strip() | |
| if not line or line.startswith("#"): | |
| index += 1 | |
| continue | |
| fields = line.split() | |
| if len(fields) < 10: | |
| raise ValueError("invalid COLMAP image record") | |
| yield fields | |
| index += 2 | |
| def _camera_matrix(fields: list[str]) -> np.ndarray: | |
| model = fields[1] | |
| width, height = int(fields[2]), int(fields[3]) | |
| params = [float(value) for value in fields[4:]] | |
| if model in {"SIMPLE_PINHOLE", "SIMPLE_RADIAL", "RADIAL"}: | |
| focal, cx, cy = params[:3] | |
| fx = fy = focal | |
| elif model in {"PINHOLE", "OPENCV", "OPENCV_FISHEYE", "FULL_OPENCV"}: | |
| fx, fy, cx, cy = params[:4] | |
| else: | |
| raise ValueError(f"unsupported COLMAP camera model: {model}") | |
| return np.array([[fx, 0.0, cx], [0.0, fy, cy], [0.0, 0.0, 1.0]], dtype=float), width, height | |
| def _quaternion_rotation(qw: float, qx: float, qy: float, qz: float) -> np.ndarray: | |
| norm = np.linalg.norm([qw, qx, qy, qz]) | |
| if norm <= 1e-12: | |
| raise ValueError("COLMAP quaternion is zero") | |
| qw, qx, qy, qz = np.asarray([qw, qx, qy, qz], dtype=float) / norm | |
| return np.array([ | |
| [1 - 2 * (qy * qy + qz * qz), 2 * (qx * qy - qz * qw), 2 * (qx * qz + qy * qw)], | |
| [2 * (qx * qy + qz * qw), 1 - 2 * (qx * qx + qz * qz), 2 * (qy * qz - qx * qw)], | |
| [2 * (qx * qz - qy * qw), 2 * (qy * qz + qx * qw), 1 - 2 * (qx * qx + qy * qy)], | |
| ]) | |
| def import_colmap(cameras_path: str | Path, images_path: str | Path, image_root: str | Path, held_out: set[str] | None = None) -> dict[str, object]: | |
| cameras = {} | |
| for fields in _records(Path(cameras_path)): | |
| matrix, width, height = _camera_matrix(fields) | |
| cameras[int(fields[0])] = (matrix, width, height) | |
| image_root = Path(image_root) | |
| held_out = held_out or set() | |
| views = [] | |
| for fields in _image_records(Path(images_path)): | |
| image_id, qw, qx, qy, qz, tx, ty, tz, camera_id = fields[:9] | |
| name = " ".join(fields[9:]) | |
| if int(camera_id) not in cameras: | |
| raise ValueError(f"image {image_id} references missing camera {camera_id}") | |
| matrix, width, height = cameras[int(camera_id)] | |
| extrinsics = np.eye(4, dtype=float) | |
| extrinsics[:3, :3] = _quaternion_rotation(float(qw), float(qx), float(qy), float(qz)) | |
| extrinsics[:3, 3] = [float(tx), float(ty), float(tz)] | |
| relative_image = Path(name) | |
| absolute_image = image_root / relative_image | |
| view = { | |
| "id": str(image_id), | |
| "image": relative_image.as_posix(), | |
| "split": "held_out" if name in held_out or str(image_id) in held_out else "train", | |
| "width": width, | |
| "height": height, | |
| "intrinsics": matrix.tolist(), | |
| "extrinsics": extrinsics.tolist(), | |
| } | |
| if absolute_image.is_file(): | |
| view["sha256"] = hashlib.sha256(absolute_image.read_bytes()).hexdigest() | |
| views.append(view) | |
| return { | |
| "project": "Simam3D", | |
| "dataset": "COLMAP text model", | |
| "coordinate_convention": "right-handed; extrinsics are world-to-camera", | |
| "views": views, | |
| } | |
| def main(argv: list[str] | None = None) -> int: | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument("cameras", type=Path) | |
| parser.add_argument("images", type=Path) | |
| parser.add_argument("image_root", type=Path) | |
| parser.add_argument("output", type=Path) | |
| parser.add_argument("--held-out", nargs="*", default=[]) | |
| args = parser.parse_args(argv) | |
| if not args.held_out: | |
| parser.error("provide at least one image name or IMAGE_ID with --held-out") | |
| manifest = import_colmap(args.cameras, args.images, args.image_root, set(args.held_out)) | |
| args.output.write_text(json.dumps(manifest, indent=2), encoding="utf-8") | |
| print(f"wrote {args.output} with {len(manifest['views'])} views") | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |