[WACV 2026] FlowCLAS: Enhancing Normalizing Flow-Based Anomaly Segmentation Via Contrastive Learning
Official repository for FlowCLAS.
All commands below assume the working directory is src/ (where trainer_cli.py lives).
The image is defined under docker/. It uses a multi-stage build (CUDA 12.8, PyTorch 2.7, MMCV, SAM2, and project Python dependencies). GPU access at runtime requires the NVIDIA Container Toolkit.
Build (from the repository root):
bash scripts/docker_build.bashEquivalent manual command (paths are relative to the repo root; the build context must be the docker/ directory):
RENDER_GID=$(getent group render | cut -d: -f3)
docker build \
--network=host \
--build-arg "USERNAME=$(whoami)" \
--build-arg CREATE_USER=true \
--build-arg "WORKDIR_PATH=/home/$(whoami)" \
--build-arg "RENDER_GID=${RENDER_GID}" \
-t flowclas \
-f docker/Dockerfile \
docker| Flag / argument | Purpose |
|---|---|
--network=host |
Uses the host network during build (helps reach package indexes). |
CREATE_USER=true |
Creates a container user matching your host username (for volume permissions). |
WORKDIR_PATH |
Sets the container working directory to your home path inside the image. |
RENDER_GID |
Adds the container user to a render group with the host’s GID (for GPU/GL access). |
The image is tagged flowclas. To start an interactive container with project directories mounted, run from the repository root:
bash scripts/docker_run.bash /path/to/your/datasetsPaths in the YAML configs below are relative to src/ (for example, ../data/cityscapes → ~/data/cityscapes inside the container).
- Register and download the gtFine split (and matching leftImg8bit images) from the Cityscapes dataset.
- (Recommended) Preprocess labels to the common 19-class
trainIdconvention using cityscapesScripts — install the package, then runcreateTrainIdLabelImgs.pyon your download. FlowCLAS remaps rawlabelIdsto 19 training classes in code; generating*_gtFine_labelTrainIds.pngkeeps your tree aligned with the standard Cityscapes tooling. - Set
city_rootinsrc/configs/base/cityscapes_coco.yamlto the dataset root that containsleftImg8bit/andgtFine/(Torchvision layout).
Expected layout:
cityscapes/
├── leftImg8bit/
│ ├── train/<city>/*.png
│ └── val/<city>/*.png
└── gtFine/
├── train/<city>/*_gtFine_labelIds.png
└── val/<city>/*_gtFine_labelIds.png
A new version of the ALLO dataset will be released soon. For the current benchmark, set allo_root, train_dir, and test_dir in src/configs/base/allo_coco.yaml.
COCO images are used as an OoD proxy during training (paste/mix with in-distribution scenes). You can either prepare them yourself or use the preprocessed archive on Google Drive (same folder as Fishyscapes and Road Anomaly).
Option A — prepare from scratch (Meta-OoD):
- Download COCO 2017 train images and instance annotations.
- Run
preparation/prepare_coco_segmentation.pyto build binaryood_segmasks for images without instances that overlap Cityscapes train classes.
Option B — download preprocessed data from the Google Drive folder and extract under your coco_root.
In both cases, set coco_root in the data config you use (cityscapes_coco.yaml and/or allo_coco.yaml).
Expected layout:
coco/
├── annotations/
│ ├── instances_train2017.json
│ └── ood_seg_train2017/ # optional; masks may also live at repo root
├── ood_seg_train2017/*.png # required by FlowCLAS loaders
└── train2017/*.jpg
You can override any root on the CLI, for example:
--data.init_args.city_root /path/to/cityscapes \
--data.init_args.coco_root /path/to/cocoPreprocessed Fishyscapes, Road Anomaly, and COCO (see above) are available on Google Drive. After extracting, point the paths in cityscapes_coco.yaml:
| Config key | Role |
|---|---|
fishy_root |
Fishyscapes Lost & Found split (images/, labels/) |
roadanomaly_root |
Road Anomaly frames and semantic labels |
smiyc_root |
Segment Me If You Can (AnomalyTrack + ObstacleTrack) |
Expected layouts (as used by this repo):
fishyscapes/
├── LostAndFound/
│ ├── images/*.png
│ └── labels/*.png
└── Static/
├── images/*.png
└── labels/*.png
RoadAnomaly/
└── frames/
├── <clip>.jpg
└── <clip>.labels/
└── labels_semantic.png
smiyc/
├── dataset_AnomalyTrack/
│ ├── images/
│ └── labels_masks/
└── dataset_ObstacleTrack/
├── images/
└── labels_masks/
Evaluation uses fishy_root for Fishyscapes (default: ../data/fishyscapes/LostAndFound), plus smiyc_root and roadanomaly_root for the other test benchmarks in the Cityscapes training pipeline.
Pre-trained backbones, SAM2, and FlowCLAS checkpoints are available on Google Drive. After downloading, place files under the repo root as follows (paths are relative to src/ in the configs):
| File | Destination |
|---|---|
rein_dinov2l_allo.pth |
weights/rein_dinov2l_allo.pth |
rein_dinov2l_city.pth |
weights/rein_dinov2l_city.pth |
sam2.1_hiera_large.pt |
misc/weights/sam2.1_hiera_large.pt |
best_allo.ckpt |
weights/flowclas/best_allo.ckpt |
best_cityscapes.ckpt |
weights/flowclas/best_cityscapes.ckpt |
Training reads backbone_ckpt from flowclas_allo.yaml / flowclas_city.yaml; inference passes --ckpt to a FlowCLAS checkpoint under weights/flowclas/.
Pre-trained checkpoints and pixel-level metrics on the official test splits (checkpoints from Google Drive):
| Benchmark | Data config | Model config | Checkpoint | Pixel AP | Pixel FPR95 |
|---|---|---|---|---|---|
| ALLO | config | config | best_allo.ckpt |
88.4 | 6.6 |
| Fishyscapes | config | config | best_cityscapes.ckpt |
88.8 | 0.7 |
| Road Anomaly | config | config | best_cityscapes.ckpt |
93.0 | 3.3 |
Fishyscapes and Road Anomaly are evaluated with the same Cityscapes-trained best_cityscapes.ckpt; use the inference commands in Road Anomaly Segmentation below.
Replace <num_devices>, <num_workers>, and <seed> with values for your setup.
python3 trainer_cli.py fit \
--data configs/base/allo_coco.yaml \
-c configs/base/base.yaml \
--trainer.devices <num_devices> \
--data.num_workers <num_workers> \
-c configs/flowclas/flowclas_allo.yaml \
--seed_everything <seed> \
--experiment.name flowclas_allo \
--experiment.project_name ALLOpython3 trainer_cli.py fit \
--data configs/base/cityscapes_coco.yaml \
-c configs/base/base.yaml \
--trainer.devices <num_devices> \
--data.num_workers <num_workers> \
-c configs/flowclas/flowclas_city.yaml \
--seed_everything <seed> \
--experiment.name flowclas_city \
--metrics_on_cpu \
--no_sigmoidPaper-style runs using alternate experiment configs (same flag set as above):
ALLO
python3 trainer_cli.py fit \
--data configs/base/allo_coco.yaml \
-c configs/base/base.yaml \
--trainer.devices 4 \
--data.num_workers 8 \
-c configs/flowclas/flowclas_allo.yaml \
--seed_everything 42 \
--experiment.name flowclas_allo \
--experiment.project_name flowclas_ALLOCityscapes
python3 trainer_cli.py fit \
--data configs/base/cityscapes_coco.yaml \
-c configs/base/base.yaml \
--trainer.devices 2 \
--data.num_workers 8 \
-c configs/flowclas/flowclas_city.yaml \
--seed_everything 42 \
--experiment.name flowclas_city \
--experiment.project_name flowclas_Cityscapes \
--metrics_on_cpu \
--no_sigmoidReplace <num_devices>, <num_workers>, <seed>, and <ckpt> with values for your setup.
python3 trainer_cli.py test \
--data configs/base/allo_coco.yaml \
-c configs/base/base.yaml \
--trainer.devices <num_devices> \
--data.num_workers <num_workers> \
-c configs/flowclas/flowclas_allo.yaml \
--seed_everything <seed> \
--experiment.name flowclas_allo \
--data.eval_batch_size 2 \
--experiment.logger [csv] \
--trainer.precision 32 \
--ckpt <ckpt>python3 trainer_cli.py test \
--data configs/base/cityscapes_coco.yaml \
-c configs/base/base.yaml \
--trainer.devices <num_devices> \
--data.num_workers <num_workers> \
-c configs/flowclas/flowclas_city.yaml \
--seed_everything <seed> \
--experiment.name flowclas_city \
--data.eval_batch_size 2 \
--metrics_on_cpu \
--no_sigmoid \
--experiment.logger [csv] \
--trainer.precision 32 \
--ckpt <ckpt>Download checkpoints from Google Drive first (see Weights and checkpoints). Example inference commands (same flag set as above):
ALLO
python3 trainer_cli.py test \
--data configs/base/allo_coco.yaml \
-c configs/base/base.yaml \
--trainer.devices 1 \
--data.num_workers 8 \
-c configs/flowclas/flowclas_allo.yaml \
--seed_everything 42 \
--experiment.name flowclas_allo_test \
--data.eval_batch_size 2 \
--experiment.logger [csv] \
--trainer.precision 32 \
--ckpt ../weights/flowclas/best_allo.ckptRoad Anomaly Segmentation (Fishyscapes & Road Anomaly)
python3 trainer_cli.py test \
--data configs/base/cityscapes_coco.yaml \
-c configs/base/base.yaml \
--trainer.devices 1 \
--data.num_workers 1 \
-c configs/flowclas/flowclas_city.yaml \
--seed_everything 42 \
--experiment.name flowclas_city_test \
--data.eval_batch_size 2 \
--metrics_on_cpu \
--no_sigmoid \
--experiment.logger [csv] \
--trainer.precision 32 \
--ckpt ../weights/flowclas/best_cityscapes.ckptIf you use FlowCLAS in your research, please cite:
@inproceedings{lee2026flowclas,
title={FlowCLAS: Enhancing Normalizing Flow-Based Anomaly Segmentation Via Contrastive Learning},
author={Lee, Chang Won and Leveugle, Selina and Grouchy, Paul and Langley, Chris and Stolpner, Svetlana and Kelly, Jonathan and Waslander, Steven L},
booktitle={Proceedings of the IEEE/CVF Winter Conference on Applications of Computer Vision},
pages={6998--7007},
year={2026}
}