Farthest-point sampling
Read these first if they are new to you
- Distance between two points Basic
- Shapes as point clouds Intermediate
The idea in one paragraph
Farthest-point sampling picks a few points from a point cloud so that they cover the whole shape evenly. It starts from one point. Then it repeats one step: find the point whose nearest picked point is farthest away, and pick it. Each pick fills the biggest remaining gap. A random sample, by contrast, picks mostly where points are crowded and can leave sparse parts of the shape empty.
Random picks follow the crowd; farthest picks follow the shape
Many methods need a smaller set of points from a large cloud: a model that accepts a fixed number of points, or a set of centers to group nearby points around. A random sample keeps the cloud's density. If one part of a scan holds ten times as many points, it gets about ten times as many picks, and a thin part may get none.
Farthest-point sampling (FPS) spreads its picks over the space the shape fills, regardless of how crowded each part is. The quantity it watches is each point's gap: its distance to the nearest point picked so far. A point with a large gap is poorly covered.
The cloud has a crowded blob on the left and a sparse arc on the right. Pick 12 points with each method. Before you start, predict which method leaves the larger empty circle.
Keep one gap per point, and pick the largest each round
- Pick a starting point. Set every point's gap to its distance from that start.
- Pick the point with the largest gap.
- Measure every point's distance to the new pick. Where it is smaller than the point's gap, it becomes the new gap.
- Repeat steps 2 and 3 until you have as many points as you need.
Step 3 is cheap: the nearest pick of each point either stays the same or becomes the new pick, so one comparison per point updates every gap.
The points are A (0, 0), B (1, 0), C (2, 0), D (6, 0) and E (6, 3). Start at A.
| After picking | A | B | C | D | E | Next pick |
|---|---|---|---|---|---|---|
| A | 0 | 1 | 2 | 6 | 6.71 | E |
| A, E | 0 | 1 | 2 | 3 | 0 | D |
| A, E, D | 0 | 1 | 2 | 0 | 0 | C |
- From A, E is farthest: √(6² + 3²) = √45 ≈ 6.71. Pick E.
- New distances to E: B is √(25 + 9) ≈ 5.83, C is √(16 + 9) = 5, D is 3. Keep the smaller of old and new gap: B stays 1, C stays 2, and D drops from 6 to 3. The largest gap is D's 3. Pick D.
- D is 5 from B and 4 from C, so their gaps stay 1 and 2. The largest is C's 2. Pick C.
The order is A, E, D, C. B, crowded next to A and C, is picked last.
fps.py
import numpy as np
def farthest_point_sampling(pts, k, start=0):
chosen = [start]
gap = np.sqrt(((pts - pts[start]) ** 2).sum(axis=1)) # distance to the chosen set
for _ in range(k - 1):
# argmax() finds the index of the largest gap, and int() makes it a plain
# Python number. append adds that index to the end of the list chosen.
i = int(gap.argmax()) # the worst-covered point
chosen.append(i)
d = np.sqrt(((pts - pts[i]) ** 2).sum(axis=1))
gap = np.minimum(gap, d) # the new point may be closer
return chosen
pts = np.array([[0, 0], [1, 0], [2, 0], [6, 0], [6, 3]], dtype=float)
print(farthest_point_sampling(pts, 4))
[0, 4, 3, 2]
Indexes 0, 4, 3, 2 are A, E, D, C. pts - pts[i] subtracts one point from every row at once, and
broadcasting explains how. np.minimum takes the smaller of two numbers at
each position, which is step 3 for every point at once. The name _ is a loop variable that the loop never uses.
Each pick measures one distance per point, so K picks from N points cost K × N distances. Picking 1,024 points from 100,000 takes about 108 distances: work that a GPU does quickly, since every point's update is the same simple step.
FPS is greedy: each pick is the best next step, not part of a best overall plan. Its largest gap is still at most twice the smallest largest gap that any choice of the same number of points could reach. The result depends on the start point, which code often picks at random. And because FPS chases far-away points, a single stray point from noise in a scan gets picked early.
Check yourself
-
Your turn: the points are (0, 0), (2, 0), (3, 0), (10, 0) and (10, 1). Start at (0, 0) and pick three points in all. Which are they, in order?
Answer
(0, 0), then (10, 1) at √101 ≈ 10.05. The gaps are then 2 for (2, 0), 3 for (3, 0) and 1 for (10, 0), because (10, 0) is 1 from (10, 1). So the third pick is (3, 0).
-
A scan of a room has 90% of its points on one wall that the scanner faced. You need 500 points that show the whole room. Why is FPS a better choice than a random sample here?
Answer
A random sample would put about 450 of the 500 points on that one wall. FPS spreads its picks over the whole room, because it always fills the largest uncovered region.
-
In the widget with farthest-point picks, what happens to the dashed circle after each pick, and why can it never grow?
Answer
It shrinks or stays the same. Each new pick can only make a point's nearest pick closer, so no gap ever grows, and so neither can the largest gap.
Go further
Optional background.
-
The same method under its name in computer science, with its cost and its "at most twice" guarantee.
-
PointNet++: Deep Hierarchical Feature Learning on Point Sets in a Metric Space Qi, Yi, Su and Guibas, 2017 Paper Advanced
The paper that made FPS a standard step in neural networks for point clouds. Section 3.2 uses it to choose centers.
Where this shows up
No unit or explanation builds on this one yet.