wikillm

Paper · 2019-04-16 · cs.CV

Detecting the Unexpected via Image Resynthesis

Flags unexpected objects in street scenes without training on them. A segmenter labels the image, a pix2pixHD generator repaints it from that label map, and a discrepancy network marks pixels where the two images disagree, trained on random label swaps of known objects. Author-reported AUROC 0.79-0.93 on Lost and Found and a 60-image road set, above uncertainty and autoencoder baselines.

Anomaly detectionSemantic segmentationImage-to-image translationAdversarial robustnessanomaly-detectionsemantic-segmentationimage-resynthesisadversarial-attacks

arXivPDFCodeMarkdown12 min read

AI-compiled by claude-haiku-5-5 on 2026-10-19, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.

In plain words

A segmentation network labels every pixel with one of the classes it was trained on. When an object it never saw in training, such as an animal or a lost cargo box on the road, appears in the scene, the network tends to give it a wrong label instead of flagging it.

The idea is to check the labels by redrawing the scene. A generator paints a photo from the label map alone. Where the map is wrong, the painted picture looks different from the real photo, so comparing the two exposes the problem spots. A second network learns which differences matter. It never sees unknown objects during training: known objects are relabelled at random, and the generator paints the photo from those wrong labels.

The same comparison also helps spot adversarial attacks, because an attacked label map produces a badly distorted picture. The authors report that this beats methods based on prediction uncertainty and on autoencoder reconstruction errors.

TL;DR

  • The pipeline segments the image, resynthesizes it from the label map with a pix2pixHD generator, then compares the original and resynthesized images with a discrepancy network that also reads the labels §3 fig. 2 fig. 3.
  • The discrepancy network is trained on synthetic label swaps of known object instances, so it never needs unknown classes §3.2.
  • Author-reported AUROC is 0.79 to 0.93 across the reported settings. It is above the uncertainty and RBM baselines in every row of fig. 5, with false positives still present §4.2 §5.
  • The same comparison feeds a HOG-distance plus logistic-regression detector for adversarial attacks. It beats spatial consistency on Cityscapes and is on par on BDD100K §4.3 tab. 1.
  • The released code is GPL-3.0-or-later. This card describes it in our own words and cites paths only.

Problem

Standard semantic segmentation assumes every class seen at test time was also present in training. Road scenes break that assumption, with animals, snow heaps and lost cargo that have no label in standard datasets §1.

Two families of detectors were common before this work. Uncertainty-based methods flag low-confidence pixels, but the authors observed that uncertain regions often do not coincide with unknown objects §1 §2. Autoencoder-based methods flag poorly reconstructed regions, but autoencoders tend to learn generic compression of the input rather than content-specific structure §1 §2.

The paper assumes that a segmenter tends to emit spurious labels for unknown objects, so a resynthesis from the label map will look different there §1. It also assumes a generator trained on the target domain and a labelled training set for that domain (Cityscapes, downscaled) §3.2 §4.

Method

Inference pipeline

The pipeline has three stages §3 fig. 2:

  1. A semantic segmenter (the paper uses SegNet-style and PSP-style models) predicts a label map for the input image §3.
  2. A pix2pixHD generator resynthesizes an image from that label map alone §3.
  3. A discrepancy network takes the original image, the resynthesized image and the label map, and outputs a two-class score per pixel §3.1 fig. 3. The anomaly score is the softmax probability of the "discrepancy" class app. A experiments.py.
Pseudocode
# Per-pixel anomaly score for one image  [§3]  [fig. 2]
# Input:  image (3, H, W); segmenter; generator; discrepancy_net
# Output: anomaly_p (H, W), values in [0, 1]

