Places, Not Pixels

Accessible remote sensing: a queryable representation of the living planet, running in this browser tab
Team Picasso
OpenCV AI Competition 2026, powered by AWS
Keywords: Earth observation · contour tokens · laminar trees · hypergraphs · bit-column indexes · OpenCV 5 · WebAssembly

The demo video. The page below is the same system, live: every section runs in this tab. Three companion spaces go deeper: the token, the index and the model.

opencv.js: loading · SAM 2.1: on first tokenize · planner: loading · hypergraph: loading
downloading the engines (about 50 MB). Everything runs locally, so the weights are sent to your device once.

Abstract Satellite imagery is stored as pixels, and pixels cannot be queried, negated or counted. This page demonstrates, live, a different representation: each scene is cut into closed shapes with permanent addresses, each shape's facts are stored as bit columns, and plain English questions compile to exact set arithmetic. Every model and vision operation below runs in your browser (OpenCV 5 as WebAssembly, SAM 2.1 via onnxruntime, and a 26M parameter planner), and each section includes the source code that produced it. On the three-pass Kansas archive shown below — rebuilt with the archive-fine-tuned decoder — 365 token records carry 169 fact columns and 1,187 hyperedges; the question green in 2023 and not in 2024 answers 27, exactly, by set subtraction; and bitwise containment agrees with geometry on 1,369 of 1,369 tested pairs.

1Pixels cannot be queried

Almost every satellite data product in use was built for a single purpose: a deforestation layer, a crop map, a flood extent. Each is its own project, it answers the one question it was built for, and it starts to go stale on the next overpass. The imagery was never the constraint. What has been missing is a unifying representation: one way to describe any place, over time, that every question can share.

Three questions recur in every monitoring domain, and none of them is a mapping question. Was this place ever X? needs a record that is never overwritten and a query that can express a negative. How much changed between then and now? needs exact area and exact difference, not a similarity score. Which places are behaving abnormally right now? needs each place compared against its own normal. All three require counting, subtracting and intersecting. A map supports none of them, and neither does a grid of pixels. Click a question below to see it fail.

THE GRID'S ANSWER

Pick a question above. A pixel grid is an array of brightness values. It contains no objects, so there is nothing to count, nothing to negate, and no identity to compare across dates.

Figure 1: one Landsat pass over the Kansas site, shown as the grid it is stored as. The fix is to change the unit of storage: if the scene is cut into closed shapes first, every question above becomes set arithmetic.

2Tokens in a tree

The tokenizer asks SAM 2.1 hiera-base-plus (int8-quantized from 277 to 69 MB, executed by onnxruntime in WebAssembly) to segment the scene at 64 equally spaced points — and that number has a story. We first built a learned proposer (a field-trained U-Net suggesting one prompt per region, the segmenteverygrain pattern of Sylvester et al.), then ran the control: a plain grid of dots, no learning at all. Against hand-labelled Landsat the dots won — 28/34 labelled circles against the U-Net's 22/34, with half the tokens — so the dots shipped, and the U-Net is reported in the repository as a falsified hypothesis. OpenCV traces every mask into a polygon (Suzuki–Abe findContours, Ramer–Douglas–Peucker approxPolyDP); near-duplicates are rejected; a second half-stride grid round covers what the first missed; and whatever remains becomes classical tokens, so no pixel is left unowned. Quantization is nearly free: int8 masks are essentially identical to full precision on both validation sites. The decoder you are running was then fine-tuned on our own archive: 65k point→mask pairs from 38 Landsat spot-histories (1985–2025), where each target is the track’s multi-decade consensus boundary rather than one noisy pass. On the same hand-labelled harness it lifts recall from 28/34 to 31/34 circles, trading looser coverage (the classical remainder partition absorbs it) for tighter, object-scale masks — so expect more, smaller tokens. The model downloads once, on your first press of the button. The traced regions are nested by area into a laminar tree: any two regions are either disjoint or one contains the other. Laminarity is not a stylistic choice. It is the condition under which the statistics of a region are well defined at all. The worst bug of this project was two boundary-sharing regions slipping in as siblings, which silently zeroed every statistic beneath them; one containment test, applied on points pulled slightly toward the interior, fixed it.

Because the scene is a tree, each token's position in it can be written down once and kept forever: a 64-bit address, eight bits per level, encoding the path from the root. Containment then needs no geometry. Token B is inside token A exactly when (addr(B) ≫ 8·Δd) ⊕ addr(A) = 0, one shift and one XOR. On the test scene this agrees with the polygon geometry on all 1,369 ordered pairs. The tree is also small: a 37-shape scene flattens to 74 tokens in 865 bytes, and a full landscape hierarchy ships in 120 to 250 KB at 0.95 to 0.97 IoU, roughly 2,000 times smaller than its dense masks.

