Skip to main content

UPAL: Unified and Efficient Point-Line Local Features - ECCV 2026

François Costa * · Raphael Kreft * · Eckhard Goedeke · Felix Möller · Hardik Shah · Ramanathan Rajaraman · Shaohui Liu · Rémi Pautrat · Marc Pollefeys

* denotes equal contribution

Paper

UPAL inference on the boat pair
Red dots are learned keypoints, green segments are line detections supported by the learned distance field, and colored links are mutual-nearest descriptor matches.

Upal

Standalone PyTorch inference for UPAL, an ECCV 2026–accepted joint point-line detector. The repository contains the network architecture, trained weights, and runnable inference, point-matching, and line-matching examples on the classic boat1 / boat2 images. For every input image, UPAL predicts:

  • sub-pixel keypoints and their confidence scores;
  • 128-dimensional, L2-normalized local descriptors;
  • a dense keypoint/junction heatmap;
  • a dense line distance field.

The inference network uses an ALIKED-style multi-scale encoder, deformable convolutions in its deeper stages, a sparse deformable descriptor head, a point/junction scoring head, and a line-distance-field decoder. The demo uses the points-lsd detector (source bundled as a submodule), seeded from UPAL's learned keypoints, and filters its proposals with the learned distance field.

Installation

Python 3.10 or newer.

pip install upal            # network + point-seeded line detector

points-lsd is the point-seeded line segment detector used for line extraction and is installed automatically. Wheels exist for Linux, macOS and Windows (CPython 3.10-3.14). On platforms without a compatible wheel, such as musl-based distributions, install the pinned submodule from source as shown below. The separately distributed detector includes an AGPL-3.0-or-later LSD core; see its PyPI page for the complete license declaration.

To develop against this repository instead:

python3 -m venv .venv && source .venv/bin/activate
python3 -m pip install -e .

To build the detector from the bundled submodule instead:

git submodule update --init --recursive
python3 -m pip install ./third_party/points_lsd

For CUDA, install the matching PyTorch build from pytorch.org first.

Quick start

import torch
from upal import UPAL, load_model, mutual_nearest_neighbors, match_lines_from_endpoints
from upal.demo_utils import load_image  # reads a file into an RGB uint8 array and a 1 x 3 x H x W tensor

model = UPAL.from_pretrained("rkreft/upal")          # weights from the Hugging Face Hub
# model = load_model("weights/upal.tar")               # or the checkpoint in this repository

_, image0 = load_image("assets/boat1.png", max_size=640)  # paths relative to this repository
_, image1 = load_image("assets/boat2.png", max_size=640)
feats0, feats1 = model.extract(image0), model.extract(image1)

feats0["keypoints"]            # N x 2 pixel (x, y)
feats0["descriptors"]          # N x 128, L2-normalised
feats0["keypoint_scores"]      # N
feats0["keypoint_dispersity"]  # N, spread of the score peak (lower = sharper)
feats0["keypoint_heatmap"]     # H x W
feats0["line_distance_field"]  # H x W, pixels
feats0["lines"]                # L x 2 x 2 endpoints

point_matches = mutual_nearest_neighbors(feats0["descriptors"], feats1["descriptors"])
desc0, desc1 = model.describe_lines(image0, feats0["lines"]), model.describe_lines(image1, feats1["lines"])
line_matches, scores = match_lines_from_endpoints(desc0, desc1)   # NumPy: K x 2 indices, K scores

extract(image, lines=True, max_lines=200, min_line_length=25.0, max_line_distance=2.0) accepts a C x H x W (or 1 x C x H x W) tensor in [0, 1], RGB or grayscale. Padding to a multiple of 32 is handled internally and removed from every output. The keypoint budget is a model argument: UPAL.from_pretrained("rkreft/upal", max_num_keypoints=2048) or load_model(..., max_num_keypoints=2048). Calling the model directly (model(batch)) runs the network only on a B x C x H x W batch and returns batched tensors; see upal/model.py.

Run the demos

From the repository root, run inference on one image and save its feature overlay:

python demo_inference.py

Match learned point descriptors across an image pair:

python demo_match_points.py

Match field-supported line segments across the same pair:

python demo_match_lines.py

The scripts write outputs/inference.png, outputs/point_matches.png, and outputs/line_matches.png, respectively. CPU inference is supported; CUDA is selected automatically when available. The line matcher extracts descriptors at the two endpoints of each detected segment, scores both endpoint orientations, and solves a one-to-one line assignment.

Useful options:

python demo_match_points.py \
  --image0 path/to/first.jpg \
  --image1 path/to/second.jpg \
  --device cuda \
  --max-size 800 \
  --max-keypoints 1500 \
  --output outputs/my_pair.png

Hugging Face Hub

The release workflow publishes the weights as rkreft/upal (model.safetensors + config.json, loaded through PyTorchModelHubMixin). To generate the Hub folder from weights/upal.tar and verify it reproduces the checkpoint:

python scripts/push_to_hub.py            # writes outputs/hub/upal and checks the round trip
python scripts/push_to_hub.py --push --private  # upload while the Hub repository is private

Uploads verify the repository visibility before writing. The production release workflow expects rkreft/upal to be public, so make the Hub repository public before pushing the release tag.

BibTeX

@misc{costa2026unifiedefficientpointlinelocal,
      title={Unified and Efficient Point-Line Local Features}, 
      author={François Costa and Raphael Kreft and Eckhard Goedeke and Felix Möller and Hardik Shah and Ramanathan Rajaraman and Shaohui Liu and Rémi Pautrat and Marc Pollefeys},
      year={2026},
      eprint={2608.19894},
      archivePrefix={arXiv},
      primaryClass={cs.CV},
      url={https://arxiv.org/abs/2608.19894}, 
}

Acknowledgments

Parts of this codebase reuse code from ALIKED and glue-factory; we thank their authors for making it available. We also thank the authors of SuperPoint, ALIKED, DaD, and DeepLSD for releasing their pre-trained models, which we use as teachers.

Metadata

Release files for upal 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for upal 0.1.0
File Size Uploaded Provenance Actions
upal-0.1.0.tar.gz 22.1 kB With provenance Details for upal-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for upal 0.1.0
File Interpreter ABI Platform Actions
upal-0.1.0-py3-none-any.whl Python 3 none any Details for upal-0.1.0-py3-none-any.whl

Total release size: 40.9 kB

Release files / upal-0.1.0.tar.gz

Download URL upal-0.1.0.tar.gz
Size 22.1 kB
Tags Source
SHA-256 checksum
How to use checksums
96a8924db8f881701f2a3c9aababcd4cb1a129c9b2b2ffeab5c35df7033dbad8
BLAKE2b-256 checksum
How to use checksums
42ccf2f007cf155a2eee1658770e01a4612beb4123af4618b79cca0ac2d5b883
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.

Transparency log

Release files / upal-0.1.0-py3-none-any.whl

Download URL upal-0.1.0-py3-none-any.whl
Size 18.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
20681b99998f1344132922075e3d5646f8b95d4c6becc5925093ed1f28f8a114
BLAKE2b-256 checksum
How to use checksums
863de5c63fb35fa991b88d37daca5d1e47ac9d8dfe7116d36d8b061f64cb47e6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.

Transparency log

Security for upal 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for upal-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page