Group detection, description and matching

Vanishing points, planes and parametric primitives are detected per image and associated across images. Vanishing point and plane detectors have registries of their own, on the pages beside this one.

Detection and description

class limap.image.groups.VPDetectionOptions(skip_exists: bool = True, method: str = 'jlinkage', options: limap.image.groups.vplib.register_vp_detector.DetectorOptions = <factory>)
__init__(skip_exists: bool = True, method: str = 'jlinkage', options: DetectorOptions = <factory>) None
method: str = 'jlinkage'
options: DetectorOptions
skip_exists: bool = True
limap.image.groups.vp_detection(image_names: dict[int, Path], all_2d_lines: dict[int, list[Line2d]], output_dir: Path, options: VPDetectionOptions) dict[int, VPResult]
class limap.image.groups.PlaneDetectionOptions(weight_path: pathlib.Path = PosixPath('/local/home/shaoliu/.limap/models'), skip_exists: bool = True, method: str = 'pxwplanar', visualize: bool = True, _options: limap.image.groups.planelib.register_plane_detector.DetectorOptions = <factory>)
__init__(weight_path: Path = PosixPath('/local/home/shaoliu/.limap/models'), skip_exists: bool = True, method: str = 'pxwplanar', visualize: bool = True, _options: DetectorOptions = <factory>) None
method: str = 'pxwplanar'
property options: DetectorOptions
skip_exists: bool = True
visualize: bool = True
weight_path: Path = PosixPath('/local/home/shaoliu/.limap/models')
limap.image.groups.plane_detection(image_names: dict[int, Path], output_dir: Path, options: PlaneDetectionOptions)

Detect planar regions in images, yielding results one at a time.

This is a generator to avoid accumulating all plane masks and normal maps in memory (which can easily exceed 30 GB for large datasets).

Parameters:

output_dir – Base directory (plane_info). Outputs go to: - output_dir/seg_labels/ for segmentation masks - output_dir/normal_maps/ for normal maps - output_dir/moge_outputs/ for MoGe-specific visualizations - output_dir/viz/ for visualizations (if options.visualize)

Yields:

(img_id, plane_mask, normal_map) tuples per image

class limap.image.groups.GroupDescriptionOptions(detect_vp: bool = True, vp_detection: limap.image.groups.specs.VPDetectionOptions = <factory>, detect_plane: bool = True, plane_detection: limap.image.groups.specs.PlaneDetectionOptions = <factory>, plane_min_line_overlap_length: float = 40, plane_dilation_radius: float = 2)
__init__(detect_vp: bool = True, vp_detection: VPDetectionOptions = <factory>, detect_plane: bool = True, plane_detection: PlaneDetectionOptions = <factory>, plane_min_line_overlap_length: float = 40, plane_dilation_radius: float = 2) None
detect_plane: bool = True
detect_vp: bool = True
plane_detection: PlaneDetectionOptions
plane_dilation_radius: float = 2
plane_min_line_overlap_length: float = 40
vp_detection: VPDetectionOptions
limap.image.groups.group_description(image_names: dict[int, Path], output_dir: Path, db_path: Path, structure_db_path: Path, options: GroupDescriptionOptions, recon=None) dict[str, dict[int, int]]

Detect groups (vanishing points, planes) and import into the structure DB.

Points and lines are read from the databases per-image to avoid holding all keypoints and line detections in memory simultaneously.

Parameters:

recon – Optional pycolmap.Reconstruction. When provided, keypoints are read from recon.images[img_id].points2D instead of the COLMAP database. Use this when point detection was skipped and the database has no imported keypoints.

Returns:

start ids (for each image) corresponding to each group type

limap.image.groups.sam3_group_description(image_names: dict[int, Path], sam3_base_dir: Path, category_to_type: dict[str, GroupType], output_dir: Path, db_path: Path, structure_db_path: Path, existing_start_ids: dict | None = None, min_line_overlap_length: float = 40.0, dilation_radius: float = 2.0, recon=None) dict[str, dict[int, int]]

Load SAM3 segmentation results and import as Group2d into structure DB.

Creates groups for each GroupType found in category_to_type, associates keypoints and lines, saves label masks for dense matching, and imports the groups into the structure database.

Parameters:
  • image_names – Mapping from COLMAP image_id to image path

  • sam3_base_dir – Base directory of SAM3 output

  • category_to_type – Mapping from SAM3 category name to GroupType, e.g. {“can”: GroupType.CYLINDER, “football”: GroupType.SPHERE}

  • output_dir – Workspace directory for saving label masks. Masks go to output_dir/{TYPE}_detections/seg_labels/

  • db_path – Path to COLMAP database (for reading keypoints)

  • structure_db_path – Path to structure database

  • existing_start_ids – Existing start_ids dict to merge with (e.g. from VP/plane detection)

  • min_line_overlap_length – Minimum overlap length for line association

  • dilation_radius – Dilation radius for point/line association

  • recon – Optional pycolmap.Reconstruction for reading keypoints

