Concept units

Farthest-point sampling

Intermediate

Read these first if they are new to you

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.

Pick points one at a time, and watch the largest gap

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.

The dashed circle is the largest gap. It is centered on the worst-covered point, and its radius is that point's distance to the nearest pick. Farthest-point sampling always picks that center next, so the circle never grows. Dots fade as they become well covered.

Keep one gap per point, and pick the largest each round

  1. Pick a starting point. Set every point's gap to its distance from that start.
  2. Pick the point with the largest gap.
  3. Measure every point's distance to the new pick. Where it is smaller than the point's gap, it becomes the new gap.
  4. 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.

Worked example: four picks from five points

The points are A (0, 0), B (1, 0), C (2, 0), D (6, 0) and E (6, 3). Start at A.

After pickingABCDENext pick
A01266.71E
A, E01230D
A, E, D01200C
  1. From A, E is farthest: √(6² + 3²) = √45 ≈ 6.71. Pick E.
  2. 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.
  3. 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.

What "evenly" means here, and its limits

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

  1. 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).

  2. 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.

  3. 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.

Where this shows up

No unit or explanation builds on this one yet.