def anomaly_score(image, segmenter, generator, discrepancy_net):
    # 1. Predict a semantic label map from the input image  [§3]
    labels = segmenter(image)                          # (H, W) class ids
    # 2. Resynthesize an image from the label map alone  [§3]
    resynth = generator(labels)                        # (3, H, W)
    # 3. Score every pixel for significant image discrepancy  [§3.1]
    logits = discrepancy_net(image, resynth, labels)   # (2, H, W)
    # 4. Use the softmax probability of the discrepancy class as the score
    #    [app. A] [code: src/a05_differences/experiments.py#L750-L752]
    anomaly_p = softmax(logits)[1]                     # (H, W)
    return anomaly_p

Discrepancy network

The network has three streams and a decoder §3.1 fig. 3 app. A:

  • Image streams. The original image and the resynthesized image each pass through a pre-trained VGG16 feature extractor with shared weights. Features are taken at four levels, with depths 64, 128, 256 and 512 §3.1 fig. 3 app. A.
  • Label stream. A custom CNN processes the one-hot label map at each level §3.1 app. A.
  • Fusion and correlation. At each level, the three feature streams are concatenated and passed through a 1x1 convolution to reduce channels. In parallel, pointwise correlations between the original and resynthesized features are computed. Both go to an up-convolution pyramid §3.1 fig. 3.
  • Decoder and output. The up-convolution blocks use 3x3 convolutions with SELU activations and 2x2 transposed convolutions with stride 2. The final layer is a 1x1 convolution to two outputs, followed by softmax app. A networks.py.
Pseudocode
# Discrepancy network forward pass  [§3.1]  [fig. 3]
# Input:  image, resynth (3, H, W); labels (H, W) class ids
# Output: logits (2, H, W), no-error and error channels

def discrepancy_net(image, resynth, labels):
    # 1. Extract VGG16 features of both images with shared weights  [§3.1]
    feats_img = vgg16(image)              # four levels, depths 64..512
    feats_resynth = vgg16(resynth)        # same levels, same weights
    # 2. Encode the one-hot label map at each level  [§3.1] [app. A]
    feats_sem = label_cnn(one_hot(labels))
    value = None
    for level in levels_coarse_to_fine:
        # 3. Correlate the original and resynthesized features pointwise  [§3.1]
        corr = pointwise_correlation(feats_img[level], feats_resynth[level])
        # 4. Fuse the three streams with a 1x1 convolution  [§3.1]
        fused = conv1x1(concat(feats_img[level], feats_resynth[level],
                               feats_sem[level]))
        # 5. Up-convolve, carrying the previous level's output forward  [§3.1]
        value = up_block(concat(corr, fused, value))
    # 6. Two-channel prediction: no error, error  [app. A]
    return final_conv1x1(value)           # (2, H, W)

The paper does not define the correlation operator beyond "pointwise correlation" §3.1. The released code computes a correlation between the two feature maps and concatenates it with the fused features networks.py.

Training with synthetic discrepancies

The discrepancy network never sees unknown objects. Instead, known object instances are relabelled to other known classes, and the generator repaints the image from the altered map §3.2 fig. 4:

  1. Pick a random subset of object instances §3.2.
  2. Give each chosen instance a different random class from the set of known classes §3.2.
  3. Resynthesize the image from the altered label map with the generator trained on the dataset of interest §3.2.
  4. Target: the pixels whose label changed transforms.py. Pairs of real and resynthesized images, with this target, train the discrepancy network §3.2.
Pseudocode
# One synthetic training example  [§3.2]  [fig. 4]
# Input:  image (3, H, W); labels (H, W); instances (H, W); generator
# Output: image, resynth, labels_fake, target (H, W) in {0, 1}

def make_training_example(image, labels, instances, generator):
    # 1. Choose a random subset of object instances to relabel  [§3.2]
    #    The released config uses half of them
    #    [code: src/a05_differences/experiments.py#L994]
    chosen = random_subset(object_instances(instances), fraction=0.5)
    # 2. Give each chosen instance a different random known class  [§3.2]
    #    [code: src/a05_differences/transforms.py#L238-L245]
    labels_fake = relabel(labels, instances, chosen)
    # 3. Repaint the image from the altered label map  [§3.2]
    resynth = generator(labels_fake)
    # 4. Target is the set of pixels whose label changed  [§3.2]
    #    [code: src/a05_differences/transforms.py#L289-L290]
    target = labels_fake != labels
    return image, resynth, labels_fake, target

