The problem in brief
Each 512x512 image is a smooth synthetic background into which axis-aligned rectangles of four kinds of "glitch" have been planted. Noise fills a rectangle with random RGB pixels. Invert replaces the colours by . Solid fills a rectangle with one colour. Scramble shuffles the original pixels inside a rectangle, so the colour histogram is unchanged. To make life harder, the images also contain decoys that must be left unmarked: JPEG re-encoded patches, hue-shifted patches and Gaussian-blurred patches. For every test image you submit a class mask (0 to 4) and the number of glitch instances of each class. You get 200 training images with masks and counts; the 200 test labels are private, so we evaluate on 40 training images held out from the start.
How you are scored
Per image and per glitch class , the intersection over union of predicted and true pixels,
equals 1 when both are empty. Its mean over the four classes is the image's macro-IoU. Counts are scored by per image and class. The final score is
Two consequences: an absent class must stay exactly absent (one false-positive blob turns an IoU of 1 into 0 for that class), and every spurious blob also costs a count point. No reference score was published that we could access, so there is no target to compare with.
First look at the data
Glitch pixels are only 8 % of the image, about 2 % per class. Every class appears 0 to 3 times per image with roughly uniform frequency. Two structural facts are worth a lot of points.
- Glitches are rectangles and never touch. In all 200 training masks, the number of connected components of each class equals the official count, 800 out of 800 (image, class) pairs.
- Each glitch type has a physical signature. Solid means exactly constant pixels. Invert means the content becomes continuous with its surroundings again once you re-invert it. Noise and scramble are textures: noise draws colours from the whole RGB cube, scramble from the local palette.
A first baseline
A small U-Net (channels 16, 32, 48, 64, 96; the first stage at full resolution so that pixel-level texture is not lost; about 0.6 M parameters) is trained with class-weighted cross-entropy (weight 0.3 on the background) on random 256x256 crops. Augmentation uses flips and 90-degree rotations, which keep rectangles rectangles. Training runs 18 minutes on 2 CPU threads (800 steps of batch 8), and inference averages the 4 rotations. Taking the argmax per pixel and counting connected components gives 70.3 on the 40 held-out images.
Rectangle filling. Using the first structural fact, label the connected components of "any glitch" pixels, drop the tiny ones, give each component the class with the largest summed probability, and replace it by its bounding box. Counts become the number of rectangles per class. Score 73.4 (count score from 73.0 to 81.5).
The pixel confusion matrix of this model is very instructive:
| true \ predicted | clean | noise | invert | solid | scramble |
|---|---|---|---|---|---|
| noise | 0 % | 100 % | 0 % | 0 % | 0 % |
| invert | 0 % | 0 % | 98 % | 2 % | 0 % |
| solid | 2 % | 0 % | 87 % | 11 % | 0 % |
| scramble | 4 % | 0 % | 0 % | 0 % | 96 % |
Noise and scramble are solved; the network even separates them perfectly. But solid blocks are called "invert". On these smooth, saturated backgrounds, an inverted patch and a solid patch both look like "a flat rectangle of an odd colour", and after 800 steps the network has not learned the difference. It also produces invert blobs on the hue-shift decoys.
The key idea
When a network confuses two classes that differ by an exact property, do not train harder: test the property directly, at the level of the object, after segmentation.
Solid = exactly flat. Compute, for every pixel, whether its 3x3 neighbourhood is exactly constant in all three channels (max filter equals min filter). On the training images this is true for 93 % of solid pixels (all but the edges) and for 0 % of noise, invert and scramble pixels. Background and decoys are almost never flat, 0.6 % (JPEG blocks can be). So a rectangle more than half flat is relabelled solid, and a rectangle the network called solid that is not flat goes to its second-best class.
Invert = continuous after re-inversion. For a rectangle the network calls invert, compare a 2-pixel ring just inside each edge with the ring just outside it, twice:
A truly inverted patch was cut out of a smooth background, so re-inverting it closes the jump at the border: , often almost 0. A hue-shift decoy is not an inversion, so re-inverting makes the edge worse. We keep the rectangle only if . No threshold needs tuning: it is a comparison of two hypotheses.

On the held-out images, 82 blobs are predicted invert, 56 of them truly inverted. The re-inversion test keeps 49 of the 56 true ones and rejects all 26 impostors. The 7 it loses are mostly small patches touching other glitches or the image border, where the ring is polluted.

The full solution
- U-Net on 256x256 crops (full-resolution first stage), class-weighted cross-entropy, AdamW with cosine learning rate, flips and rotations; 4-rotation test-time averaging.
- Instances: connected components of the argmax "glitch" pixels; components under 60 pixels are dropped.
- Class of each component: largest summed softmax probability, then the two physical tests (flatness: solid; re-inversion: invert or reject).
- Mask: each kept component is filled as its bounding box. Counts: rectangles per class.
cls = int(prob[1:, sl[0], sl[1]][:, comp].sum(1).argmax()) + 1 # soft majority vote
if flat[sl][comp].mean() > 0.5:
cls = 3 # exactly constant: solid
if cls == 2 and not reinversion_test(img, sl):
continue # hue-shift decoy, drop it
mask[sl] = cls # glitches are rectangles
counts[NAMES[cls - 1]] += 1
Results
40 training images held out from the start (official test labels are private); official metric re-implemented from the statement.
| approach | final | macro-IoU | count score |
|---|---|---|---|
| U-Net after 42 s of training (smoke test, 4 images) | 22.2 | 12.5 | 44.8 |
| U-Net 18 min, pixel argmax + connected components | 70.3 | 69.2 | 73.0 |
| + rectangle filling and soft majority vote | 73.4 | 69.9 | 81.5 |
| + flatness test for solid | 89.4 | 88.1 | 92.4 |
+ re-inversion test for invert (final, solution.py) | 93.0 | 92.0 | 95.5 |
Exact-count accuracy per class in the final model: noise 100 %, scramble 97.5 %, solid 92.5 %, invert 75 %. Invert is the remaining weak spot: missed small inversions and a few decoys the network never flagged.
Caveat. The two rules were chosen by looking at the errors on these 40 held-out images. They have no fitted threshold (a majority at 0.5 and a comparison ) and follow from how the glitches are defined, so the optimism should be small, but a fully clean estimate would need a second untouched split. The minimum blob area (60 pixels) makes no difference between 30 and 300.
What to remember
- Read the confusion matrix before training longer. It showed noise and scramble were solved and the whole loss was a solid/invert confusion plus decoys: a 20-point problem with a 20-line fix.
- Exploit the generator's structure. Rectangles that never touch turn segmentation into "find blobs, fill bounding boxes", and turn counting into counting blobs.
- Test physical definitions, not appearances. "Exactly constant" and "continuous after " are properties a network learns slowly but a few lines of NumPy check exactly. Comparing two hypotheses ( against ) avoids tuning a threshold.
- Metrics with "empty = 1" punish false positives hard. Rejecting the 26 decoy blobs was worth as much as finding the true ones.
- Use the network for what it is good at (textures: noise against scramble), and rules for what is exactly defined.