🧩 PixCon

July 7, 2026 · View on GitHub

🧩 PixCon

Clean-Positive Contrastive Learning for Foundation-Model Semi-Supervised Segmentation

arXiv Project Page HF Demo HF Models License Python Stars

📄 Paper · 🌐 Project page · 🤗 Live demo · ⬇️ Weights

The bottleneck moved: at foundation-model strength, accuracy lives in how the embedding is structured by class, not in which pseudo-labels you filter.

PixCon architecture: DINOv2-B encoder + DPT-lite decoder with a clean-positive pixel-contrastive branch PixCon before/after on a Cityscapes scene

Before → after wipe (interactive slider on the project page).

TL;DR

With a DINOv2 teacher, a strict confidence threshold already retains a measured ~98%-clean pseudo-label set, so the accuracy that remains lives in how the embedding space is structured by class, not in the filter. PixCon adds a single clean-positive pixel-contrastive branch on a UniMatch V2 consistency backbone: a per-class memory bank that admits only labeled pixels the student already classifies correctly, giving a contamination-free positive set (ρ_F = 0) by construction.

  • No inference-time parameters and no bank-specific threshold — a single switch over the backbone
  • Matches the strongest published SSSS result (UniMatch V2-B) and beats our compute-matched UniMatch V2 reproduction on Pascal & ADE20K
  • Far above pre-foundation-model methods (~9–18 mIoU) on all three benchmarks
  • Measured pseudo-label contamination ρ_F = 0.018 (Pascal) / 0.106 (ADE20K), not assumed

🏆 Leaderboards (1/8 split, mIoU %)

Pascal VOC
MethodBackbonemIoU
UniMatch V2-B (published)DINOv2-B87.9
PixCon (ours)DINOv2-B87.90
UniMatch V2 (our repro)DINOv2-B87.01
SemiVLCLIP-B85.6
— foundation ↑ / specialised ↓ —
BeyondPixelsRN-10178.6
PrevMatchRN-10178.5
AllSparkMiT-B578.4
UniMatchRN-10177.2
AugSegRN-10175.5
PS-MTRN-10169.6
Cityscapes
MethodBackbonemIoU
UniMatch V2-B (published)DINOv2-B84.3
UniMatch V2 (our repro)DINOv2-B83.96
PixCon (ours)DINOv2-B83.88
SemiVLCLIP-B79.4
— foundation ↑ / specialised ↓ —
BeyondPixelsRN-10179.2
CorrMatchRN-10178.5
UniMatchRN-10177.9
AugSegRN-10177.8
ADE20K
MethodBackbonemIoU
UniMatch V2-B (published)DINOv2-B49.8
PixCon (ours)DINOv2-B49.23
UniMatch V2 (our repro)DINOv2-B49.10
SemiVLCLIP-B39.4
— foundation ↑ / specialised ↓ —
UniMatchCLIP-B38.0
UniMatchRN-10134.6

PixCon and the "our repro" baseline differ by exactly one switch (the clean-positive branch), so any margin is attributable to it. Pascal is a 3-seed mean; Cityscapes/ADE20K are single-seed.

🎬 More qualitative results

PixCon on Pascal VOC
Pascal VOC · 21 classes
PixCon on ADE20K
ADE20K · 150 classes

Method

ComponentSetting
BackboneDINOv2-Base (ViT-B/14), fine-tuned end-to-end
DecoderDPT-lite (4 ViT layers → coarse-to-fine pyramid)
ConsistencyTwo strong+CutMix views, complementary channel dropout (UniMatch V2)
ThresholdFixed conf ≥ 0.95
AuxiliaryPixCon: 1×1 projection head, per-class memory bank (256/class), clean-positive filter (labeled ∧ pred==GT), supervised InfoNCE (τ = 0.1)
OptimizerAdamW, backbone LR 5e-6, decoder LR 2e-4, poly schedule

The admission rule: a labeled pixel is stored as a positive only if the student already predicts it correctly — so every stored positive is verifiably clean (ρ_F = 0). A first-order analysis of the supervised-InfoNCE gradient shows the false-positive term scales as ρ_F/(1−ρ_F); we measure ρ_F rather than assume it.

Install

pip install -r requirements.txt

Train

python train.py \
  --config configs/pascal.yaml \
  --data-root /path/to/pascal \
  --labeled-id-path splits/pascal/1_8/split_0/labeled.txt \
  --unlabeled-id-path splits/pascal/1_8/split_0/unlabeled.txt \
  --save-path exp/pascal/1_8/seed0 --seed 0

# UniMatch V2 baseline (no PixCon): add --no-pixcon
# Decomposition ablation: --pixcon-bank-filter {clean,labeled,conf}

Configs for configs/{pascal,cityscapes,ade20k}.yaml. Resume with --resume <save_path>/latest.pth.

Inference

import torch
from model.segmentor import PixConSegmentor
from core.inference import whole_inference

model = PixConSegmentor(backbone='dinov2_vitb14', nclass=21, pretrained=False).eval()
sd = torch.load('pixcon_pascal_1_8.pth', map_location='cpu')   # from the HF model repo
model.load_state_dict(sd, strict=False)                        # proj_head is inference-unused

Pretrained weights: Pascal VOC · Cityscapes · ADE20K. Run the browser demo locally from demo/ or online on 🤗 Spaces.

Tests

python -m pytest tests/ -q   # CPU-only smoke tests

Citation

@article{tarubinga2026pixcon,
  title   = {PixCon: Clean-Positive Contrastive Learning for Foundation-Model
             Semi-Supervised Segmentation},
  author  = {Tarubinga, Ebenezer},
  journal = {arXiv preprint arXiv:2607.03068},
  year    = {2026}
}

License

Apache-2.0. DINOv2 weights are loaded from facebookresearch/dinov2 via torch.hub.

If PixCon is useful, please ⭐ the repo — it helps others find it.