Loss: per-pixel cross-entropy with class weights, Adam with learning rate 1e-4, 50 epochs app. A. The released configuration uses class weights of 1.45693524 for the no-error class and 19.18586532 for the error class experiments.py (see Paper vs code).

Adversarial attack detection

The same resynthesis is reused to detect adversarial examples §3.3 fig. 8:

  1. Segment the input, adversarial or not, and resynthesize the image from the predicted map §3.3.
  2. Compute HOG features on the input and on the resynthesized image §3.3.
  3. Use the L2 distance between the two HOG descriptors as a single feature §3.3.
  4. Train a logistic regression on these distances to predict whether the input is adversarial §3.3.

The paper argues that this detector is simple and non-differentiable, so it is harder to attack than a learned detector §3.3. Inference needs a single forward pass through the segmenter and generator, unlike the 50 patch pairs compared by spatial consistency §4.3.

Pseudocode
# Adversarial-input score from one image pair  [§3.3]
# Input:  image (3, H, W); resynth (3, H, W), from the predicted map
# Output: scalar distance; a logistic regressor maps it to a label

def attack_distance(image, resynth):
    # 1. Compute HOG descriptors of the input and of the resynthesis  [§3.3]
    #    HOG parameters are not stated in the paper.
    hog_input = hog(image)
    hog_resynth = hog(resynth)
    # 2. Return the L2 distance between the two descriptors  [§3.3]
    return l2_distance(hog_input, hog_resynth)

Hyperparameters

NameValueWhereNotes
Training resolution1024 x 512 (Cityscapes train set, downscaled)§4Reason given: GPU memory
Random training crop384 x 768experiments.pyCode default; not in the paper
OptimizerAdam, learning rate 1e-4app. A config.pyCode weight decay 0; betas not stated
LR scheduleReduceLROnPlateau, patience 5, minimum 1e-8config.py experiment.pyNot stated in the paper
Epochs50app. A experiments.py
Batch sizetrain 2, eval 3experiments.pyNot stated in the paper
Lossper-pixel cross-entropy with class weightsapp. A experiments.pyPaper cites a weighting scheme from [Paszke16] without values
Class weights1.45693524 (no error), 19.18586532 (error)experiments.pyNot in the paper
Swap fraction0.5 of object instancesexperiments.pyPaper says "randomly-chosen" §3.2
Swap target classesall 19 training classes except the originaltransforms.pyPaper says "known classes" §3.2
Image backboneVGG16, pre-trained§3.1 app. ADataset of pre-training not stated
Backbone trainingfrozen in the released comparatornetworks.py experiments.pyPaper does not say frozen
ActivationSELU in up-convolutionsapp. A
Label inputone-hot, 19 channelsapp. A networks.py
SegmentersBayesian SegNet and PSP Net, trained on BDD100K§4
Ensemble4 PSP models§4.1The released script trains 5 (see Paper vs code)
Dropout samples (Bayesian SegNet)not stated§4.1Uncertainty is the variance over samples
Generatorpix2pixHD, trained on the dataset of interest§3.2 §4Released config uses a "nostyle, crop" variant experiments.py
RBM baseline8 x 8 patches, stride 6, 20 hidden units§4.1 experiments.pyReleased code also normalizes patches locally networks.py
Lost and Found test set17 frames excluded§4.2.1ROI excludes the ego-vehicle
Road anomaly set60 images, resized to 1280 x 720§4.2.2Annotated with GrabCut-assisted manual labels
Attack budgetat most 200 iterations, L-infinity 0.05 per stepapp. BUsed for DAG and Houdini
Attack detector split80% train, 20% evaluationapp. BLogistic regression detectors
Spatial consistency patch256 x 256 on 1024 x 512 inputsapp. BBaseline setting
Attack evaluation set500 Cityscapes and 1000 BDD100K validation images§4.3

