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
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)
| 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)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| 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 logRelease 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 logSecurity 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.
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 logA 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.