terrain-gate
Decides whether a 128x128 Minecraft map tile is vanilla terrain or something a player made. It is stage 1 of mapart-nsfw-detector: a cheap, high-recall filter that keeps an expensive content model from ever looking at auto-generated world maps.
It does not decide whether an image is safe. Terrain versus not-terrain is the whole question it answers. Treating its score as an NSFW score would be a category error.
What it is
Gradient boosting over 18 hand-built features, exported to ONNX. 311 KB, CPU only, with no GPU path and none needed.
| Property | Value |
|---|---|
| Input | one 128x128 RGBA tile, native map resolution, never resized |
| Output | p(not terrain) |
| Threshold | 0.0376, shipped in terrain_gate.meta.json |
| Latency | 2.23 ms/tile, one CPU thread |
| AUC | 0.9993, grouped 5-fold CV over 5 seeds |
The features are palette statistics rather than pixels: colour-transition rates, run lengths, entropy, and the group that earns its keep, which is how a tile steps between shades of one base colour versus between different base colours. Minecraft derives a pixel's shade from the height difference with the block to its north, so vanilla terrain steps within a colour as the ground rises, while a player dithering with wool steps across colours. Badlands and orange wool share exact RGB values, and nothing else separates them.
Feature order is read from terrain_gate.meta.json and never rebuilt in code. Renaming one
feature would otherwise permute every column while the scores still looked plausible.
Use
from mapart_nsfw.mapdata.image import load_rgba
from mapart_nsfw.terrain_gate import predict
rgba = load_rgba("tile.png")
predict.score(rgba) # p(not terrain)
predict.is_terrain(rgba) # score < threshold
The model ships inside the mapart-nsfw-detector wheel, so installing the package is all it
takes. It is not on PyPI yet, so install from the repository:
pip install git+https://github.com/hqkh4nh/mapart-nsfw-detector
Training data
8,045 tiles: 6,445 hand-labelled tiles from an earlier version of the KingMC server, plus
1,600 vanilla maps generated by Minecraft's own rendering code. Folds are grouped by
near-duplicate so two copies of one artwork never land on opposite sides. Art examples are
augmented 8x, and the shipped artifact records this as "variant": "B x8 (+1729 art)".
The server corpus is not distributed, because it holds content that players built. The vanilla half is public, in the GitHub repository.
Limits
- Recall-first by construction. The threshold sits at the lowest out-of-fold score any real art tile received, so it forwards more than it strictly must. Over its own corpus it forwards 7.4% of tiles, against 14.5% for the colour-only rule it replaced.
- JPEG breaks it. Re-encoding at quality 95 pushed 97% of pixels off-palette and flipped 183 of 200 tiles across the threshold. What JPEG destroys is the dither the model reads, so lossy input should bypass this stage entirely rather than be snapped and scored.
- One server's distribution. It was trained on one corpus. A server with a different build culture should retrain, and the repository ships the labelling tool for exactly that.
- Nether maps are noise, not terrain images. Vanilla generates them from a coordinate hash without reading a block. They are labelled terrain here because that is what they are, legitimate game output, but they look like nothing else in the set.
Sponsor
KingMC, a Vietnamese Minecraft server at kingmc.vn,
sponsors this project and is where the training corpus was collected. There is a
live demo of both stages
together.