Returns:

Merged start_ids dict (includes existing + new SAM3 group types)

Matching by voting

Groups that carry no descriptor can still be matched, by voting with the point and line correspondences that fall inside them.

class limap.image.groups.GroupVotingOptions
__init__(*args, **kwargs)

Overloaded function.

  1. __init__(self: limap._limap._image._groups.GroupVotingOptions) -> None

  2. __init__(self: limap._limap._image._groups.GroupVotingOptions, kwargs: dict) -> None

  3. __init__(self: limap._limap._image._groups.GroupVotingOptions, **kwargs) -> None

mergedict(self: object, kwargs: dict) None
summary(self: limap._limap._image._groups.GroupVotingOptions, write_type: bool = False) str
todict(self: limap._limap._image._groups.GroupVotingOptions, recursive: bool = True) dict
property line_weight

1.0)

Type:

(float, default

property min_num_votes
Type:

(int, default

property point_weight

1.0)

Type:

(float, default

property use_feature_weights

True)

Type:

(bool, default

limap.image.groups.match_groups_by_voting(options: limap._limap._image._groups.GroupVotingOptions, point_matches: Annotated[numpy.typing.NDArray[numpy.int32], '[m, 2]', 'flags.f_contiguous'], line_matches: Annotated[numpy.typing.NDArray[numpy.int32], '[m, 2]', 'flags.f_contiguous'], structure1: limap._limap._scene.Structure2d, structure2: limap._limap._scene.Structure2d, matched_groups1: collections.abc.Set[SupportsInt | SupportsIndex], matched_groups2: collections.abc.Set[SupportsInt | SupportsIndex]) Annotated[numpy.typing.NDArray[numpy.int32], '[m, 2]']

Match unmatched groups by voting from matched points and lines.

limap.image.group_voting.vote_unmatched_groups(options: GroupVotingOptions, neighbors: dict[int, list[int]], db_path: Path, structure_db_path: Path) None

Vote for unmatched groups using matched points and lines.

Group masks I/O

limap.image.groups.get_group_mask_filename(folder: Path, image_id: int) Path
limap.image.groups.read_group_mask(folder: Path, image_id: int) ndarray
limap.image.groups.read_group_masks(folder: Path) dict[int, ndarray]
limap.image.groups.write_group_mask(folder: Path, image_id: int, mask: ndarray) None
limap.image.groups.write_group_masks(folder: Path, masks: dict[int, ndarray])

SAM 3 utilities

limap.image.groups.load_sam3_data_for_image(sam3_base_dir: Path, categories: dict[str, GroupType], image_name: str) dict[GroupType, tuple[ndarray, list[float]]]

Load SAM3 masks and scores for a given image across all categories.

Parameters:
  • sam3_base_dir – Base directory of SAM3 output (contains category subdirs)

  • categories – Mapping from category name to GroupType e.g. {“can”: GroupType.CYLINDER, “football”: GroupType.SPHERE}

  • image_name – Image filename, e.g. “output_0001.png”

Returns:

Dict mapping GroupType to (binary_masks, scores) where binary_masks is (N_det, H, W) uint8 and scores is list[float]. Only types with at least one detection are included.

limap.image.groups.convert_sam3_masks_to_groups2d(binary_masks: ndarray, group_type: GroupType, points: ndarray, lines: list[Line2d], scores: list[float] | None = None, min_line_overlap_length: float = 40.0, dilation_radius: float = 2.0, target_size: tuple[int, int] | None = None) tuple[list[Group2d], ndarray]

Convert SAM3 binary masks to Group2d objects and a label mask.

Generalized version of convert_plane_mask_to_groups2d that works with any group type and handles per-detection binary masks with overlap priority based on scores.

Parameters:
  • binary_masks – (N_det, H, W) uint8 binary masks

  • group_type – GroupType for the created groups

  • points – (N, 2) array of 2D keypoint coordinates

  • lines – list of Line2d objects

  • scores – Optional per-detection scores for overlap priority (higher score wins the pixel). If None, earlier index wins.

  • min_line_overlap_length – minimum overlap length to associate a line

  • dilation_radius – radius for dilating regions

  • target_size – Optional (height, width) to resize masks to. Use when masks were generated at a different resolution than the working images (e.g. after –max_image_dim resizing).

Returns:

Tuple of (list[Group2d], label_mask) where label_mask is (H, W) int array with labels 1..N_det (0 = background).