Results

All numbers are author-reported. Segmenters are Bayesian SegNet and PSP Net trained on BDD100K; the synthesizer and discrepancy network are trained on Cityscapes §4. Scores are AUROC over pixels §4 fig. 5.

SettingSegmenterOursOurs (labels only)Ours (resynthesis only)RBMUncertaintySource
Lost and Found, all pixels except ego-vehicleBayesian SegNet0.820.770.760.740.67 (Bayesian)fig. 5
Lost and Found, all pixels except ego-vehiclePSP Net0.820.790.760.740.67 (ensemble)fig. 5
Lost and Found, road-only regionBayesian SegNet0.920.820.850.860.70 (Bayesian)fig. 5
Lost and Found, road-only regionPSP Net0.930.830.860.860.57 (ensemble)fig. 5
Road Anomaly, 60 imagesBayesian SegNet0.790.780.780.590.70 (Bayesian)fig. 5
Road Anomaly, 60 imagesPSP Net0.830.780.800.590.67 (ensemble)fig. 5

The road-only region is the union of the obstacle and free-space annotations §4.2.1. The authors say the RBM baseline improves in that region, but the full method still outperforms it §4.2.1. The road anomaly set shows the RBM baseline most affected by road textures that differ from Cityscapes §4.2.2.

Attack detection, AUROC in percent tab. 1. Columns are the two attacks, each with a "Pure" target (one random label everywhere) and a "Shift" target (constant offset per pixel) §4.3:

DatasetSegmenterMethodDAG PureDAG ShiftHoudini PureHoudini Shift
CityscapesBayesian SegNetSC999810098
CityscapesBayesian SegNetOurs10010010098
CityscapesPSP NetSC989098100
CityscapesPSP NetOurs1009999100
BDD100KBayesian SegNetSC10010098100
BDD100KBayesian SegNetOurs989810090
BDD100KPSP NetSC9210096100
BDD100KPSP NetOurs100969895

Qualitative examples are in fig. 6 (Lost and Found) and fig. 7 (road anomaly set). Resynthesized images for adversarial inputs look very different from the input fig. 8 §4.3.

Paper vs code

Official repository: cvlab-epfl/detecting-the-unexpected, checked at commit e3caa0081633058ba6f34d2e8f8a4ca530bd67fd (2021-02-17). The lab is the authors' own (EPFL CVLab), and the README names the authors and the ICCV 2019 paper.

  • Swap fraction. The paper gives no fraction §3.2. The released configuration uses 0.5 experiments.py. The dataset notebook comments on 0.5 as default and shows 0.33 and 0.75 as alternatives Discrepancy_GenerateDataset.ipynb Discrepancy_GenerateDataset.ipynb.
  • Frozen VGG. The paper says "pre-trained VGG" §3.1. The released comparator builds its VGG extractor with freezing enabled networks.py experiments.py.
  • Class weights. The paper cites a weighting scheme from [Paszke16] but gives no values app. A. The released config hard-codes 1.45693524 and 19.18586532 experiments.py. A helper computes weights as 1 / log(1.02 + p) from class frequencies experiments.py. The hard-coded values are what the training config uses.
  • Training crop. The paper says training images are downscaled to 1024 x 512 §4. The training pipeline applies a random crop of 384 x 768 by default experiments.py.
  • Ignored pixels. Pixels outside the region of interest get the ignore label 255 before the loss experiments.py. The paper does not mention this.
  • Ensemble size. The paper says the PSP model was trained four times §4.1. The released script trains five members, indices 0 to 4 0121_PSPEns_BDD_train_all.sh.
  • RBM baseline. Patch size 8, stride 6 and 20 hidden units match §4.1 experiments.py experiments.py. The released network also normalizes patches locally by default networks.py. The paper does not mention this.
  • Attack detection. The released repository has no attack code and no HOG or logistic-regression code. A search of src/ for HOG, logistic regression, Houdini and DAG found no matches. The adversarial results cannot be reproduced from this repository alone.
  • Generator. The released config uses a "nostyle, crop" pix2pixHD variant experiments.py. The paper says only that a pix2pixHD generator is trained on the dataset §3.2. The pix2pixHD and pytorch-semantic-segmentation code are git submodules with other licenses and are not part of the repository LICENSE.md.

