File size: 2,700 Bytes
f01174a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
"""
Module: VSLAM-LAB - Baselines - colmap - create_colmap_mask_dir.py
- Author: Alejandro Fontan Villacampa
- Assisted by: Claude (Fable 5)
- Version: 1.0
- Created: 2026-08-28
- Updated: 2026-08-28
- License: GPLv3 License

Turns the path_mask_<i> column of an rgb_exp.csv (written by the run pipeline for
'segmentation: mask2former', 'refraction: refrax', or datasets that ship masks; 1 = usable pixel,
0 = masked out, which is COLMAP's own convention: no features where the mask is 0) into what
colmap feature_extractor accepts, and prints one line the calling shell script evals:
  camera_mask:<png>   every frame shares one mask (refrax's mask.png) -> --ImageReader.camera_mask_path
  mask_dir:<dir>      per-frame masks -> --ImageReader.mask_path: <dir>/<image name>.png symlinks,
                      one per frame (COLMAP looks masks up by image name + '.png')
  none                the csv has no mask column for this camera (or a mask file is missing)
"""

import argparse
import os
from pathlib import Path

import pandas as pd


def create_colmap_mask_dir(rgb_csv: str, camera_name: str, sequence_path: str, mask_dir: str) -> str:
    df = pd.read_csv(rgb_csv)
    cam_idx = camera_name.rsplit("_", 1)[-1]
    mask_col, path_col = f"path_mask_{cam_idx}", f"path_{camera_name}"
    if mask_col not in df.columns:
        print(f"    no '{mask_col}' column in {os.path.basename(rgb_csv)}; extracting features without masks")
        return "none"

    masks = [Path(sequence_path) / p for p in df[mask_col]]
    missing = [m for m in masks if not m.exists()]
    if missing:
        print(f"    {len(missing)}/{len(masks)} mask files missing (e.g. {missing[0]}); extracting features without masks")
        return "none"

    if len(set(masks)) == 1:
        return f"camera_mask:{masks[0].resolve()}"

    mask_dir = Path(mask_dir)
    mask_dir.mkdir(parents=True, exist_ok=True)
    for image, mask in zip(df[path_col], masks):
        link = mask_dir / f"{os.path.basename(image)}.png"
        if link.is_symlink() or link.exists():
            link.unlink()
        os.symlink(mask.resolve(), link)
    return f"mask_dir:{mask_dir.resolve()}"


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("rgb_csv", help="Path to the experiment's rgb csv")
    parser.add_argument("camera_name", help="camera_name (e.g. rgb_0)")
    parser.add_argument("sequence_path", help="Sequence folder the csv paths are relative to")
    parser.add_argument("mask_dir", help="Where to build the per-frame mask directory if needed")
    args = parser.parse_args()
    print(create_colmap_mask_dir(args.rgb_csv, args.camera_name, args.sequence_path, args.mask_dir))