CataNET-Terrain-v1.0-Large
A dual-head MobileNetV3-Large classifier that reads a single, top-down-rectified Catan hex tile patch and predicts both its terrain type (6 classes) and resource number token (13-way head; 12 real values plus a placeholder slot) in one forward pass. This is the second stage of the CataNET pipeline, consuming patches extracted by the CataNET-Meridian pose model.
This is the larger of the two CataNET-Terrain variants β see CataNET-Terrain-v1.0-Small for a smaller/faster backbone.
Table of Contents
- Model Details
- Uses
- Limitations
- Training Details
- Evaluation
- How to Get Started with the Model
- Citation
- Model Card Contact
Model Details
Model Description
CataNET-Terrain-v1.0-Large is a dual-head classifier built on the mobilenet_v3_large architecture (via torchvision), trained from scratch β not fine-tuned from ImageNet or any other pretrained checkpoint. The shared MobileNetV3 feature extractor feeds two independent classification heads:
- Terrain head: 6 classes β
desert,fields,forest,hills,mountains,pasture - Token head: 13 classes β
none,robber,2-6,7(unused placeholder, never a valid label),8-12
Input is expected to be a single 224Γ224 top-down-rectified crop of one hex tile (i.e. already isolated and perspective-corrected β not a full board photo).
- Developed by: nithinmanoj10
- Model type: Multi-task (dual-head) image classification
- License: MIT
- Finetuned from model: None β trained from randomly-initialized
mobilenet_v3_largeweights, no pretrained backbone.
Uses
Direct Use
Classifies a single, pre-cropped, top-down hex patch. Not intended to be run on a raw board photo directly β patches must first be extracted and perspective-rectified by an upstream pose/detection stage (see CataNET-Meridian). See How to Get Started, and note the required inference-mode workaround in Limitations before integrating this model.
Downstream Use
Intended as the terrain/token classification stage of the full CataNET pipeline: pose detection β per-hex patch extraction (homography-based rectification) β this model β structured board-state assembly, alongside a separate piece-color classifier for settlements/cities/roads.
Out-of-Scope Use
- Not intended for anything other than the standard 19-hex Settlers of Catan base game board's terrain/token vocabulary.
- Not a general-purpose texture or material classifier.
- Does not detect hex position/geometry (see CataNET-Meridian) or player pieces (see the companion Lynx MicroCNN piece-color classifier).
Limitations
- Requires
model.train()at inference time, notmodel.eval()β this is intentional, not an oversight, but it's easy to get wrong. The checkpoint was validated (and is used in production) with the model left in training mode (onlynn.Dropoutlayers forced to.eval()), because BatchNorm behaves incorrectly with this checkpoint under standard.eval()mode. Critically, this means BatchNorm uses per-batch statistics β running inference with a batch size of 1 will produce degenerate, near-constant output (in internal testing, this collapsed predictions to a single class regardless of input). Always run inference on a real batch of multiple patches at once (e.g. all hexes from one board), never a lone single-image batch. - Token accuracy is substantially better than raw numbers alone suggest, but check your ground truth. In this model's own evaluation, roughly 20% of real-photo instances had to be excluded from token scoring because the token was cropped/illegible in that specific patch β not because of a model failure. If you're benchmarking this model yourself, budget for the same issue.
- The
robberclass is a known, consistent weak point. In evaluation, robber-occupied tiles were misclassified as "no token" (none) in every single tested instance (0/6). This is likely a sim-to-real gap: the synthetic training data's robber piece asset probably doesn't closely resemble how a physical robber piece looks in a real photo. forest/mountainsterrain and2/3tokens are the next-weakest classes β see Evaluation for the specific confusion pattern.- Trained entirely on synthetic data, composited from procedurally-generated terrain patches β a sim-to-real gap should be assumed beyond what's captured in the (small) real-photo evaluation set below.
Training Details
Training Data
Trained on the terrain-patch portion of nithinmanoj10/CatanSynth-Terrain-v1-50K β procedurally-generated, pre-rectified 224Γ224 hex tile crops, each labeled with its ground-truth terrain and token (the label is encoded directly in each file's name and mirrored in a labels.csv). See the dataset card for exact split sizes and class distribution.
Training Procedure
Preprocessing
Inputs are normalized with dataset-specific (not ImageNet) statistics β use these exact values, not the standard ImageNet mean/std:
MEAN = [0.7209611535072327, 0.5950123071670532, 0.4485519230365753]
STD = [0.2021966278553009, 0.20071130990982056, 0.19387811422348022]
Training hyperparameters
| Setting | Value |
|---|---|
| Backbone | mobilenet_v3_large (torchvision), trained from scratch |
| Batch size | 512 |
| Epochs | 80 (early stopping patience: 7, on combined validation accuracy) |
| Optimizer | AdamW, lr=2e-3, weight decay 1e-4 |
| LR schedule | Linear warmup (5 epochs) β cosine annealing |
| Loss | Weighted sum of both heads' cross-entropy: 0.25 * terrain_loss + 0.75 * token_loss (token reading treated as the harder task) |
| Sampling | WeightedRandomSampler, combining inverse-frequency weights for both terrain and token, to counter real class imbalance (e.g. desert/robber are rare relative to numbered tiles) |
Evaluation
Testing Data
Evaluated on 120 real hex-tile patches, extracted (via ground-truth keypoints) from the same 12 real Catan board photographs used to evaluate the CataNET-Meridian pose models, hand-labeled for terrain type and resource token. Labels went through a correction pass after an initial labeling error was caught (menu-position mixups specifically affecting low-value tokens) and were re-verified against the raw images before this evaluation was finalized.
Metrics
- Terrain / Token accuracy β top-1 accuracy per head.
- Terrain / Token macro-F1 β unweighted mean of per-class F1, chosen specifically because both heads have real class imbalance (see Training Procedure) that raw accuracy alone would mask.
- Joint accuracy β fraction of patches where both heads are simultaneously correct.
Results
| Metric | Value |
|---|---|
| Terrain accuracy | 0.692 |
| Terrain macro-F1 | 0.720 |
| Token accuracy* | 0.802 |
| Token macro-F1* | 0.609 |
| Joint accuracy (both correct)* | 0.646 |
* Computed on 96/120 patches; 24 were excluded where the number token was cropped/illegible in that specific patch (not scoreable against any model).
Summary
desert and pasture terrain, and tokens 8-12, are classified reliably. The model confuses forestβmountains terrain, 3β2 tokens, and never correctly identifies a robber-occupied tile (0/6) β see Limitations. N=120 patches / 12 photos, selected for clean detections rather than randomly sampled β sufficient to characterize behavior, not a large-scale benchmark.
How to Get Started with the Model
import torch
import torch.nn as nn
from huggingface_hub import hf_hub_download
from torchvision.models import mobilenet_v3_large
from torchvision import transforms
from PIL import Image
class CatanEnvironmentNet(nn.Module):
def __init__(self, num_terrains=6, num_tokens=13):
super().__init__()
backbone = mobilenet_v3_large(weights=None)
self.features = backbone.features
self.pool = backbone.avgpool
feature_dim = 960
self.terrain_head = nn.Sequential(
nn.Flatten(), nn.Linear(feature_dim, 128), nn.Hardswish(), nn.Dropout(0.2), nn.Linear(128, num_terrains)
)
self.token_head = nn.Sequential(
nn.Flatten(), nn.Linear(feature_dim, 256), nn.Hardswish(), nn.Dropout(0.2), nn.Linear(256, num_tokens)
)
def forward(self, x):
x = self.features(x)
x = self.pool(x)
return self.terrain_head(x), self.token_head(x)
weights_path = hf_hub_download(repo_id="nithinmanoj10/CataNET-Terrain-v1.0-Large", filename="best.pth")
model = CatanEnvironmentNet()
model.load_state_dict(torch.load(weights_path, map_location="cpu"))
# IMPORTANT: see Limitations -- .train() (not .eval()), only Dropout forced to eval,
# and ALWAYS run on a real batch (not a single image) or BatchNorm output collapses.
model.train()
for m in model.modules():
if isinstance(m, nn.Dropout):
m.eval()
preprocess = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize(
mean=[0.7209611535072327, 0.5950123071670532, 0.4485519230365753],
std=[0.2021966278553009, 0.20071130990982056, 0.19387811422348022],
),
])
TERRAIN_CLASSES = ["desert", "fields", "forest", "hills", "mountains", "pasture"]
TOKEN_CLASSES = ["none", "robber", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"]
patches = [preprocess(Image.open(p).convert("RGB")) for p in ["hex_0.jpg", "hex_1.jpg", "..."]] # a real batch, not one image
batch = torch.stack(patches)
with torch.no_grad():
terrain_logits, token_logits = model(batch)
terrains = [TERRAIN_CLASSES[i] for i in terrain_logits.argmax(1).tolist()]
tokens = [TOKEN_CLASSES[i] for i in token_logits.argmax(1).tolist()]
Citation
No formal paper accompanies this model. If referencing it, please cite the repository directly:
@misc{catanet-terrain-large,
author = {nithinmanoj10},
title = {CataNET-Terrain-v1.0-Large},
year = {2026},
publisher = {Hugging Face},
howpublished = {\url{https://huggingface.co/nithinmanoj10/CataNET-Terrain-v1.0-Large}}
}
Model Card Contact
nithinmanoj10 via Hugging Face.
- Downloads last month
- 23
Model tree for nithinmanoj10/CataNET-Terrain-v1.0-Large
Base model
timm/tf_mobilenetv3_large_100.in1kDataset used to train nithinmanoj10/CataNET-Terrain-v1.0-Large
Collection including nithinmanoj10/CataNET-Terrain-v1.0-Large
Evaluation results
- Terrain accuracyself-reported0.692
- Terrain macro-F1self-reported0.720
- Token accuracyself-reported0.802
- Token macro-F1self-reported0.609
- Joint accuracy (terrain + token both correct)self-reported0.646