Figure 2: the tokenizer running in this tab. Each traced outline is one token. When the run finishes, scroll to section 3 and click a shape.

THE CODE: core/laminar.py (the laminar tree built above)
loading…

3The Picasso embedding

Picasso drew the same bull eleven times, removing detail at each step. The final drawing is a few outlines and is still clearly a bull. In the same way, each token gets an embedding computed from its boundary alone: Hu moments, roundness (the token's area over the area of its minimum enclosing circle, which boundary jitter cannot destroy), rectangularity, elongation. Shape combined with simple physical measurements then names the object: round and green is a pivot field, rectangular and wet is a pond, elongated and wet is a channel. These names are arithmetic on the boundary, so they are stamped EXACT. The seven Hu moments are unchanged under translation, scale and rotation, which is what lets the same outline mean the same thing anywhere on Earth. The boundaries themselves came from neural models (the U-Net and SAM 2.1 of section 2), so a proposal is stamped INFERRED; everything measured on the closed boundary is arithmetic. Every answer in this page carries one of those two stamps.

Click any contour below. One record per shape per pass is called a situation: the facts of one thing at one time.

Figure 3: the tokenized scene. Click a shape to open its situation record.

SITUATION

Tokenize in section 2 first, then click a shape here.

THE CODE: hypergraph/picasso.py (Hu moments, form factors, shape codebook)
loading…

4Facts as bit columns

Why bitmaps? A vector similarity search cannot answer not built up, because there is no direction in an embedding space that means not. If instead every fact is one bit per record (cell, period, address), then AND is set intersection, OR is union, NOT is set subtraction, and counting is a single popcount instruction. Negation becomes expressible the moment a boundary closes and its facts land in a column. These are native CPU operations; no model runs at answer time.

Relations are stored the same way. A hypergraph edge is one incidence column: when one change event on the ground touches 129 fields, that is one edge with 129 members, not 8,256 pairwise edges. Containment needs no storage at all, because the address prefix of section 2 already is the containment hyperedge. Structural questions (how is this pivot related to that event?) are answered by intersecting incidence columns, not by walking a graph. Below are the real bit columns of the three-pass Kansas archive. Pick facts, watch the AND row update, and see the matching outlines drawn on the imagery.

period

Figure 4: the archive's bit columns, one column per record, one row per selected fact. The bottom row is the result of the set operation.

THE CODE: store/bitindex.py (AND, OR, NOT, popcount, changed)
loading…
THE CODE: hypergraph/system.py (ingest, fact columns, hyperedges, queries)
loading…

5Earth memory

Identity is the same address observed in more than one pass. For each address the system keeps an exponentially weighted mean and deviation of its embedding. This is the place's own normal: an exponentially weighted mean μ and deviation σ of the combined interior-and-shape embedding, updated on every pass, with no labels and no calendar rules. A new observation x scores z = |x − μ| / σ against that history alone. Drought in a desert is normal; the same reflectance in a rice paddy is an alarm. Because the embedding carries the shape features of section 3, a pond that silts from square to ragged is flagged even when its colour barely moves. The chart shows z for every identity across the three real passes. Several identities cross the threshold, four in the latest pass; the strongest is a pivot field whose greenness collapsed between passes — cleared or harvested.

Figure 5: each line is one place compared against its own history. The dashed line is the alarm threshold z = 2.5.

THE MONITORING AGENT, TRACED ON LIVE DATA

perceive 14.62 km² change · exact
→
decide: over threshold
→
fetch its own confirming window
→
re-measure 20.3 km²
→
ALERT with both evidence images
THE CODE: hypergraph/readers.py (the seven readers; Temporal is the EWMA)
loading…

6Ask in English

A 26M parameter planner (Cactus Needle 3, WebAssembly, running on this device) turns your question into a typed tool call. It does not write the answer. The system resolves the place (WHERE) through a gazetteer to H3 cells and the time (WHEN) to archive periods, compiles an s-expression, evaluates it exactly over the bit columns, and draws the matching outlines on the imagery as evidence.

The design contract comes from System One models: a small model may only produce typed, structured decisions, never free text, so it cannot hallucinate a sentence. Three query shapes cover monitoring practice. Pinpoint: one quantity of one named thing (how large is change event one?). Thematic: tokens matching facts at times (which tokens were green in September 2024?). Structural: how two named things are related, answered as a path through the hypergraph. And the guarded fourth case: a fact, place, period or name that has no bit column in the archive is refused or clarified, never guessed. Try the licence question below.

THE CODE: bench/bench_tokenizer.py (the Graviton vs x86 benchmark, as run)
loading…