Paper · 2019-03-10 · cs.CV
Group-wise Correlation Stereo Network
Builds the stereo matching cost volume by splitting feature channels into groups and correlating each group at every disparity, then refines it with an improved stacked 3D hourglass network. Author-reported: outperforms the compared methods on Scene Flow, KITTI 2012 and KITTI 2015; KITTI 2015 test D1-all 2.11% vs 2.32% for PSMNet.
Cost volumeCost aggregationstereo-matchingdepth-estimationcost-volume
arXivPDFCodeMarkdown13 min read
AI-compiled by claude-haiku-5-5 on 2026-10-24, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.
In plain words
Two cameras side by side see the same scene from slightly different places. How far away a point is can be read from how much it shifts between the two images. Finding those shifts is the hard part.
Many methods compare each point in the left image with candidate points in the right image, shifted by every possible amount, and store the scores in a large three-dimensional table called a cost volume. A neural network then cleans up that table and reads a depth value from it.
This paper changes how the table is filled. Instead of comparing all the features at once, it cuts them into groups of channels, scores each group separately, and stacks the results. Each group gives a different kind of matching evidence, so the network has more to work with and does not have to learn the matching rule from scratch. The authors also modify the network that refines the table, which makes it cheaper to run at inference. They report better accuracy than earlier stereo networks on the Scene Flow and KITTI benchmarks.
TL;DR
- Input: a rectified left and right image pair; output: a disparity map, the horizontal pixel shift per pixel §1.
- Core change: group-wise correlation. The feature channels are split into 40 groups, and each group is correlated at every disparity, instead of one correlation or one concatenation §3.2, eq. 3, §4.3.
- Refinement: a 3D stacked hourglass network with 1×1×1 shortcut convolutions; auxiliary outputs are dropped at inference §3.3, fig. 2.
- Author-reported: better than the compared methods on Scene Flow, KITTI 2012 and KITTI 2015. KITTI 2015 test D1-all is 2.11% against 2.32% for PSMNet §4.6, tab. 4.
Problem
Stereo depth needs a matching score for every left pixel at every candidate disparity. A full correlation gives only one channel per disparity and throws away much of the feature information §2.2, §3.2. Concatenating the two feature maps keeps the information, but the aggregation network must then learn the similarity function from scratch, which costs parameters and computation §2.2, §3.2. Methods that aggregate hand-crafted matching costs cannot be trained end to end §2.2. The paper aims for matching features that keep accuracy when the 3D aggregation network is made smaller §3.2, §4.5.
Method
The pipeline runs in four stages: extract per-pixel features, build the matching volume, refine it in 3D, and read out disparities §3.1, fig. 1.
- Unary features. A ResNet-like extractor from PSMNet, with half dilation and without its spatial pyramid pooling §3.1. The outputs of conv2, conv3 and conv4 are concatenated into 320 channels at 1/4 of the input resolution §3.1, tab. 1.
- Cost volume. Two parts are built and, in the full model, concatenated. The group-wise volume has N_g = 40 channels, one per group [§3.2, tab. 1, §4.3]. The concatenation volume uses features compressed to 12 channels per side by two convolutions, giving 24 channels §3.1, tab. 1. Together they give 64 channels, the GwcNet-gc setting tab. 1.
- 3D aggregation. A pre-hourglass module of four 3D convolutions with batch normalisation and ReLU and a residual add, followed by three stacked 3D hourglass networks §3.1, §3.3, tab. 1, fig. 2.
- Output modules. After the pre-hourglass module and after each hourglass, a 3D convolution produces a one-channel volume. It is upsampled to the full disparity range and turned into probabilities with softmax over disparity. The disparity is the expectation over disparity levels (soft argmin) [§3.4, eq. 4, tab. 1].
- Loss. A weighted sum of smooth L1 losses over the four outputs [§3.4, eq. 5, eq. 6].
Group-wise correlation §3.2, eq. 3. The channels are split evenly into N_g groups. The g-th group of the left features is correlated with the g-th group of the right features shifted by d, and the inner product is divided by the number of channels per group:
C_{gwc}(d,x,y,g)=\frac{1}{N_c/N_g}\left\langle \mathbf{f}_l^g(x,y),\, \mathbf{f}_r^g(x-d,y) \right\rangle eq. 3
Here N_c is the channel count and f^g is group g. The volume has shape [D_max/4, H/4, W/4, N_g] §3.2. With N_g = 1 it reduces to full correlation §3.2.
Soft argmin §3.4, eq. 4, where p_k is the probability of disparity level k:
\widetilde{d}=\sum_{k=0}^{D_{max}-1} k \cdot p_k eq. 4
Loss §3.4, eq. 5, eq. 6. Each output i is weighted by λ_i and compared with the ground truth d* using smooth L1:
L=\sum_{i=0}^{3}\lambda_i \, \text{Smooth}_{L_1}(\widetilde{d}_i-d^*) eq. 5
\text{Smooth}_{L_1}(x)=\begin{cases} 0.5x^2, & |x|<1 \\ |x|-0.5, & \text{otherwise} \end{cases} eq. 6
Changes to the hourglass §3.3, fig. 2, tab. 1:
- An extra auxiliary output module on the pre-hourglass features §3.3.
- The residual connections between output modules are removed, so output modules 0 to 2 can be skipped at inference §3.3, fig. 2.
- 1×1×1 3D convolutions on the shortcut connections inside each hourglass. The paper says these add little computation §3.3, fig. 2, tab. 1.
Group-wise volume, as the code builds it:
# Group-wise correlation volume [§3.2, eq. 3]
# Input: left, right features (B, C, H, W), C = 320, at 1/4 resolution
# Output: volume (B, N_g, D, H, W), with D = D_max / 4
def group_wise_volume(left, right, num_groups, max_disp):
# Channels per group, e.g. 320 / 40 = 8 [§4.3]
group_size = C // num_groups
volume = zeros(B, num_groups, max_disp, H, W)
for d in range(max_disp):
# Pixel x in the left image pairs with x - d in the right [eq. 3]
# Positions with x < d stay zero [code: models/submodule.py#L53-L63]
shifted_right = shift_right(right, d)
# Element-wise product, then mean over each group [eq. 3]
product = left * shifted_right
volume[:, :, d] = mean_per_group(product, num_groups)
return volumeNetwork forward pass, with the concatenation volume enabled §3, fig. 1, tab. 1:
# GwcNet forward pass [§3, fig. 1, tab. 1]
# Input: left, right images (B, 3, H, W)
# Output: one disparity map per output module, each (B, H, W)
def forward(left, right):
# 1. Unary features: conv2, conv3 and conv4 maps concatenated [§3.1]
left_feats = unary_features(left) # (B, 320, H/4, W/4)
right_feats = unary_features(right) # (B, 320, H/4, W/4)
# 2. Group-wise correlation volume with 40 groups [§3.2, eq. 3]
volume = group_wise_volume(left_feats, right_feats, 40, D_max // 4)
# 3. Concatenation volume from 12-channel features [§3.1, tab. 1]
small_left = compress_to_12(left_feats)
small_right = compress_to_12(right_feats)
volume = concat(volume, concat_volume(small_left, small_right))
# (B, 64, D/4, H/4, W/4)
# 4. Pre-hourglass module with residual add [§3.1, tab. 1]
cost0 = pre_hourglass(volume)
# 5. Three stacked hourglasses, each fed by the previous one [§3.3]
cost1 = hourglass(cost0)
cost2 = hourglass(cost1)
cost3 = hourglass(cost2)
# 6. Output module per stage: score, upsample, softmax, soft argmin
# [§3.4, eq. 4]
disparities = []
for cost in [cost0, cost1, cost2, cost3]:
scores = output_module(cost) # (B, 1, D/4, H/4, W/4)
scores = upsample(scores, D_max, H, W) # (B, 1, D, H, W)
probs = softmax(scores, over_disparity) # (B, D, H, W)
disparities.append(soft_argmin(probs)) # [eq. 4]
# At inference only the last output is returned [§3.3]
return disparitiesOne 3D hourglass §3.3, tab. 1, fig. 2:
# One hourglass with 1x1x1 shortcuts [§3.3, tab. 1, fig. 2]
# Input: x (B, C, D, H, W), with C = 32 [tab. 1]
# Each 3D conv is followed by batch norm; ReLU where tab. 1 says so
def hourglass(x):
# Encoder: stride-2 convolutions to 2C, then 4C channels [tab. 1]
enc1 = relu(conv3d(x, 2 * C, stride=2))
enc1_b = relu(conv3d(enc1, 2 * C))
enc2 = relu(conv3d(enc1_b, 4 * C, stride=2))
enc2_b = relu(conv3d(enc2, 4 * C))
# Decoder: transposed convs plus 1x1x1 shortcuts [tab. 1]
dec1 = relu(deconv3d(enc2_b, 2 * C) + shortcut(enc1_b))
return relu(deconv3d(dec1, C) + shortcut(x))Training loss:
# Training loss [§3.4, eq. 5, eq. 6]
# Input: four predicted disparity maps, ground truth d_star (B, H, W)
# Output: scalar loss
def loss(disparities, d_star):
# Weights lambda_0 to lambda_3 [§4.2]
weights = [0.5, 0.5, 0.7, 1.0]
# Not in the paper: only pixels with 0 < d_star < D_max count
# [code: main.py#L149]
valid = (d_star > 0) & (d_star < D_max)
total = 0
for prediction, weight in zip(disparities, weights):
# Smooth L1 averaged over valid pixels [eq. 6]
# [code: models/loss.py#L4-L9]
diff = prediction[valid] - d_star[valid]
total = total + weight * smooth_l1_mean(diff)
return totalHyperparameters
| Name | Value | Where | Notes |
|---|---|---|---|
| Optimizer | Adam, β1 = 0.9, β2 = 0.999 | §4.2 main.py | No weight decay in code; paper silent |
| Batch size | 16 in total, 2 per GPU on 8 GPUs | §4.2 main.py | Hardware: 8 Nvidia TITAN Xp §4.2 |
| Scene Flow epochs | 16 | §4.2 sceneflow.sh | |
| Scene Flow learning rate | 0.001; halved after epochs 10, 12 and 14, ending at 0.000125 | §4.2 experiment.py sceneflow.sh | Code's extra entry at 16 has no effect in 16 epochs |
| KITTI fine-tuning epochs | 300, starting from the Scene Flow checkpoint | §4.2 kitti15.sh | |
| KITTI learning rate | 0.001; divided by 10 after epoch 200 | §4.2 kitti15.sh | |
| Max disparity, Scene Flow | D_max = 192 | §4.2 main.py | |
| Max disparity, KITTI | not stated | main.py | Code default is 192 and the scripts do not override it |
| Number of groups N_g | 40, so 8 channels per group | §4.3 gwcnet.py | Hard-coded; not a command-line flag |
| Unary feature channels | 320 (conv2 + conv3 + conv4) | §3.1 tab. 1 gwcnet.py | |
| Channels per side, concatenation volume | 12, from two convolutions | §3.1 tab. 1 gwcnet.py | |
| Volume channels | 40 (group-wise); 40 + 24 = 64 (with concatenation) | tab. 1 gwcnet.py | |
| Hourglass base channels | 32 | tab. 1 gwcnet.py | |
| Loss weights λ0 to λ3 | 0.5, 0.5, 0.7, 1.0 | §4.2 loss.py | |
| Smooth L1 threshold | 1 | eq. 6 loss.py | |
| Disparity regression | soft argmin over 0 to D_max − 1 | eq. 4 submodule.py | |
| Training crop, Scene Flow | random 512×256 | sceneflow_dataset.py | Not in paper |
| Training crop, KITTI | random 512×256 | kitti_dataset.py | Not in paper |
| Test input, Scene Flow | full 960×540 per paper; code uses bottom-right 960×512 | §4.2 sceneflow_dataset.py | See Paper vs code |
| Test input, KITTI | zero-pad top and right to 1248×384 | §4.2 kitti_dataset.py | |
| Valid-image filter | skip images with under 10% valid pixels | §4.2 metrics.py | |
| D1 outlier rule | error > max(3 px, 0.05 d*) | §4.1 metrics.py | |
| Weight initialisation | He-style normal for convolutions; BN weight 1, bias 0 | gwcnet.py | Paper silent |
| KITTI splits | 2015: 180 train, 20 validation; 2012: 180 train, 14 validation | §4.6 kitti15_val.txt |
Results
Author-reported. Setup: Scene Flow Finalpass, with the ablations trained by the authors at their batch size §4.1, tab. 2. KITTI test numbers come from the evaluation server §4.6. Timing is for 480×640 inputs on one Nvidia TITAN Xp tab. 2.
Scene Flow ablation tab. 2. "Base" removes the stacked hourglasses §4.3.
| Model | >1px (%) | >2px (%) | >3px (%) | EPE (px) | Time (ms) |
|---|---|---|---|---|---|
| Cat64-Base | 12.78 | 8.05 | 6.33 | 1.308 | 117.1 |
| Gwc1-Base (1×320) | 13.32 | 8.37 | 6.62 | 1.369 | 104.0 |
| Gwc10-Base (10×32) | 11.82 | 7.31 | 5.70 | 1.230 | 112.8 |
| Gwc20-Base (20×16) | 11.84 | 7.29 | 5.67 | 1.216 | 116.3 |
| Gwc40-Base (40×8) | 11.68 | 7.18 | 5.58 | 1.212 | 122.2 |
| Gwc80-Base (80×4) | 11.69 | 7.17 | 5.57 | 1.214 | 133.3 |
| Gwc160-Base (160×2) | 11.58 | 7.08 | 5.49 | 1.188 | 157.3 |
| Gwc40-Cat24-Base | 11.26 | 6.87 | 5.31 | 1.127 | 135.1 |
| PSMNet (retrained by the authors) | 9.46 | 5.19 | 3.80 | 0.887 | 246.1 |
| Cat64-original-hg | 9.47 | 5.13 | 3.74 | 0.876 | 241.0 |
| Cat64 (with the new hourglass) | 8.41 | 4.63 | 3.41 | 0.808 | 198.3 |
| Gwc40 (GwcNet-g) | 8.18 | 4.57 | 3.39 | 0.792 | 200.3 |
| Gwc40-Cat24 (GwcNet-gc) | 8.03 | 4.47 | 3.30 | 0.765 | 210.7 |
KITTI validation tab. 3.
| Model | KITTI 12 EPE (px) | KITTI 12 D1-all (%) | KITTI 15 EPE (px) | KITTI 15 D1-all (%) |
|---|---|---|---|---|
| PSMNet | 0.713 | 2.53 | 0.639 | 1.50 |
| Cat64-original-hg | 0.740 | 2.72 | 0.652 | 1.76 |
| Cat64 | 0.691 | 2.41 | 0.615 | 1.55 |
| Gwc40 | 0.662 | 2.30 | 0.602 | 1.41 |
| Gwc40-Cat24 | 0.659 | 2.10 | 0.613 | 1.49 |
KITTI 2015 test tab. 4. D1 outlier percentages, with time in seconds.
| Method | All D1-bg | All D1-fg | All D1-all | Noc D1-bg | Noc D1-fg | Noc D1-all | Time (s) |
|---|---|---|---|---|---|---|---|
| DispNetC | 4.32 | 4.41 | 4.34 | 4.11 | 3.72 | 4.05 | 0.06 |
| GC-Net | 2.21 | 6.16 | 2.87 | 2.02 | 5.58 | 2.61 | 0.9 |
| CRL | 2.48 | 3.59 | 2.67 | 2.32 | 3.12 | 2.45 | 0.47 |
| iResNet-i2e2 | 2.14 | 3.45 | 2.36 | 1.94 | 3.20 | 2.15 | 0.22 |
| PSMNet | 1.86 | 4.62 | 2.32 | 1.71 | 4.31 | 2.14 | 0.41 |
| SegStereo | 1.88 | 4.07 | 2.25 | 1.76 | 3.70 | 2.08 | 0.6 |
| GwcNet-g (Gwc40) | 1.74 | 3.93 | 2.11 | 1.61 | 3.49 | 1.92 | 0.32 |
KITTI 2012 test tab. 5. Noc and All variants, with time in seconds.
| Method | >2px Noc | >2px All | >3px Noc | >3px All | >5px Noc | >5px All | Mean err Noc (px) | Mean err All (px) | Time (s) |
|---|---|---|---|---|---|---|---|---|---|
| DispNetC | 7.38 | 8.11 | 4.11 | 4.65 | 2.05 | 2.39 | 0.9 | 1.0 | 0.06 |
| MC-CNN-acrt | 3.90 | 5.45 | 2.43 | 3.63 | 1.64 | 2.39 | 0.7 | 0.9 | 67 |
| GC-Net | 2.71 | 3.46 | 1.77 | 2.30 | 1.12 | 1.46 | 0.6 | 0.7 | 0.9 |
| iResNet-i2 | 2.69 | 3.34 | 1.71 | 2.16 | 1.06 | 1.32 | 0.5 | 0.6 | 0.12 |
| SegStereo | 2.66 | 3.19 | 1.68 | 2.03 | 1.00 | 1.21 | 0.5 | 0.6 | 0.6 |
| PSMNet | 2.44 | 3.01 | 1.49 | 1.89 | 0.90 | 1.15 | 0.5 | 0.6 | 0.41 |
| GwcNet-gc (Gwc40-Cat24) | 2.16 | 2.71 | 1.32 | 1.70 | 0.80 | 1.03 | 0.5 | 0.5 | 0.32 |
Other results, in words. The group count sweep §4.3, tab. 2 improves up to about 40 groups, after which the EPE stays near 1.2 px. Figure 3 shows the gap between the group-wise model (Gwc-Cat) and the concatenation-only model (Cat) widening as the 3D network's channel count shrinks §4.5, fig. 3. The visualisations of depth, prediction and error maps are in fig. 4. The paper does not give a numeric summary for them.
Paper vs code
Code is at commit 0987074f48b098c6e975a58ca5aacf233b0fb0eb. It was found via
the link in the abstract, and its owner is the first author.
- Scene Flow test crop. The paper feeds the full 960×540 images §4.2. The code crops the bottom-right 960×512 region, dropping the top 28 rows of a 540-row image sceneflow_dataset.py. The reported numbers therefore come from a slightly smaller test input than the text says.
- ReLU after the second pre-hourglass convolution. The paper says the pre-hourglass module has four 3D convolutions with BN and ReLU §3.1, and the footnote of tab. 1 says every 3D convolution has ReLU unless marked otherwise. The code's second block has no ReLU after its second convolution, and its residual sum is not followed by one gwcnet.py gwcnet.py.
- First pre-hourglass convolution. Tab. 1 lists it as 32×32, but its input is the 64-channel volume. The code maps 64 to 32 channels gwcnet.py.
- Loss mask. Eq. 5 has no validity mask. The code trains only on pixels with 0 < d* < D_max main.py and averages the smooth L1 loss over those pixels loss.py. Paper and code differ here.
- Training crops. Both datasets are randomly cropped to 512×256 during training sceneflow_dataset.py kitti_dataset.py. The paper does not state this.
- Dilation. The paper says "half dilation settings" §3.1. The code dilates only the last residual stage, with dilation 2 [code: models/gwcnet.py#L27]. This is the code's reading of the phrase.
- Initialisation and weight decay. Not in the paper. The code uses He-style normal initialisation gwcnet.py and no weight decay main.py.
- Number of groups. Fixed at 40 in the model gwcnet.py and not exposed as a flag, so changing it means editing the code.
- Inference outputs. The paper says output modules 0 to 2 can be dropped at inference §3.3. The code does this gwcnet.py. No discrepancy.
- Learning-rate schedule. Matches the text; the code halves cumulatively experiment.py. No discrepancy.
No discrepancies were found for the group-wise correlation formula, the soft argmin, the loss weights, the smooth L1 definition, the concatenation channel counts, or the D1 outlier rule.
Pitfalls
- Mean, not sum. Eq. 3 divides by the channels per group (8 here), so the correlation scale depends on group size, not on the number of groups §3.2, eq. 3 submodule.py. Use the mean.
- Zeros outside the image. For disparities with x < d, the group-wise and concatenation volumes stay at zero, and nothing masks them out before aggregation submodule.py submodule.py. The paper does not describe this case.
- Upsampling is unspecified. The paper says "upsampled" §3.4. The code uses trilinear interpolation and does not set an alignment option gwcnet.py. The paper states neither the mode nor the alignment.
- Table 4 key. The PSMNet row of tab. 4 cites
kendall2017end, the key the text uses for GC-Net §2.2, tab. 4. The paper does not say which reference the row is meant to carry. - Loss scale. The paper does not say whether smooth L1 is summed or averaged over pixels. The code averages over the valid pixels per output loss.py. The result depends on this choice.
- Wording vs table. §4.4 says the new hourglass "increases EPE by 7.8%" on Scene Flow. Tab. 2 shows EPE falling from 0.876 to 0.808, which is a reduction, so the table is the reliable source §4.4, tab. 2.
- Retrained baseline. The PSMNet row in tab. 2 is the authors' retrain with their batch size and evaluation settings, not the published PSMNet result tab. 2 caption.
- Validation noise. The authors say the KITTI validation results are unstable, so they fine-tuned three times and kept the best model §4.6. The selection procedure is not in the code.
- Hidden costs. The volume has N_g channels over D_max/4 disparity slices at 1/4 resolution §3.2. For D_max = 192 that is 48 slices. The paper gives timings only for 480×640 inputs on one TITAN Xp tab. 2, and memory use is not reported.
- Gwc1 is full correlation. Gwc1-Base (1 group × 320 channels) is full correlation in the paper's own terms §3.2, so it is the reference point of the group-count sweep tab. 2.
Lineage
- Builds on: PSMNet [chang2018pyramid], whose feature extractor and 3D stacked hourglass the paper modifies. The paper does not give an arXiv id, and the PSMNet card is not in this wiki §3.1, §3.3. GC-Net [kendall2017end] for the soft argmin and the concatenation volume §2.2, §3.4. DispNetC [mayer2016large] for full correlation §2.2.
- Compared against: DispNetC, GC-Net, CRL, iResNet, PSMNet, SegStereo and MC-CNN-acrt tab. 4, tab. 5.
- Superseded by: not stated in this wiki.
Source and license
Adapted from “Group-wise Correlation Stereo Network” by Xiaoyang Guo, Kai Yang, Wukui Yang, Xiaogang Wang, Hongsheng Li (arXiv:1903.04025), used under CC BY 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. Code citations refer to https://github.com/xy-guo/GwcNet at commit 0987074f48b0 (MIT). Any quoted code is shown under that license.