Matching#
Descriptor matchers return (dists, idxs): one value and one index pair per match.
Each row of idxs is (index in desc1, index in desc2).
Choose the matcher by its acceptance rule, and interpret its first output as follows:
Matcher |
Acceptance rule |
First output |
|---|---|---|
Nearest neighbour; MNN also requires the reverse match. |
Distance; lower is better. |
|
Nearest-to-second-nearest ratio test; SMNN requires mutual matches and tests both directions. |
Ratio; lower is better. SMNN returns the larger ratio. |
|
Ratio test against a geometrically inconsistent neighbour, using LAFs. |
Ratio; lower is better. |
|
Local affine consistency using LAFs. |
Inverse confidence, or a squared-distance ratio in the no-seed fallback; lower is better. |
|
Learned matching with pretrained weights for the selected feature. |
Matching score; higher is better. |
DescriptorMatcher wraps NN, MNN, SNN and SMNN as a module.
GeometryAwareDescriptorMatcher wraps FGINN and AdaLAM.
For padded batches of descriptor pairs, use match_smnn_batched(); its index rows include the batch index.
To detect, describe and match two images in one call, use LocalFeatureMatcher with a descriptor-only matcher.
LightGlue takes feature dictionaries and returns the full network outputs; OnnxLightGlue
runs an exported model. LoFTR matches images directly, without a detector.
To keep matches that agree with a geometric model, use RANSAC.
Descriptor matching#
- kornia.feature.match_nn(desc1, desc2, dm=None)[source]#
Match each descriptor in desc1 to its nearest neighbor in desc2, with no filtering.
dists, idxs = match_nn(desc1, desc2)takes two descriptor sets(B1, D)and(B2, D)and returns, for every row ofdesc1in order, the L2 distance(B1, 1)to its nearest row ofdesc2and the index pair(B1, 2)of the two rows; a precomputeddm(B1, B2)replaces the L2 distances. Every row is matched, however ambiguous its nearest neighbour, and several rows can match the same row ofdesc2. To keep only the reliable matches, usematch_mnn()(mutual nearest neighbours),match_snn()(Lowe’s ratio test) ormatch_smnn()(both) instead.- Convention:
Every
match_*function returns(dists, idxs), one row per match.idxsis an int64(M, 2)tensor of(index in desc1, index in desc2)rows;match_smnn_batched()adds the batch index as a first column.distsis(M, 1). Withoutdm, it has the descriptor dtype, except thatmatch_adalam()returns float32. Lower values are better, and their meaning depends on the function:match_nn(),match_mnn(): the distance, the entry ofdmwhen it is given.match_snn(): the ratio of the nearest to the second-nearest distance, not a distance.match_smnn(),match_smnn_batched(): the larger of the two ratios, desc1 to desc2 and back.match_fginn(): the ratio of the nearest distance to that of a second neighbour, taken among the 10 nearest, whoselafs2centre lies at leastspatial_thfrom the nearest one’s, and 0 without one; see its Convention block.match_adalam(): the inverse of the local RANSAC confidence; see its Convention block.
LightGlueMatcherreturns LightGlue’s matching score in the place ofdists, which is higher for better matches, andLocalFeatureMatcherreports1 - distsasconfidence.The ratio tests of
match_snn(),match_smnn(),match_smnn_batched()andmatch_fginn()keepratio <= th: a ratio equal tothpasses.Without
dm, the functions use L2 distances. A givendmreplaces them:match_nn()andmatch_mnn()require only that smaller entries are better, while ratio tests require non-negative distances. Useth ** 2for squared distances.Of tied nearest neighbours,
match_nn()andmatch_mnn()take the one with the lower index.An empty desc1 or desc2 gives a
(0, 1)and a(0, 2)tensor ((0, 3)formatch_smnn_batched()). The ratio tests need a second neighbour:match_snn()andmatch_fginn()return no match unless desc2 has two rows, andmatch_smnn(),match_smnn_batched()andmatch_adalam(), which scores its putative matches by the same ratio, need two rows in both.
- Parameters:
- Return type:
- Returns:
Descriptor distance of matching descriptors, shape of
(B1, 1), or(0, 1)when desc2 is empty.Long torch.Tensor indexes of matching descriptors in desc1 and desc2, shape of
(B1, 2), or(0, 2)when desc2 is empty.
See also
match_mnn(): keeps only the pairs that are each other’s nearest neighbours.match_snn(): keeps only the nearest neighbours that pass Lowe’s ratio test.DescriptorMatcher: the module form, withmatch_mode="nn".
- kornia.feature.match_mnn(desc1, desc2, dm=None)[source]#
Match the descriptors of desc1 and desc2 that are each other’s nearest neighbors.
dists, idxs = match_mnn(desc1, desc2)takes two descriptor sets(B1, D)and(B2, D)and returns the pairs whose two descriptors are each other’s nearest neighbour, the mutual nearest neighbours: their L2 distances(B3, 1)and index pairs(B3, 2), withB3 <= min(B1, B2). Each row of either set is matched at most once, but a distinctive descriptor and an ambiguous one are kept alike; to also require Lowe’s ratio test, usematch_smnn()instead.- Convention:
See the Convention block on
match_nn(). A pair is kept only when each of the two descriptors is the nearest neighbour of the other. Ties go to the lower index in both directions, so of several identical rows of desc2 only the first can be matched.
- Parameters:
- Return type:
- Returns:
Descriptor distance of matching descriptors, shape of
(B3, 1).Long torch.Tensor indexes of matching descriptors in desc1 and desc2, shape of
(B3, 2), where 0 <= B3 <= min(B1, B2)
See also
match_nn(): the nearest neighbour of every row of desc1, mutual or not.match_smnn(): mutual nearest neighbours that also pass the ratio test in both directions.match_snn(): the ratio test in one direction, without the mutual check.DescriptorMatcher: the module form, withmatch_mode="mnn".
- kornia.feature.match_snn(desc1, desc2, th=0.8, dm=None)[source]#
Match each descriptor in desc1 to its nearest neighbor in desc2 if it passes Lowe’s ratio test.
dists, idxs = match_snn(desc1, desc2, th)takes two descriptor sets(B1, D)and(B2, D), keeps the rows ofdesc1whose nearest neighbour indesc2is at mostthtimes as far as the second-nearest, and returns their ratios(B3, 1), not distances, and index pairs(B3, 2), withB3 <= B1, in the order of the rows ofdesc1. Several rows ofdesc1can match the same row ofdesc2; to keep only mutual nearest neighbours that pass the test in both directions, usematch_smnn()instead.Speed, with the same matches, on 1,024 to 8,192 RootSIFT descriptors: on an x86 CPU this is faster than faiss and OpenCV’s
BFMatcher, while faiss’s exact k-nearest-neighbour search runs this ratio test up to 2.2x faster on CUDA and up to 2.0x faster on an Apple M1 CPU. See the image matching guide.- Convention:
See the Convention block on
match_nn();distsis the ratio of the nearest to the second-nearest distance.
- Parameters:
desc1 (
Tensor) – Batch of descriptors of a shape(B1, D).desc2 (
Tensor) – Batch of descriptors of a shape(B2, D).th (
float, optional) – distance ratio threshold. Default:0.8dm (
Optional[Tensor], optional) – torch.Tensor containing the distances from each descriptor in desc1 to each descriptor in desc2, shape of(B1, B2). Default:None
- Return type:
- Returns:
Ratio of the nearest to the second-nearest distance of each match, shape of
(B3, 1).Long torch.Tensor indexes of matching descriptors in desc1 and desc2. Shape:
(B3, 2), where 0 <= B3 <= B1.
See also
match_nn(): the nearest neighbour of every row of desc1, without the ratio test.match_mnn(): mutual nearest neighbours, without the ratio test.match_smnn(): the ratio test in both directions, on mutual nearest neighbours.match_fginn(): the same ratio test, with a second neighbour that lies elsewhere in the image.DescriptorMatcher: the module form, withmatch_mode="snn".
- kornia.feature.match_smnn(desc1, desc2, th=0.95, dm=None)[source]#
Match mutual nearest neighbors that also pass Lowe’s ratio test in both directions.
dists, idxs = match_smnn(desc1, desc2, th)takes two descriptor sets(B1, D)and(B2, D), keeps the mutual nearest neighbours whose nearest-to-second-nearest distance ratio is at mostthboth fromdesc1todesc2and back, and returns the larger of their two ratios(B3, 1)and their index pairs(B3, 2), withB3 <= min(B1, B2), in the order of the rows ofdesc1. For many pairs of descriptor sets, padded to common sizes, usematch_smnn_batched()instead of a loop.Speed, with the same matches, on 1,024 to 8,192 RootSIFT descriptors: on an x86 CPU this is faster than faiss and OpenCV’s
BFMatcher, while faiss’s exact k-nearest-neighbour search, run both ways, is up to 1.6x faster on CUDA and up to 2.0x faster on an Apple M1 CPU. See the image matching guide.- Convention:
See the Convention block on
match_nn(). A pair is kept when the two descriptors are mutual nearest neighbours and pass the ratio test,ratio <= th, in both directions;distsis the larger of the two ratios.thdefaults to 0.95, wherematch_snn()andmatch_fginn()default to 0.8.
- Parameters:
desc1 (
Tensor) – Batch of descriptors of a shape(B1, D).desc2 (
Tensor) – Batch of descriptors of a shape(B2, D).th (
float, optional) – distance ratio threshold. Default:0.95dm (
Optional[Tensor], optional) – torch.Tensor containing the distances from each descriptor in desc1 to each descriptor in desc2, shape of(B1, B2). Default:None
- Return type:
- Returns:
The larger of the two distance ratios of each match, shape of
(B3, 1).Long torch.Tensor indexes of matching descriptors in desc1 and desc2, shape of
(B3, 2)where 0 <= B3 <= B1.
See also
match_snn(): the ratio test from desc1 to desc2 only, without the mutual check.match_mnn(): mutual nearest neighbours without the ratio test.match_smnn_batched(): the same matches for a batch of padded descriptor-set pairs, in one call.DescriptorMatcher: the module form, withmatch_mode="smnn"and a defaultthof 0.8.RANSAC: the next step, which keeps the matches one geometric model explains.
- kornia.feature.match_smnn_batched(desc1, desc2, th=0.95, dm=None, mask1=None, mask2=None)[source]#
Match a batch of padded descriptor-set pairs in one call, each as match_smnn matches one pair.
ratios, idxs = match_smnn_batched(desc1, desc2, th)takesBpairs of descriptor sets,(B, N, D)and(B, M, D), padded to common sizes, with optional boolean masksmask1(B, N)andmask2(B, M)of the valid rows, and returns the matches of every pair at once, without a loop over the pairs: the larger of the two ratios(K, 1)and index rows(K, 3)of(batch index, index in desc1, index in desc2), which index the padded tensors and come sorted by batch index, then index in desc1. For a single pair, usematch_smnn().- Convention:
See the Convention blocks on
match_nn()andmatch_smnn(). For each batch item it returns the pairs and ratios thatmatch_smnn()returns for finite distances between the valid descriptors of that item, with the batch index as the first column of the indices. Unlikematch_smnn(), it rejects a match when the nearest or second-nearest distance in either direction is non-finite. Withoutdm, float16 and bfloat16 descriptors give the results ofmatch_smnn()on float32 copies, with the ratios rounded back, and a few pairs and ratios can differ slightly from those ofmatch_smnn()on the half-precision descriptors.
- Parameters:
desc1 (
Tensor) – First descriptors, shape(B, N, D).desc2 (
Tensor) – Second descriptors, shape(B, M, D).th (
float, optional) – Inclusive distance-ratio threshold. Default:0.95dm (
Optional[Tensor], optional) – Optional precomputed L2 distances, shape(B, N, M). Default:Nonemask1 (
Optional[Tensor], optional) – Optional boolean mask of valid first descriptors, shape(B, N). Default:Nonemask2 (
Optional[Tensor], optional) – Optional boolean mask of valid second descriptors, shape(B, M). Masks allow padding variable-length pairs; padding never becomes a neighbor. Default:None
- Return type:
- Returns:
Ratios, shape
(K, 1), with the input device and dtype.Long indices, shape
(K, 3):(batch_index, index_in_desc1, index_in_desc2). Indices refer to the original padded tensors, sorted by batch and first-descriptor index. A pair with fewer than two valid descriptors on either side contributes no matches.
Note
Memory for the distance matrix scales as \(B N M\). Bucket pairs by descriptor counts and limit batch size when matching large collections. Half-precision descriptors use float32 distance computation before ratios are converted back to the input dtype. Masked inputs compute the distances directly, without the matrix-multiplication form, which avoids its cancellation but can be slower for large masked descriptor sets. As in
match_smnn(), a zero second-neighbor distance produces an undefined ratio and that ambiguous match is rejected. Ties followtorch.topk()and do not have a guaranteed cross-device ordering.See also
match_smnn(): the same test for one pair of descriptor sets of any sizes, with no padding.match_snn(): the ratio test in one direction for one pair, without the mutual check.
Example
>>> a = torch.tensor([[[0., 0.], [1., 1.], [2., 2.]]]) >>> ratios, indices = match_smnn_batched(a, a.flip(1)) >>> indices tensor([[0, 0, 2], [0, 1, 1], [0, 2, 0]])
- kornia.feature.match_fginn(desc1, desc2, lafs1, lafs2, th=0.8, spatial_th=10.0, mutual=False, dm=None)[source]#
Match desc1 to desc2 by a ratio test against the first geometrically inconsistent neighbor (FGINN).
dists, idxs = match_fginn(desc1, desc2, lafs1, lafs2, th, spatial_th)takes two descriptor sets(B1, D)and(B2, D)and the LAFs(1, B2, 2, 3)ofdesc2, keeps the rows ofdesc1whose nearest neighbour indesc2passes the ratio test ofmatch_snn()against the nearest candidate whoselafs2centre lies at leastspatial_thfrom the nearest one’s, and returns their ratios(B3, 1)and index pairs(B3, 2), in the order of the rows ofdesc1. Closer candidates are skipped (see [MMP15] for more details), so a near-duplicate detection of the same structure, such as another scale or orientation at one place, does not reject the match as it can inmatch_snn().lafs1is not read, andmutual=Trueadds the mutual nearest-neighbour check. To test the local affine consistency of the matches in both images, usematch_adalam()instead.- Convention:
See the Convention block on
match_nn().distsis a ratio: the nearest distance over the distance of the second neighbour, which is the nearest of themin(10, B2)nearest candidates, the first one excluded, whoselafs2centre lies at leastspatial_thfrom the centre of the first one. Candidates closer than that count as the same structure, not as competitors, whatever their descriptor distance. When all of them are – a dense cluster of detections on one structure – the ratio is 0 and the match is accepted: no distinct competing structure among the candidates means the nearest neighbour is unambiguous. A descriptor beyond themin(10, B2)nearest is never the second neighbour, even when every candidate is excluded.Only the centres of
lafs2are read, andspatial_this in their units: pixels for LAFs in pixels.
- Parameters:
desc1 (
Tensor) – Batch of descriptors of a shape(B1, D).desc2 (
Tensor) – Batch of descriptors of a shape(B2, D).lafs1 (
Tensor) – LAFs of a shape(1, B1, 2, 3). Accepted for API symmetry withmatch_adalam()but not read by this function – onlylafs2feeds the geometric check.lafs2 (
Tensor) – LAFs of a shape(1, B2, 2, 3).th (
float, optional) – distance ratio threshold. Default:0.8spatial_th (
float, optional) – minimal distance between thelafs2centres of the 1st and the 2nd nearest neighbor, in the units of those centres. Default:10.0mutual (
bool, optional) – also perform mutual nearest neighbor check. Default:Falsedm (
Optional[Tensor], optional) – torch.Tensor containing the distances from each descriptor in desc1 to each descriptor in desc2, shape of(B1, B2). Default:None
- Return type:
- Returns:
Ratio of the nearest to the second-neighbour distance of each match, see the Convention block, shape of
(B3, 1).Long torch.Tensor indexes of matching descriptors in desc1 and desc2. Shape:
(B3, 2), where 0 <= B3 <= B1.
See also
match_snn(): the plain ratio test, whose second neighbour can lie on the same structure.match_adalam(): keeps the nearest-neighbour matches that local affine models of both images support.GeometryAwareDescriptorMatcher: the module form, withmatch_mode="fginn".
- kornia.feature.match_adalam(desc1, desc2, lafs1, lafs2, config=None, hw1=None, hw2=None, dm=None)[source]#
Match descriptors by nearest neighbour, filtered by local affine model support (AdaLAM).
dists, idxs = match_adalam(desc1, desc2, lafs1, lafs2, hw1=(H1, W1), hw2=(H2, W2))takes two descriptor sets(B1, D)and(B2, D), their LAFs(1, B1, 2, 3)and(1, B2, 2, 3)and, optionally, the image sizes in the units of the LAF centres, pixels for LAFs in pixels. It matches every row ofdesc1to its nearest neighbour indesc2and returns the matches that the local RANSACs around confident seed matches accept, each match once: the inverse RANSAC confidences(B3, 1), float32, and the index pairs(B3, 2). The local models read the LAF orientations and scales as well as the centres, and need no global model of the scene, see [CLO+20] for more details. To keep the matches that one global model explains instead, pass the matches ofmatch_snn()ormatch_smnn()toRANSAC.- Convention:
See the Convention block on
match_nn().distsis the inverse of the confidence of the most confident local RANSAC that accepted the match (overlapping neighbourhoods often accept a match several times), in float32 whatever the descriptor dtype, so at most1 / min_confidence(1 / 200by default). If no seed survives, for example with very few keypoints, the matches of a ratio test on squared distances,ratio <= 0.95, are returned with that ratio instead.The putative matches are the nearest neighbours in the distance matrix
dm, scored by the ratio of the two smallest distances; withforce_seed_mnnonly mutual nearest neighbours can be seeds.dmholds plain distances, like thedmofmatch_nn()and the other matchers: AdaLAM squares it internally (its thresholds are tuned for squared distances), sodm=torch.cdist(desc1, desc2)gives the matches ofdm=None, where the squared L2 distances between the descriptors are used.The LAFs give each keypoint’s centre, orientation in degrees and scale. Orientations and scales are read only through how their change between the two images varies from match to match: adding an angle to the orientations of one image or both, negating both, or scaling the LAFs of one image or both alike changes nothing.
hw1andhw2enter only through the area of their image, which sets the neighbourhood radius in that image, so(H, W)and(W, H)give the same matches. Without them, the extent of each image’s keypoints is used.configis merged into the defaults ofkornia.feature.adalam.get_adalam_default_config(); an unknown key warns and is ignored. The computation runs on the device ofdesc1unlessconfigsets"device".Local affine consensus counts distinct source and target locations, reserving three samples for the seed and minimal fitting pair. Repeated locations use the first observation in descriptor-score order as evidence; a later conflicting match cannot replace it. Equal residuals do not imply duplicate keypoints. The full accepted radius is returned, including repeated observations tied at its boundary and mixtures of exact and noisy matches. Confidence uses a conservative residual-area floor of
2**-24in normalized coordinates, remaining finite for exact fits.Unlike the reference implementation, which merges nearly equal residuals into float16 bins, the consensus radius is the largest at which both the cumulative inlier density and the density over the last
min_inlierssamples reachmin_confidence.
- Parameters:
desc1 (
Tensor) – Batch of descriptors of a shape(B1, D).desc2 (
Tensor) – Batch of descriptors of a shape(B2, D).lafs1 (
Tensor) – LAFs of a shape(1, B1, 2, 3).lafs2 (
Tensor) – LAFs of a shape(1, B2, 2, 3).config (
Optional[AdalamConfig], optional) – dict with AdaLAM config, merged into the defaults; see the Convention block. Default:Nonehw1 (
Optional[Sequence[int]], optional) – size of the first image,(H, W)or(W, H). Default:Nonehw2 (
Optional[Sequence[int]], optional) – size of the second image,(H, W)or(W, H). Default:Nonedm (
Optional[Tensor], optional) – torch.Tensor containing the non-negative distances (not squared) from each descriptor in desc1 to each descriptor in desc2, shape of(B1, B2). Smaller is better. Default:None
- Return type:
- Returns:
Inverse local RANSAC confidence of each match, float32, shape of
(B3, 1); see the Convention block.Long torch.Tensor indexes of matching descriptors in desc1 and desc2. Shape:
(B3, 2), where 0 <= B3 <= B1.
See also
match_fginn(): a ratio test that reads the LAF centres of desc2 only, with no local models.GeometryAwareDescriptorMatcher: the module form, withmatch_mode="adalam"and no image sizes.RANSAC: one global model, such as a homography or a fundamental matrix, for all the matches.
- class kornia.feature.DescriptorMatcher(match_mode='snn', th=0.8)[source]#
Match two descriptor sets by nearest neighbours, mutual ones or a ratio test, as a module.
matcher = DescriptorMatcher("snn", th=0.8)builds the module anddists, idxs = matcher(desc1, desc2)takes two descriptor sets(B1, D)and(B2, D)and returns whatmatch_nn(),match_mnn(),match_snn()ormatch_smnn()returns for them, bymatch_mode:(B3, 1)qualities and(B3, 2)index pairs. It holds no parameters and reads no LAFs, which makes it a matcher forLocalFeatureMatcher. For matchers that also read the LAFs, useGeometryAwareDescriptorMatcherinstead, and for a learned one,LightGlueMatcher.- Convention:
See the Convention block on
match_nn().forwardreturns what the function ofmatch_mode(case-insensitive) returns for the two descriptor sets, with the module’sthforsnnandsmnn.thdefaults to 0.8 for both, whilematch_smnn()defaults to 0.95, andnnandmnndo not read it. There is nodm.
- Parameters:
- Raises:
NotImplementedError – for another
match_mode.
See also
match_nn(): the function ofmatch_mode="nn", which also takes a precomputeddm; so do the other three.match_mnn(): the function ofmatch_mode="mnn".match_snn(): the function of the defaultmatch_mode="snn".match_smnn(): the function ofmatch_mode="smnn", whose ownthdefaults to 0.95.GeometryAwareDescriptorMatcher: the module form ofmatch_fginn()andmatch_adalam(), which also take LAFs.LocalFeatureMatcher: detects and describes the features of two images, then matches them with a module like this one.
- forward(desc1, desc2)[source]#
Run forward.
- Parameters:
- Return type:
- Returns:
distsof the matching function, seematch_nn(), shape of(B3, 1).- Long torch.Tensor indexes of matching descriptors in desc1 and desc2,
shape of
(B3, 2)where \(0 <= B3 <= B1\).
- class kornia.feature.GeometryAwareDescriptorMatcher(match_mode='fginn', params=None)[source]#
Match two descriptor sets that come with their LAFs, by FGINN or AdaLAM, as a module.
matcher = GeometryAwareDescriptorMatcher("fginn", params)builds the module anddists, idxs = matcher(desc1, desc2, lafs1, lafs2)takes two descriptor sets(B1, D)and(B2, D)and their LAFs(1, B1, 2, 3)and(1, B2, 2, 3), and returns whatmatch_fginn()ormatch_adalam()returns for them, bymatch_mode:(B3, 1)ratios or inverse confidences and(B3, 2)index pairs. It passes nodm, and no image sizes to AdaLAM; to give them, call the functions directly. For descriptors without LAFs, useDescriptorMatcherinstead.- Convention:
See the Convention block on
match_nn().forwardreturns what the function ofmatch_mode(case-insensitive) returns:"fginn"reads the keysth,spatial_thandmutualofparamsand ignores any other key.thdefaults to 0.85, whilematch_fginn()defaults to 0.8."adalam"passesparamsas the AdaLAMconfig, and nohw1orhw2, so AdaLAM takes the extent of each image’s keypoints as the image size.
- Parameters:
- Raises:
NotImplementedError – for another
match_mode.
See also
match_fginn(): the function of the defaultmatch_mode="fginn", whose ownthdefaults to 0.8.match_adalam(): the function ofmatch_mode="adalam", which also takes the image sizes.DescriptorMatcher: the module form of the matchers that read descriptors only.LightGlueMatcher: a learned matcher with the same call, which also takes the image sizes.
- forward(desc1, desc2, lafs1, lafs2)[source]#
Run forward.
- Parameters:
- Return type:
- Returns:
distsof the matching function, seematch_nn(), shape of(B3, 1).- Long torch.Tensor indexes of matching descriptors in desc1 and desc2,
shape of
(B3, 2)where \(0 <= B3 <= B1\).
- class kornia.feature.LocalFeatureMatcher(local_feature, matcher)[source]#
Detect, describe and match the local features of two images with a descriptor matcher.
matcher = LocalFeatureMatcher(local_feature, DescriptorMatcher("snn", 0.8))builds the module andout = matcher({"image0": img0, "image1": img1})runslocal_featureon both batches of images, which theLocalFeaturepresets take as grayscale(B, 1, H, W), exceptSIFTFeature,KeyNetHardNetandKeyNetAffNetHardNet, which take one image per call,(1, 1, H, W). It matches the descriptors of each pair of images and returns a dict of the matches of all pairs:keypoints0andkeypoints1(NC, 2),lafs0andlafs1(1, NC, 2, 3),confidence(NC,)and the pair of each match,batch_indexes(NC,). Features already computed for an image can be passed instead of runninglocal_featureon it. For a matcher that reads the LAFs, such asLightGlueMatcher, call it on the outputs of the local feature yourself; for matching without a detector, useLoFTR.- Convention:
keypoints0andkeypoints1are the centres(x, y)of the returned LAF tensorslafs0andlafs1, respectively; seelaf_from_center_scale_ori()and Local features for the LAF frame.confidenceis1 - distsof the matcher (seematch_nn()).All-zero LAFs, the padding slots of kornia’s detectors (see
laf_is_filled()), are dropped before matching.matcheris called asmatcher(descriptors0, descriptors1)for each image pair, so it has to be a descriptor-only matcher such asDescriptorMatcher;GeometryAwareDescriptorMatcherandLightGlueMatcherneed LAFs and do not fit.
- Parameters:
local_feature (
Module) – Local feature detector and descriptor, called aslocal_feature(image, mask)and returning(lafs, responses, descriptors)likeLocalFeature. SeeGFTTAffNetHardNet.matcher (
Module) – Descriptor matcher, seeDescriptorMatcher.
- Returns:
Dictionary with image correspondences and confidence scores.
- Return type:
Dict[str, torch.Tensor]
See also
LocalFeature: the detector and descriptor pipeline thatlocal_featureusually is.DescriptorMatcher: the descriptor-only matcher it takes.LightGlueMatcher: a learned matcher, which takes the LAFs and the image sizes as well.LoFTR: matches two images directly, without a detector.RANSAC: the next step, which keeps the matches one geometric model explains.
Example
>>> img1 = torch.rand(1, 1, 320, 200) >>> img2 = torch.rand(1, 1, 128, 128) >>> input = {"image0": img1, "image1": img2} >>> gftt_hardnet_matcher = LocalFeatureMatcher( ... GFTTAffNetHardNet(10), kornia.feature.DescriptorMatcher('snn', 0.8) ... ) >>> out = gftt_hardnet_matcher(input)
- forward(data)[source]#
Run forward.
- Parameters:
data (
Dict[str,Tensor]) – dictionary containing the input data in the following format:- Keyword Arguments:
image0 – left image with shape
(N, 1, H1, W1). Required also withlafs0anddescriptors0: its first dimension gives the number of image pairs.image1 – right image with shape
(N, 1, H2, W2); not read whenlafs1anddescriptors1are given.mask0 (optional) – left image mask. ‘0’ suppresses detection, with shape
(N, H1, W1)or(N, 1, H1, W1).mask1 (optional) – right image mask. ‘0’ suppresses detection, with shape
(N, H2, W2)or(N, 1, H2, W2).lafs0 (optional) – pre-extracted LAFs of image0,
(N, M0, 2, 3). Whenlafs0anddescriptors0are both given, they are matched andlocal_featuredoes not run on image0.descriptors0 (optional) – pre-extracted descriptors of image0,
(N, M0, D).lafs1 (optional) – pre-extracted LAFs of image1,
(N, M1, 2, 3); withdescriptors1, as for image0.descriptors1 (optional) – pre-extracted descriptors of image1,
(N, M1, D).
- Return type:
- Returns:
keypoints0, matching keypoints from image0(NC, 2).keypoints1, matching keypoints from image1(NC, 2).confidence,1 - descriptor distance(NC,). This lies in \([0, 1]\) only for the ratio-basedDescriptorMatchermodes (snn,smnn);nnandmnnreturn raw distances, which are unbounded for an arbitrary descriptor, so the confidence has no lower bound in general – it goes at least down to -1, the value reached by unit-norm descriptors, whose distance tops out at 2.0.lafs0, matching LAFs from image0(1, NC, 2, 3).lafs1, matching LAFs from image1(1, NC, 2, 3).batch_indexes, batch indexes for the keypoints and lafs(NC,).
Learned matchers#
- class kornia.feature.LightGlueMatcher(feature_name='disk', params=None)[source]#
Match descriptors and LAFs with LightGlue, called like GeometryAwareDescriptorMatcher.
matcher = LightGlueMatcher("disk")loads the LightGlue weights trained for that local feature, andscores, idxs = matcher(desc1, desc2, lafs1, lafs2, hw1, hw2)takes the descriptors(B1, D)and(B2, D)of two images (or(1, B1, D)and(1, B2, D)), their LAFs(1, B1, 2, 3)and(1, B2, 2, 3)in pixels and, optionally, the image sizes(H, W), and returns LightGlue’s matching scores(B3, 1), higher for better matches, and the index pairs(B3, 2), in the format ofGeometryAwareDescriptorMatcher. The descriptors must come from the feature the weights were trained for, seefeature_namein the Convention block. To pass batches of keypoints in dicts and read every output of the network, useLightGlue; for an exported ONNX model, useOnnxLightGlue. It is based on the original code of “LightGlue: Local Feature Matching at Light Speed”, see [LSP23] for more details.- Convention:
lafs1andlafs2use the pixel convention oflaf_from_center_scale_ori()(see Local features), and their centres go to LightGlue unchanged as the keypoints.hw1andhw2are the(H, W)of each image, which become LightGlue’simage_size(W, H); without them, the image size is taken as the largest keypoint coordinates of that image,(max x, max y).Choose
feature_namefor the source of the descriptors (case-insensitive). The network receives the following data besides keypoints:"aliked": use the 128-D descriptors ofALIKED. The 64-D"aliked-t16"descriptors are incompatible with these weights."dedodeb","dedodeg": use theB, respectivelyG, descriptors ofDeDoDe. Build the LAFs at keypoints minus 0.5, because DeDoDe puts pixeliati + 0.5."disk","raco-aliked","superpoint","xfeat": use descriptors from the named feature and no additional LAF attributes. For"xfeat", pass the 64-D descriptors ofdetectAndCompute()and LAFs centred on its keypoints, such aslaf_from_center_scale_ori(keypoints[None]), and call the matcher undertorch.no_grad()ortorch.inference_mode(): it raises on XFeat’s inference tensors with gradients enabled (#6533)."sift": useSIFTFeatureScaleSpacewithdescriptor_layout="opencv". The weights expect the scale and orientation of the COLMAP keypoints they were trained on: the negated LAF orientation in \((-\pi, \pi]\), and a sixth of the LAF scale, which is the detection sigma for 6-sigma DoG frames such asSIFTFeatureScaleSpace’s. The descriptors must be in OpenCV’s SIFT layout, which these weights expect, while kornia’s SIFT descriptors use another order of the same values: create the feature withdescriptor_layout="opencv", or convert the descriptors withconvert_sift_descriptor_layout(descs, "kornia", "opencv")(convert_sift_descriptor_layout())."doghardnet","dog_affnet_hardnet": useDoGHardNetorDoGAffNetHardNet. Pass the LAFs the HardNet descriptors were computed on, at the scale the weights were trained with: 12 sigma, six times OpenCV’sKeyPoint.sizeand twice the 6-sigma DoG frames ofSIFTFeatureScaleSpace.DoGHardNetandDoGAffNetHardNetreturn such LAFs and their descriptors; with another 6-sigma DoG detector, applyscale_laf()with2.0to its LAFs before describing them. The network takes a sixth of the LAF scale, which isKeyPoint.sizefor these frames, and the LAF orientation in \([0, 2\pi)\)."keynet_affnet_hardnet": useKeyNetAffNetHardNet. The network receives the ends of the two LAF axes,c + A[:, 0]andc + A[:, 1], normalised like the keypoints.
The first returned value is LightGlue’s matching score, not a distance: higher is better, and only matches scoring above
filter_threshold(0.1 by default) are returned. The index rows are(index in desc1, index in desc2). Fewer than two descriptors on either side give no match.All-zero padding LAFs are not filtered out: such a slot reaches the network as a keypoint at
(0, 0). Drop them withlaf_is_filled()first.
- Parameters:
See also
LightGlue: takes batches of keypoints in dictionaries and returns all network outputs.OnnxLightGlue: runs an exported LightGlue model for one image pair.LocalFeatureMatcher: matches local features with a descriptor-only matcher.LoFTR: matches images directly, without a detector.GeometryAwareDescriptorMatcher: offers FGINN and AdaLAM with the same call.
- forward(desc1, desc2, lafs1, lafs2, hw1=None, hw2=None)[source]#
Run forward.
- Parameters:
desc1 (
Tensor) – Batch of descriptors of a shape(B1, D), or(1, B1, D)for a batch of one.desc2 (
Tensor) – Batch of descriptors of a shape(B2, D), or(1, B2, D)for a batch of one.lafs1 (
Tensor) – LAFs of a shape(1, B1, 2, 3).lafs2 (
Tensor) – LAFs of a shape(1, B2, 2, 3).hw1 (
Optional[Tuple[int,int]], optional) –(H, W)of the first image; see the Convention block. Default:Nonehw2 (
Optional[Tuple[int,int]], optional) –(H, W)of the second image; see the Convention block. Default:None
- Return type:
- Returns:
LightGlue’s matching score of each match, higher is better, shape of
(B3, 1).- Long torch.Tensor indexes of matching descriptors in desc1 and desc2,
shape of
(B3, 2)where \(0 <= B3 <= B1\).
- class kornia.feature.LightGlue(features='superpoint', **conf_)[source]#
Match the keypoints and descriptors of image pairs with the LightGlue transformer, given as dicts.
lightglue = LightGlue("disk")loads the weights trained for that local feature andout = lightglue({"image0": feats0, "image1": feats1})takes, for each image, a dict ofkeypoints(B, M, 2)as(x, y)pixels,descriptors(B, M, D)andimage_size(B, 2)as(W, H), and returns a dict whosematches0(B, M)holds the index in image1 of the match of each keypoint of image0,-1for an unmatched one, with its score inmatching_scores0(B, M);matchesandscoreslist the matched index pairs(S, 2)and scores(S,)of each pair of images. The network is a series of transformer layers, with early stopping and point pruning unless the configuration turns them off. For the call of the other kornia matchers, on descriptors and LAFs, useLightGlueMatcher; for an exported ONNX model run by onnxruntime, useOnnxLightGlue.- Convention:
Complete learned weights return the model in evaluation mode, even if derived buffers are absent from the checkpoint. Random or partially loaded learned weights leave it in training mode.
The
keypointsof each image are(x, y)pixels andimage_sizeis(W, H); animagetensor gives the size from its last two dimensions instead.forwardnormalises the keypoints to(k - size / 2) / (max(size) / 2), as the reference implementation does, and raises for a keypoint outside \([-1, 1]\) after that. The keypoints can be wider than the model, like the float32 keypoints of kornia’s detectors with a float16 or bfloat16 model; the positional encoding runs in the model’s dtype.The
"sift","doghardnet"and"dog_affnet_hardnet"configurations, which setadd_scale_ori, also readscalesandoris(radians) of each image; the last two multiplyscalesby theirscale_coefof 1/6 first, so they take the LAF scale."keynet_affnet_hardnet", which setsadd_laf, readslafs.LightGlueMatcherbuilds them from LAFs.The matching scores,
matching_scores0,matching_scores1andscores, are in the dtype of the descriptors withmp=False. Withmp=True, autocast sets it, for every configuration and also when point pruning removes every point. On CPU, autocast runs in float16 for a float16 model and in bfloat16, CPU autocast’s default, for any other: a float16 model gives float16 scores for float16 and float32 descriptors, a float32 or bfloat16 model gives bfloat16 scores for float32 and bfloat16 descriptors, and a float64 model gives float64 scores for float64 descriptors. Float16 descriptors with any other model, and bfloat16 descriptors with a float16 model, are not supported there (#5929). On MPS, float32, float16 and bfloat16 descriptors give float32 scores; on CUDA, float32 descriptors give float32 scores, and a float64 model gives float64 scores for float64 descriptors, as on CPU, because autocast does not cast float64. An image without descriptors runs no layer, and its zero scores keep the descriptor dtype, also withmp=True.Each pair of a batch gives the matches and scores it gives on its own when point pruning can run, that is with
width_confidence > 0, no padding to thestatic_lengthsofLightGlue.compile(which wraps the transformer layers intorch.compile()), and an image with more keypoints thanpruning_min_kpts(device)returns: any number on CPU and MPS, more than 1536 on CUDA with the defaultflash=Trueand more than 1024 without. Pruning keeps a different number of points in each pair, so then the pairs run one at a time:matches0,matches1,matching_scores0,matching_scores1,prune0andprune1stack the rows of the pairs,matchesandscoreslist them,stopis the largest number of layers that a pair ran, and there is nolog_assignment. Otherwise a batch runs as one, and early stopping (depth_confidence) ends it at a layer only when every pair meets its own criterion. A pair can therefore run more layers in a batch than on its own, and its matches can differ. Early stopping counts the real keypoints only, so padding to thestatic_lengthsofLightGlue.compiledoes not change the layer at which a pair stops.
- Parameters:
features (
Optional[str], optional) – The type of local features to match, whose configuration and pretrained weights are loaded: one of"aliked","dedodeb","dedodeg","disk","dog_affnet_hardnet","doghardnet","keynet_affnet_hardnet","raco-aliked","sift","superpoint"and"xfeat".Nonebuilds the model fromdefault_confand the keyword arguments and downloads no weights. Default:"superpoint"**conf – keyword arguments that override entries of
default_confand of the configuration offeatures, such asfilter_thresholdordepth_confidence. The configuration offeaturessets the defaults, and an explicit keyword argument always wins, exceptweights, which must name the checkpoint offeatureswhenfeaturesis given;weights=None, the value indefault_conf, counts as not given. This departs on purpose from cvg/LightGlue, which applies the configuration offeaturesafter the keyword arguments, so that the tuning knobs a preset sets, such as"xfeat"’swidth_confidence, can be changed.
See also
LightGlueMatcher: the same network on descriptors and LAFs, returning scores and index pairs like the other kornia matchers.OnnxLightGlue: an exported LightGlue model run by onnxruntime, for one image pair on CPU or CUDA.LoFTR: a transformer matcher that takes the images themselves, without a detector.
- forward(data)[source]#
Match keypoints and descriptors between two images.
- Return type:
- Input (dict):
- image0: dict
keypoints: [B x M x 2] descriptors: [B x M x D] image: [B x C x H x W] or image_size: [B x 2] scales, oris: [B x M], with add_scale_ori lafs: [B x M x 2 x 3], with add_laf
- image1: dict
keypoints: [B x N x 2] descriptors: [B x N x D] image: [B x C x H x W] or image_size: [B x 2] scales, oris: [B x N], with add_scale_ori lafs: [B x N x 2 x 3], with add_laf
- Output (dict):
log_assignment: [B x M+1 x N+1], not for a batch that prunes points (see the Convention block) matches0: [B x M] matching_scores0: [B x M] matches1: [B x N] matching_scores1: [B x N] matches: List[[Si x 2]], scores: List[[Si]] stop, prune0, prune1: diagnostics of early stopping and point pruning
- class kornia.feature.OnnxLightGlue(weights=None, device='cpu')[source]#
Match the keypoints and descriptors of one image pair with an exported LightGlue ONNX model.
matcher = OnnxLightGlue("disk", device="cpu")downloads an exported LightGlue [LSP23] model, or loads your own ONNX file, into an onnxruntime session, andout = matcher(data)takes the dicts ofLightGluefor a batch of one: float32keypoints(1, M, 2)in pixels anddescriptors(1, M, D)on that device, withimage_size(1, 2)as(W, H)or animage. It returns the matched index pairsmatches(S, 2)and theirscores(S,). It needsonnxruntimeand runs on CPU or CUDA only. For batches, other devices or dtypes, scales, orientations or LAFs, or the full outputs of the network, useLightGlueinstead.If you have trained your own LightGlue model, see fabio-sim/LightGlue-ONNX for how to export the model to ONNX and optimize it.
- Convention:
See the Convention block on
LightGlue: the keypoints are normalised the same way, withimage_sizein the order(W, H), and the same range check applies. UnlikeLightGlue, it takes float32 inputs with a batch of one and no scales, orientations or LAFs, and it runs on CPU or CUDA only.Known defect: the session and its inputs are bound to GPU 0 for every CUDA
device, sodevice="cuda:1"does not run on GPU 1 (#6427).
- Parameters:
weights (
str|None, optional) – Pretrained weights, or a path to your own exported ONNX model. Available pretrained weights are'disk','superpoint','disk_fp16', and'superpoint_fp16'. Note that FP16 requires CUDA. Defaults to'disk_fp16'ifdeviceis CUDA, and'disk'if CPU. Default:Nonedevice (
Union[str,device,None], optional) –'cpu'or'cuda'to run inference on; another device type raisesValueError.Nonemeans'cpu', as in the other kornia feature constructors, not torch’s current default device. Default:"cpu"
See also
LightGlue: the PyTorch network, which also runs batches, on MPS, and in float16, bfloat16 or float64 (see its Convention block), and returns every output.LightGlueMatcher: LightGlue on descriptors and LAFs, returning scores and index pairs like the other kornia matchers.
- forward(data)[source]#
Match keypoints and descriptors between two images.
The output contains the matches (the indices of the matching keypoint pairs between the first and second image) and the corresponding confidence scores. Only a batch size of 1 is supported.
- Parameters:
data (
dict[str,dict[str,Tensor]]) – Dictionary containing both images and the keypoints and descriptors thereof.- Return type:
- Returns:
Dictionary containing the matches and scores.
data(dict):image0(dict):keypoints(float32):(1, M, 2)descriptors(float32):(1, M, D)image:(1, C, H, W)orimage_size:(1, 2)image1(dict):keypoints(float32):(1, N, 2)descriptors(float32):(1, N, D)image:(1, C, H, W)orimage_size:(1, 2)
output(dict):matches(int64):(S, 2)scores(float32):(S,)
- class kornia.feature.LoFTR(pretrained='outdoor', config=default_cfg)[source]#
Match two grayscale images without a detector, by transformer-matched dense features (LoFTR).
loftr = LoFTR("outdoor")loads pretrained weights andout = loftr({"image0": img0, "image1": img1})takes two batches ofNgrayscale images(N, 1, H, W)in \([0, 1]\), the two possibly of different sizes, and returns a dict of the matches of all pairs: the pixel positionskeypoints0andkeypoints1(NC, 2), as(x, y)in the frame of the reference implementation, half a pixel off kornia’s (see the Convention block), theirconfidence(NC,)and the pair of each match,batch_indexes(NC,). It needs no keypoints or descriptors: the matches of image0 lie on a grid of 8-pixel cells, not at detected keypoints. To match one image against many, compute its backbone features, which do not depend on the other image, once withextract_features()and pass them asfeatures0orfeatures1. For matching detected keypoints and their descriptors, useLightGlueMatcherorLocalFeatureMatcherinstead.This is based on the original code of “LoFTR: Detector-Free Local Feature Matching with Transformers”, see [SSW+21] for more details.
- Convention:
image0andimage1are grayscale(N, 1, H, W)images in \([0, 1]\), the input of the pretrained models. With the defaultconfig, whoseresolution(8, 2) every pretrained model uses, the two images can differ in size, and their heights and widths do not need to be multiples of 8 (the reference implementation expects multiples of 8).With that
resolution,keypoints0lie on the grid of the 1/8-resolution coarse feature map, at8 * (column, row)of a coarse cell, andkeypoints1at8 * (column, row)of the matching cell of image1 plus the offset found by the fine stage, at most 4 pixels along each axis. Both are \((x, y)\) in the frame of the reference implementation, in which the pretrained models put the centre of pixel \((x, y)\) at \((x + 0.5, y + 0.5)\): subtract 0.5 to get kornia’s frame (Local features), in which pixel centres have integer coordinates. The keypoints are float32 for a float16 or bfloat16 model and float64 for a float64 one;confidencekeeps the model dtype.confidenceis the coarse matching score of the match, a dual-softmax score with the defaultmatch_type; only matches abovemax(thr, 0)are returned, with thethrofconfig["match_coarse"], 0.2 by default.Coarse matches within
border_rmcells of an edge are dropped (2 cells, 16 pixels by default). Withmask0ormask1, each sample’s border is measured from its valid region, assumed to be a top-left-aligned rectangle with padding on the right and bottom. Either mask may be supplied alone; the other image is then treated as fully valid. Masks are sampled at the coarse cell centres.
- Parameters:
pretrained (
Optional[str], optional) – Download and set pretrained weights to the model. Options: ‘outdoor’, ‘indoor’, ‘indoor_new’. ‘outdoor’ is trained on the MegaDepth dataset and ‘indoor’ and ‘indoor_new’ on the ScanNet. ‘indoor_new’ also switches the positional encoding of the copy totemp_bug_fix=True, which is what its checkpoint was trained with. The weighted model comes back in evaluation mode.Nonebuilds random weights in training mode, whereCoarseMatchingruns its supervised training branch and needs ground-truth data; call.eval()for inference, also after loading your own weights. Default:"outdoor"config (
dict[str,Any], optional) – Dict with initialization parameters. Do not pass it, unless you know what you are doing. The model works on its own deep copy, so the dict you pass is never modified. Default:default_cfg
- Returns:
Dictionary with image correspondences and confidence scores.
See also
LightGlueMatcher: a transformer matcher for the keypoints and descriptors of a detector.LocalFeatureMatcher: detects, describes and matches two images with a descriptor matcher.RANSAC: the next step, which keeps the matches one geometric model explains, such as afind_fundamental()matrix.
Example
>>> img1 = torch.rand(1, 1, 320, 200) >>> img2 = torch.rand(1, 1, 128, 128) >>> input = {"image0": img1, "image1": img2} >>> loftr = LoFTR('outdoor') >>> out = loftr(input)
- extract_features(image)[source]#
Compute the features of images that do not depend on the image they are matched with.
These are the backbone’s coarse and fine feature maps, the coarse one with its positional encoding. Pass the returned dict to
forward()asfeatures0orfeatures1instead ofimage0orimage1to match an image against many others without running the backbone again for every pair.- Parameters:
image (
Tensor) – grayscale images with shape(N, 1, H, W).- Return type:
- Returns:
coarse, position-encoded 1/8-resolution features(N, C_c, ceil(H / 8), ceil(W / 8)).fine, 1/2-resolution features(N, C_f, ceil(H / 2), ceil(W / 2)). With the default config this is 128 channels, about 96 MB in float32 for a 1024 x 736 image.
Example
>>> loftr = LoFTR(None).eval() >>> images = [torch.rand(1, 1, 64, 80), torch.rand(1, 1, 72, 64), torch.rand(1, 1, 64, 64)] >>> features = [loftr.extract_features(image) for image in images] >>> out = loftr({"features0": features[0], "features1": features[1]}) >>> out = loftr({"features0": features[0], "image1": images[2]})
- forward(data)[source]#
Run forward.
- Parameters:
data (
dict[str,Any]) – dictionary containing the input data in the following format:- Keyword Arguments:
image0 – left image with shape
(N, 1, H1, W1).image1 – right image with shape
(N, 1, H2, W2).features0 (optional) – instead of
image0, itsextract_features()output.features1 (optional) – instead of
image1, itsextract_features()output.mask0 (optional) – left image mask. ‘0’ indicates a padded position
(N, H1, W1).mask1 (optional) – right image mask. ‘0’ indicates a padded position
(N, H2, W2).
- Return type:
- Returns:
keypoints0, matching keypoints from image0(NC, 2).keypoints1, matching keypoints from image1(NC, 2).confidence, the coarse matching score, see the Convention block(NC,).batch_indexes, batch index of each match(NC,).