Paper · 2022-02-26 · cs.CV
An End-to-End Transformer Model for Crowd Localization
CLTR casts crowd localization as direct set prediction: a ResNet-50 feature map (stride 32) feeds a 6+6-layer transformer with 500 learned queries that output head points and confidences. Training matches predictions with a Hungarian matcher plus a kNN-context cost (KMO). Author-reported: best F1 on UCF-QNRF (80.97) and ShanghaiTech Part A at sigma=4 (43.2); NWPU test F1 68.5, behind TopoCount.
Direct set predictionBipartite matching for set-prediction lossesObject queries (learned query slots in transformer decoders)Focal losscrowd-localizationcrowd-countingtransformerpoint-regression
arXivPDFCodeMarkdown13 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
Counting people in a crowd photo is easier than saying where each head is. This paper, CLTR, predicts the list of head positions directly. A convolutional network builds a feature map, a transformer reads it, and a fixed set of learned query slots each output one point and a confidence score. No boxes, density maps or peak-finding step are needed.
The hard part is training. Each prediction has to be paired with one real head, and in a dense crowd the simple distance rule often pairs the wrong ones. The authors add a term that looks at each head's nearby neighbours, so the pairing respects the local layout. The approach is trained end to end, and the authors report gains on several crowd datasets.
TL;DR
CLTR is a DETR-style model for crowd localization that regresses head coordinates directly, with no pseudo boxes, density maps or non-maximum suppression §1. Training uses a one-to-one Hungarian matcher whose cost adds the L1 distance between each head's mean distance to its k nearest neighbours (the KMO term) §3.3. The paper reports the best F1 on UCF-QNRF and ShanghaiTech Part A (σ=4), and a close second on NWPU-Crowd test tab. 1 tab. 2 tab. 3. All numbers are author-reported.
Problem
Crowd localization means predicting the centre of every head in an image. Datasets usually give only point labels, and box labels are expensive in dense scenes §1. Map-based methods predict density-like maps and need a non-differentiable peak-finding step and multi-scale features §1 §2.2. Detection-based methods rely on pseudo boxes and NMS §2.1. Plain DETR applied to crowds performs poorly, because its L1-based Hungarian matcher has no context and dense heads have similar confidences, which produces ambiguous matches §1 fig. 1.
Method
The model has four parts: a CNN backbone, a transformer encoder, a transformer decoder with learned queries, and two heads (points and classes). Training adds a KMO-based matcher and a loss §3 fig. 2.
- Backbone. A CNN produces a single-scale feature map at 1/32 of the input resolution, with no feature aggregation §3 §4.1. The paper uses ResNet-50 §4.1.
- Encoder input. A 1×1 convolution reduces the channels to c=256, the map is flattened to a sequence, and a position embedding is added §3.1.
- Encoder. Six layers, each with multi-head self-attention (8 heads)
and a feed-forward layer §3.1 §4.1 eq. 1. The attention is
softmax(QKᵀ/√c)V, with the heads concatenated and projected eq. 1 §3.1. - Decoder. Six layers, each with self-attention, cross-attention and a feed-forward layer §3.2 §4.1. The N=500 learned queries Q_h act as the decoder's object queries §4.1. The cross-attention uses the conditional form, where the queries are the learned embeddings concatenated with the content query, following Conditional DETR §3.2.
- Heads. Each decoded embedding gives a point (coordinates) through a regression head and a confidence through a classification head §3 fig. 2.
- Matching (training only). Predictions are matched one-to-one with the ground-truth points by the Hungarian algorithm. The cost is the L1 distance between points plus the L1 distance between kNN-context values minus the predicted confidence §3.3 eq. 2 eq. 3. The paper says the kNN value of each predicted point is output by the network §3.3. Unmatched predictions are background §3.3.
- Loss. Point regression is an L1 loss on the matched coordinates,
normalised to [0,1] §3.4 eq. 4. Classification is a focal loss over
all predictions. The total is
L = L_cls + λ·L_locwith λ=2.5, and both terms are normalised by the number of instances in the batch §3.4 eq. 5. - Inference. Each image is split into non-overlapping patches of the training crop size, zero-padded at the border. Predictions with confidence below 0.35 are removed. The count is the number of remaining points §4.1 §4.3.
The matcher's kNN value for each ground-truth point is the mean distance to its k nearest neighbours. The paper sets k=4 §4.1 eq. 3. The ground-truth kNN value is computed from the ground-truth points themselves §3.3 eq. 3.
# KMO-based Hungarian matching [§3.3] [eq. 2] [eq. 3]
# Input: pred_points (N, 2), pred_conf (N,), pred_knn (N,)
# Input: gt_points (M, 2) with M <= N, gt_knn (M,)
# Output: one prediction index per ground-truth point
def kmo_match(pred_points, pred_conf, pred_knn, gt_points, gt_knn):
# L1 distance between coordinates, for each (gt, pred) pair [eq. 3]
coord_cost = l1_distance(gt_points, pred_points) # (M, N)
# L1 distance between kNN-context values [eq. 3]
knn_cost = l1_distance(gt_knn, pred_knn) # (M, N)
# Subtract the predicted confidence of each prediction [eq. 2] [eq. 3]
cost = coord_cost + knn_cost - pred_conf[None, :] # (M, N)
# One-to-one assignment with the Hungarian algorithm [§3.3]
return hungarian(cost)# Training loss [§3.4] [eq. 4] [eq. 5]
# Matched pairs come from kmo_match above.
def training_loss(pred_points, pred_conf, gt_points, match, labels):
# L1 regression on the matched coordinates only [eq. 4]
loc = l1_loss(pred_points[match], gt_points)
# Focal classification over all N predictions [§3.4]
cls = focal_loss(pred_conf, labels)
# Both terms are divided by the number of GT instances in the batch [§3.4]
# Weighted sum with lambda = 2.5 [eq. 5]
return cls + 2.5 * locHyperparameters
Values marked "not stated" are absent from the paper. Code-only values are from the official repository at the commit in the frontmatter, and they are not confirmed by the paper.
| Name | Value | Where | Notes |
|---|---|---|---|
| backbone | ResNet-50 | §4.1 | single-scale feature map at stride 32 §3 |
| backbone freezing | conv1 and layer1 frozen; BatchNorm frozen | not stated | backbone.py backbone.py |
| feature channels C | not stated | §3 | |
| projection dim c | 256 | §3.1 | |
| encoder layers | 6 | §4.1 | |
| decoder layers | 6 | §4.1 | |
| attention heads m | 8 | §3.1 | |
| feed-forward width | not stated | code default 2048 config.py | |
| dropout | not stated | code default 0.1 config.py | |
| position embedding | not stated | code default sine config.py | |
| queries N | 500 | §4.1 | code default 500 config.py; NWPU script uses 700 nwpu.sh |
| crop size | 128×128 ShanghaiTech Part A; 256×256 others | §4.1 | code default 256 config.py |
| long-side cap | 2048 px for UCF-QNRF, JHU-Crowd++, NWPU-Crowd; aspect kept | §4.1 | |
| batch size | 16 | §4.1 | config.py |
| optimizer | Adam | §4.1 | train_distributed.py |
| learning rate | 1e-4 | §4.1 | one rate for all parameters in code train_distributed.py |
| weight decay | not stated | code 5e-4 config.py | |
| lr schedule | not stated | code: ×0.1 at milestone 1200, stepped once per epoch train_distributed.py train_distributed.py | |
| epochs | not stated | scripts use 1500 nwpu.sh | |
| random crop | yes, size as above | §4.1 | |
| horizontal flip | p = 0.5 | §4.1 | dataset.py |
| random scale | probability not stated | §4.1 | code p = 0.3 config.py |
| grayscale | not stated | code p = 0.1 default config.py; JHU script p = 0.3 jhu.sh | |
| kNN count k | 4 | §4.1 tab. 9 | code averages 3 neighbours, see Paper vs code dataset.py |
| matching cost weights | unit weights in eq. 2–3 | §3.3 eq. 2 eq. 3 | code: class 2, point 5 config.py |
| loss λ | 2.5 | §3.4 eq. 5 | code: cls weight 2, point weight 5 config.py |
| auxiliary decoder losses | not stated | code on by default config.py | |
| confidence threshold | 0.35 | §4.1 | code default config.py |
| inference patches | non-overlapping, crop-size, zero padding | §4.1 | code pads on left and top dataset.py |
| GT point normalisation | to [0,1] | §3.4 | code divides by crop width dataset.py |
| random seed | not stated | code default 1 config.py | |
| localisation σ | NWPU: √(w²+h²)/2; ShanghaiTech: 4 and 8; UCF-QNRF: 1 to 100 px | §4.3 |
Results
Author-reported. Setup: CLTR uses point labels only, a ResNet-50 at 1/32 resolution, and 500 queries §4.1. Dataset sizes: NWPU-Crowd 3109/500/1500 train/val/test §4.2; UCF-QNRF 1201/334 train/test §4.2; ShanghaiTech Part A 300/182 §4.2; JHU-Crowd++ 2272/500/1600 §4.2. Competitors that use box labels are marked with an asterisk in the paper tab. 1.
Localisation, NWPU-Crowd tab. 1
| Method | Val P | Val R | Val F | Test P | Test R | Test F |
|---|---|---|---|---|---|---|
| Faster RCNN* | 96.4 | 3.8 | 7.3 | 95.8 | 3.5 | 6.7 |
| TinyFaces* | 54.3 | 66.6 | 59.8 | 52.9 | 61.1 | 56.7 |
| TopoCount* | - | - | - | 69.5 | 68.7 | 69.1 |
| GPR | 61.0 | 52.2 | 56.3 | 55.8 | 49.6 | 52.5 |
| RAZ_Loc | 69.2 | 56.9 | 62.5 | 66.6 | 54.3 | 59.8 |
| AutoScale_loc | 70.1 | 63.8 | 66.8 | 67.3 | 57.4 | 62.0 |
| Crowd-SDNet | - | - | - | 65.1 | 62.4 | 63.7 |
| GL | - | - | - | 80.0 | 56.2 | 66.0 |
| CLTR (ours) | 73.9 | 71.3 | 72.6 | 69.4 | 67.6 | 68.5 |
Localisation, UCF-QNRF tab. 2 (averaged over σ = 1 to 100 px)
| Method | Av. Precision | Av. Recall | F1 |
|---|---|---|---|
| CL | 75.80 | 59.75 | 66.82 |
| LCFCN | 77.89 | 52.40 | 62.65 |
| Method in [ribera2019] | 75.46 | 49.87 | 60.05 |
| LSC-CNN | 75.84 | 74.69 | 75.26 |
| AutoScale_loc | 81.31 | 75.75 | 78.43 |
| GL | 78.20 | 74.80 | 76.40 |
| TopoCount | 81.77 | 78.96 | 80.34 |
| CLTR (ours) | 82.22 | 79.75 | 80.97 |
Localisation, ShanghaiTech Part A tab. 3
| Method | σ=4 P | σ=4 R | σ=4 F | σ=8 P | σ=8 R | σ=8 F |
|---|---|---|---|---|---|---|
| LCFCN | 43.3 | 26.0 | 32.5 | 75.1 | 45.1 | 56.3 |
| Method in [ribera2019] | 34.9 | 20.7 | 25.9 | 67.7 | 44.8 | 53.9 |
| LSC-CNN | 33.4 | 31.9 | 32.6 | 63.9 | 61.0 | 62.4 |
| TopoCount | 41.7 | 40.6 | 41.1 | 74.6 | 72.7 | 73.6 |
| CLTR (ours) | 43.6 | 42.7 | 43.2 | 74.9 | 73.5 | 74.2 |
Counting, NWPU-Crowd tab. 4 (position output column: whether the method outputs point positions)
| Method | Positions | Val MAE | Val MSE | Test MAE | Test MSE |
|---|---|---|---|---|---|
| MCNN | no | 218.5 | 700.6 | 232.5 | 714.6 |
| CSRNet | no | 104.8 | 433.4 | 121.3 | 387.8 |
| CAN | no | 93.5 | 489.9 | 106.3 | 386.5 |
| SCAR | no | 81.5 | 397.9 | 110.0 | 495.3 |
| BL | no | 93.6 | 470.3 | 105.4 | 454.2 |
| SFCN | no | 95.4 | 608.3 | 105.4 | 424.1 |
| DM-Count | no | 70.5 | 357.6 | 88.4 | 388.6 |
| RAZ_loc | yes | 128.7 | 665.4 | 151.4 | 634.6 |
| AutoScale_loc | yes | 97.3 | 571.2 | 123.9 | 515.5 |
| TopoCount | yes | - | - | 107.8 | 438.5 |
| GL | yes | - | - | 79.3 | 346.1 |
| CLTR (ours) | yes | 61.9 | 246.3 | 74.3 | 333.8 |
Counting, UCF-QNRF, ShanghaiTech Part A and Part B tab. 5
| Method | Positions | QNRF MAE | QNRF MSE | Part A MAE | Part A MSE | Part B MAE | Part B MSE |
|---|---|---|---|---|---|---|---|
| CSRNet | no | - | - | 68.2 | 115.0 | 10.6 | 16.0 |
| L2SM | no | 104.7 | 173.6 | 64.2 | 98.4 | 7.2 | 11.1 |
| DSSI-Net | no | 99.1 | 159.2 | 60.6 | 96.0 | 6.9 | 10.3 |
| MBTTBF | no | 97.5 | 165.2 | 60.2 | 94.1 | 8.0 | 15.5 |
| BL | no | 88.7 | 154.8 | 62.8 | 101.8 | 7.7 | 12.7 |
| AMSNet | no | 101.8 | 163.2 | 56.7 | 93.4 | 6.7 | 10.2 |
| LibraNet | no | 88.1 | 143.7 | 55.9 | 97.1 | 7.3 | 11.3 |
| KDMG | no | 99.5 | 173.0 | 63.8 | 99.2 | 7.8 | 12.7 |
| NoisyCC | no | 85.8 | 150.6 | 61.9 | 99.6 | 7.4 | 11.3 |
| DM-Count | no | 85.6 | 148.3 | 59.7 | 95.7 | 7.4 | 11.8 |
| CL | yes | 132.0 | 191.0 | - | - | - | - |
| RAZ_loc+ | yes | 118.0 | 198.0 | 71.6 | 120.1 | 9.9 | 15.6 |
| PSDDN | yes | - | - | 65.9 | 112.3 | 9.1 | 14.2 |
| LSC-CNN | yes | 120.5 | 218.2 | 66.4 | 117.0 | 8.1 | 12.7 |
| TopoCount | yes | 89.0 | 159.0 | 61.2 | 104.6 | 7.8 | 13.7 |
| AutoScale_loc | yes | 104.4 | 174.2 | 65.8 | 112.1 | 8.6 | 13.9 |
| GL | yes | 84.3 | 147.5 | 61.3 | 95.4 | 7.3 | 11.7 |
| CLTR (ours) | yes | 85.8 | 141.3 | 56.9 | 95.2 | 6.5 | 10.6 |
Categorical counting, JHU-Crowd++ tab. 6 (Low: 0–50, Medium: 51–500, High: >500 people per image; Weather: degraded images)
| Method | Positions | Low MAE | Low MSE | Med MAE | Med MSE | High MAE | High MSE | Weather MAE | Weather MSE | Overall MAE | Overall MSE |
|---|---|---|---|---|---|---|---|---|---|---|---|
| MCNN | no | 97.1 | 192.3 | 121.4 | 191.3 | 618.6 | 1166.7 | 330.6 | 852.1 | 188.9 | 483.4 |
| CSRNet | no | 27.1 | 64.9 | 43.9 | 71.2 | 356.2 | 784.4 | 141.4 | 640.1 | 85.9 | 309.2 |
| JHU++ | no | 14.0 | 42.8 | 35.0 | 53.7 | 314.7 | 712.3 | 120.0 | 580.8 | 71.0 | 278.6 |
| LSC-CNN | no | 10.6 | 31.8 | 34.9 | 55.6 | 601.9 | 1172.2 | 178.0 | 744.3 | 112.7 | 454.4 |
| BL | no | 10.1 | 32.7 | 34.2 | 54.5 | 352.0 | 768.7 | 140.1 | 675.7 | 75.0 | 299.9 |
| AutoScale_loc | yes | 13.2 | 30.2 | 32.3 | 52.8 | 425.6 | 916.5 | - | - | 85.6 | 356.1 |
| TopoCount | yes | 8.2 | 20.5 | 28.9 | 50.0 | 282.0 | 685.8 | 120.4 | 635.1 | 60.9 | 267.4 |
| GL | yes | - | - | - | - | - | - | - | - | 59.9 | 259.5 |
| CLTR (ours) | yes | 8.3 | 21.8 | 30.7 | 53.8 | 265.2 | 614.0 | 109.5 | 568.5 | 59.5 | 240.6 |
Ablations on UCF-QNRF (the paper runs these on UCF-QNRF only, with one run per setting and no variance reported)
Transformer size tab. 7:
| Layers | N (queries) | P (%) | R (%) | F (%) | MAE | MSE |
|---|---|---|---|---|---|---|
| 3 | 500 | 80.60 | 79.44 | 80.02 | 88.4 | 149.9 |
| 6 | 500 | 82.22 | 79.75 | 80.97 | 85.8 | 141.3 |
| 12 | 500 | 80.82 | 79.41 | 80.11 | 87.7 | 150.3 |
| 6 | 300 | 80.61 | 79.18 | 79.89 | 89.9 | 153.6 |
| 6 | 700 | 81.32 | 79.38 | 80.34 | 86.8 | 146.4 |
Matching cost tab. 8:
| Matching cost | Av. Precision (%) | Av. Recall (%) | F1 (%) | MAE | MSE |
|---|---|---|---|---|---|
| L_m (L1 + confidence) | 80.89 | 79.17 | 80.02 | 91.3 | 157.4 |
| L_m^k (with KMO, ours) | 82.22 | 79.75 | 80.97 | 85.8 | 141.3 |
Number of neighbours k tab. 9:
| k | Av. Precision (%) | Av. Recall (%) | F1 (%) | MAE | MSE |
|---|---|---|---|---|---|
| 3 | 81.46 | 79.19 | 80.31 | 87.1 | 146.8 |
| 4 | 82.22 | 79.75 | 80.97 | 85.8 | 141.3 |
| 5 | 81.52 | 79.34 | 80.42 | 86.9 | 148.1 |
Complexity at 1024×768 on a 3090 GPU tab. 10:
| Method | Parameters (M) | MACs (G) |
|---|---|---|
| LSC-CNN | 35.0 | 1244.3 |
| AutoScale | 24.9 | 1074.6 |
| TopoCount | 25.8 | 797.2 |
| GL | 21.5 | 324.6 |
| CLTR (ours) | 43.4 | 157.2 |
Paper vs code
Official code: dk-liang/CLTR, linked from the project page, owned by the
first author, MIT licence, commit ab1b5e58eec4851cc629379abeeaceff908b48f4.
I searched the project page, then cloned the repository. The paper and the
code disagree in the following ways.
- KMO term is implemented differently. The paper's kNN values are a separate L1 term over the mean neighbour distance, with unit weights, and are predicted by the network §3.3 eq. 3. In the code, the mean kNN distance is appended as a third coordinate of each ground-truth point dataset.py dataset.py. The point head outputs three values conditional_detr.py, and the matcher's L1 cost runs over all three with weight 5 matcher.py matcher.py config.py.
- Class cost differs from eq. 2 and eq. 3. The paper subtracts the predicted confidence Ĉ_j eq. 2 eq. 3. The code instead uses a focal-style cost over the ground-truth class, with weight 2 matcher.py config.py.
- k is effectively 3, not 4. The paper sets k=4 and averages over k neighbours §4.1 eq. 3. The code queries 4 points, drops the first column (the point itself) and averages the rest, so it averages 3 neighbours dataset.py. The paper's sum over k is ambiguous on whether the point itself is counted.
- Regression loss covers the kNN value. The paper's regression loss is over coordinates only eq. 4. The code applies the L1 loss to all three outputs, including the kNN value conditional_detr.py.
- Loss weights. The paper uses weight 1 for classification and λ=2.5 for regression §3.4 eq. 5. The code uses 2 and 5 config.py. The ratio 5:2 equals λ=2.5, but the absolute scale differs.
- Normalisation. The paper normalises both terms by the number of instances in the batch §3.4. The code divides the regression loss by the total ground-truth count across the batch, averaged over processes conditional_detr.py conditional_detr.py. The classification term is further multiplied by the number of queries conditional_detr.py.
- Auxiliary losses. The code adds a loss at every decoder layer by default conditional_detr.py conditional_detr.py config.py. The paper does not mention them.
- Queries. The paper gives N=500 §4.1. The NWPU-Crowd script trains with 700 queries nwpu.sh. Section 5.4.1 reports that 700 queries lower UCF-QNRF performance (MAE 86.8 vs 85.8) tab. 7.
- Augmentation. The paper lists crop, scale and flip §4.1. The code adds grayscale augmentation, which the paper does not mention config.py jhu.sh. The flip probability of 0.5 matches dataset.py.
- Patch padding. The paper says zero padding §4.1. The code pads on the left and top only dataset.py.
- Crop filter. The code rejects training crops with as many or more
ground-truth points than queries, or fewer than
min_num(default -1) dataset.py. The paper states the observation that all patches contain fewer than 500 people §5.4.1 but not this rule. - Normalisation docstring. The criterion's docstring says targets are normalised by image size conditional_detr.py. The data code divides by the crop width dataset.py. The paper says [0,1] §3.4.
- Backbone. The code freezes conv1, layer1 and all BatchNorm layers backbone.py backbone.py. The paper does not state any freezing.
- Single learning rate. The code has an
lr_backboneargument, but the optimiser uses one rate for every parameter train_distributed.py config.py. The paper states one rate and no backbone rate.
Pitfalls
- The kNN statistic is crop-local. Ground-truth points are taken from the training crop only dataset.py, so points near the crop border have fewer neighbours than the same points would have in the full image. The paper does not say whether the kNN values are computed per crop or per image.
- The kNN value is a learned output. Matching depends on a predicted quantity that the network must learn §3.3. The paper does not show how well the kNN head is trained, and the ablation does not separate this from the coordinate head.
- Small point counts use other branches. Crops with 1 to 3 points take separate branches in the kNN code dataset.py. I read only part of them, so check these before reimplementing.
- Abstract vs table. The abstract says the method achieves the best localisation on NWPU-Crowd, but on the NWPU test set TopoCount has a higher F1 (69.1 vs 68.5) tab. 1. The text calls it "competitive" §5.1. TopoCount uses box labels in training.
- Ablations are single runs. The paper gives no seeds or spread. The gap between L_m and L_m^k (MAE 91.3 vs 85.8) is one run tab. 8. The code's default seed is 1 config.py.
- Inference cost. The model has 43.4M parameters, the most in the comparison, though it has the fewest MACs at 1024×768 tab. 10. Matching runs on the CPU with SciPy per image matcher.py, and the cost matrix is moved to CUDA matcher.py.
- Patch borders. Non-overlapping patches with zero padding can split or miss heads at patch borders. The paper names fixed-size crops as the cause of failure on very large heads §5.5 fig. 5.
- Query count caps crop density. Training keeps only crops whose point count is below the query count dataset.py, and the paper picks N=500 §4.1. Table 7 shows 700 queries slightly worse than 500 on UCF-QNRF tab. 7. The paper does not say what to do for crops above N.
- Reported metrics. NWPU-Crowd uses a per-head σ from head size §4.3. The paper does not state how the head width and height were obtained for each annotation.
Lineage
- Builds on: DETR (Carion et al., 2020) for set prediction with a transformer decoder and a Hungarian matcher §2.4 §3.3.
- Builds on: Conditional DETR (Meng et al., 2021) for the conditional cross-attention §3.2.
- Compared against: TopoCount, AutoScale, LSC-CNN, GL, RAZ_loc, CL, and box-based detectors tab. 1 tab. 2 tab. 3.
- Concurrent work: P2PNet, a regression-based crowd localiser that uses 8×W×H point proposals §2.3.
- Superseded by: not known from content in this wiki.
Source and license
Adapted from “An End-to-End Transformer Model for Crowd Localization” by Dingkang Liang, Wei Xu, Xiang Bai (arXiv:2202.13065), used under CC0 1.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. Code citations refer to https://github.com/dk-liang/CLTR at commit ab1b5e58eec4 (MIT). Any quoted code is shown under that license.