Lowe's Ratio Test
The ratio test keeps a match only when the best candidate beats the runner up clearly, which drops pairs from repetitive texture.
Why Does This Exist?
Nearest neighbor always returns something, even on brick walls where every patch looks alike. Absolute distance thresholds fail because good pairs in hard lighting can sit farther than false pairs in clean texture. Lowe's fix compares each query against itself: how much better is best than second best.
A clear winner means a distinctive patch. A close race means ambiguity, so the pair is dropped before geometry. It sits between feature matching and robust fitting.
Think of It Like This
A photo finish rule
A race winner by a full stride is clear. A blanket finish with three noses together is a guess. Judges demand a margin before paying out.
The ratio is that margin. Below about pays, above it refuses. Where it stops: sprint margins are fixed, while descriptor margins shift with binary versus float distances.
How It Actually Works
Run knn with per query to get best distance and runner up . Keep the match when:
Lowe's suits SIFT floats. Binary ORB often uses to on Hamming distances. Apply before cross check and RANSAC so estimators see cleaner input.
Worked example
A SIFT query with and gives , rounded to , and survives. A brick query with and gives , rounded to , and is dropped even though its absolute best beats the first query. On raw pairs, ratio filtering often keeps to , and most drops are correct rejections of repeated texture. Tighten to for cleaner input, loosen to when recall starves.
Code
The point of this snippet is the filter itself: applying to the worked example's distance pairs reproduces the keep and drop verdicts with only the standard library.
pairs = [(210, 340), (190, 205)]tau = 0.75
for d1, d2 in pairs: ratio = round(d1 / d2, 2) print(d1, d2, ratio, "keep" if ratio < tau else "drop")# -> 210 340 0.62 keep# -> 190 205 0.93 dropThe first pair keeps at and the second drops at , matching the hand computation.
Watch Out For
One threshold for all descriptors
Float Euclidean gaps and binary Hamming gaps live on different scales. Copying everywhere either starves ORB or floods SIFT. Tune per descriptor family on your own pairs.
Ratio as the only filter
Ratio kills ambiguity, not geometric error. Repeated windows can still pass by chance. Always follow with cross check or a RANSAC vote before estimating pose.
The Quick Version
- Needs knn with distances per query.
- Keeps matches with below threshold, classically .
- The fixture keeps and drops despite the dropped query's smaller best distance.
- Rejects repetitive texture better than absolute caps.
- Tune separately for float and binary descriptors.