Pitfalls

  • Unstated constants. The class-weight constant, the dropout sample count for the Bayesian SegNet baseline, the HOG parameters, and the exact correlation operator are not stated §3.1 §3.3 §4.1. Take the released values only as reference, and report them as code-derived.
  • Swapping details. The paper says "randomly-chosen object instances" without a fraction §3.2. The released code also skips an instance whose most common label is the ignore class, and draws the new class from all 19 classes except the original transforms.py. Those choices change the training distribution.
  • Generator dependency. The discrepancy network is trained on repaintings from a generator trained on the same dataset §3.2. For BDD100K attacks, the Cityscapes-trained generator is used on other images §4.3. A generator that does not match the target images could produce false discrepancies. The paper does not measure this effect; the authors name false positives as the main remaining issue §5.
  • Class-weight sensitivity. The error class weight of about 19 is large experiments.py. The weights come from class statistics of the training data experiments.py, so a new dataset needs its own values.
  • Costs. Each frame needs a segmenter pass, a generator pass, and two VGG16 passes. The paper gives no runtime figures and does not state the test resolution of the generator. Attack detection needs a single pass §4.3.
  • Known failure modes. An unknown object labelled as a visually similar known class, such as an animal labelled as a person, can be missed by the discrepancy network. The system can still flag it through the label map app. A. Known objects with unusual appearance can be flagged as unexpected app. A.
  • False positives. The authors state that false positives remain and that reducing them is future work §5. Some flagged objects are valid unexpected objects that the dataset did not annotate §4.2.1.
  • Evaluation details. Lost and Found excludes 17 frames §4.2.1. The road anomaly images are rescaled to 1280 x 720 §4.2.2, but the paper does not say at what resolution the segmenter and discrepancy network process them.
  • Licence. The released code is GPL-3.0-or-later LICENSE.md. This card cites paths and line numbers and paraphrases the logic. A verbatim copy would carry copyleft obligations.

Lineage

  • Builds on: pix2pixHD (Wang et al., 2018, cited as Wang18c), a generative network that maps semantic maps back to images §1, used as the resynthesis generator §3; the feature-correlation idea from co-segmentation by Li, Jafari and Rother (2018) §3.1; VGG16 features §3.1; SegNet and PSP Net as segmenters §3.
  • Compared against: Bayesian SegNet with dropout uncertainty (Kendall et al., 2015) and PSP ensembles §4.1; the RBM road-reconstruction method (Creusot and Munawar, 2015) §4.1; spatial consistency for attack detection (Xiao et al., 2018) §4.3.
  • Superseded by: not stated.

Source and license

Adapted from “Detecting the Unexpected via Image Resynthesis” by Krzysztof Lis, Krishna Nakka, Pascal Fua, Mathieu Salzmann (arXiv:1904.07595), used under CC BY-SA 4.0. Changes: condensed and restructured for implementation, with tables, equations and algorithms taken from the paper and its code, each cited to its source. Not affiliated with or endorsed by the authors. This page is licensed under CC BY-SA 4.0. Code citations refer to https://github.com/cvlab-epfl/detecting-the-unexpected at commit e3caa0081633 (GPL-3.0-or-later). Any quoted code is shown under that license.