Paper · 2023-12-01 · cs.CV
FSGS: Real-Time Few-shot View Synthesis using Gaussian Splatting
Fits 3D Gaussian Splatting from three posed photos. Proximity-guided Gaussian unpooling adds Gaussians between sparse neighbours, and pseudo views with a monocular-depth correlation loss curb overfitting. Author-reported: 20.31 dB PSNR on LLFF at 1/8 resolution with 458 FPS, against 19.86 dB and 0.21 FPS for SparseNeRF.
Gaussian splattingNovel view synthesisMonocular depth estimationnovel-view-synthesisgaussian-splattingfew-shotdepth-regularizationreal-time-rendering
arXivPDFCodeMarkdown12 min read
AI-compiled by claude-haiku-5-5 on 2026-10-31, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.
In plain words
Gaussian splatting draws a scene as a cloud of soft blobs and renders it fast. It normally needs many photos. With only three photos from different places, the starting cloud is far too sparse, and the blobs learn to fit those three photos exactly and then look wrong from new angles.
FSGS makes the cloud grow where it is thin. Each blob looks at its nearest neighbours. Where a blob is far from its neighbours, the method places new blobs halfway along the links to them. The method also invents extra camera positions near the real ones and asks that the scene's depth look plausible from those positions too. The depth comes from a pretrained model that guesses depth from a single picture. Its guesses are used only for their shape, not their absolute scale.
For an engineer the useful parts are the growth rule, the extra views, and the depth loss. The card lists the values the paper gives and the places where the paper leaves a value out or the equations do not say which way to go.
TL;DR
FSGS is a Gaussian-splatting method for novel view synthesis from as few as three training views. It starts from a sparse COLMAP point cloud, adds Gaussians by proximity-guided unpooling, and regularises depth on training and synthesised pseudo views with a monocular depth prior. The authors report 20.31 dB PSNR on LLFF at 1/8 resolution with 458 FPS. Official code exists but is licensed for non-commercial research only, so this card does not use it (see Paper vs code).
Problem
Three-view novel view synthesis from 3D Gaussian Splatting (3D-GS) fails for two reasons. First, the 3D-GS densification rule adds Gaussians where view-space position gradients are large, which is noisy with few views, and 3D-GS depends on the quantity and accuracy of its initial SfM points §3.1. Second, with insufficient parallax the Gaussians can grow very large and overfit the training views §3.1, fig. 3. The paper targets real-time rendering, which NeRF-based few-shot methods do not reach §1.
Method
The pipeline runs in four parts §3, fig. 2. The numbering of equations follows the order in the LaTeX source.
1. Initialisation §3, app. Initialization. Camera poses and a sparse point cloud come from COLMAP using the training views only. Each point starts one Gaussian. Spherical-harmonic (SH) coefficients start at degree 0, the remaining SH coefficients and the rotation are set to 0, opacity starts at 0.1, and the scale is set to the average distance between points app. Initialization.
2. Proximity graph and unpooling §3.2, fig. 4, alg. 1.
- Build a directed graph: each Gaussian links to its K nearest neighbours by Euclidean distance between centres. K = 3 §3.2.
- Distances: d_{ij} = \lVert \mu_i - \mu_j \rVert §3.2. The neighbour set is D_i^K = K\text{-min}_{j \neq i}\, d_{ij} eq. 3.
- Proximity score: P_i = \frac{1}{K}\sum_{j=1}^{K} D_i^K, the mean distance to the K neighbours eq. 4.
- If P_i exceeds t_\text{prox}, place one new Gaussian at the midpoint of each edge from the source Gaussian to a destination neighbour §3.2. The new Gaussian copies the scale and opacity of the destination. Its rotation and SH coefficients are set to 0 §3.2.
- The graph is rebuilt after each densification or pruning step §3.2.
3. Pseudo views §3.3, eq. 5, alg. 1. A pseudo camera is made from two nearby training cameras. Its rotation is the average of their rotations (as a quaternion). Its location is the average of their locations plus noise, \mathbf{t}' = \mathbf{t} + \varepsilon with \varepsilon \sim \mathcal{N}(0, \delta) on the three translation degrees of freedom eq. 5. Pseudo views are used only after 2,000 iterations §4.1, alg. 1.
4. Depth correlation loss §3.3, eqs. 6–7. A pretrained monocular depth model predicts a depth map for each training image and for each rendered pseudo view §3.3, app. Training. The rendered depth is the alpha-blended z-buffer of the Gaussians eq. 7. The loss compares the two depth maps with the Pearson correlation eq. 6. Correlation is used because it ignores the unknown absolute scale of the prior §3.3. For Blender scenes, correlation is computed only on pixels whose depth is greater than 0 app. Training.
The total loss, with weights found by grid search §3.4, eq. 8:
\mathcal{L} = \lambda_1 \lVert C - \hat C \rVert_1 + \lambda_2\, \mathrm{D\text{-}SSIM}(C, \hat C) + \lambda_3 \lVert \mathrm{Corr}(D_\text{ras}, D_\text{est}) \rVert_1 eq. 8
with \lambda_1 = 0.8, \lambda_2 = 0.2, \lambda_3 = 0.05 §3.4. Algorithm 1 adds a fourth term, \lambda_4 times the Pearson loss on pseudo views, but the paper does not state \lambda_4 alg. 1.
# FSGS training loop [alg. 1] [§3.4] [app. Training]
# Input: training images, poses, SfM points; depth model E
# Output: Gaussians G that render novel views
def train(images, poses, sfm_points, depth_model):
# 1. Monocular depth for each training image [alg. 1]
depth_priors = [depth_model(image) for image in images]
# 2. Pseudo camera poses from pairs of training poses [§3.3] [eq. 5]
pseudo_poses = make_pseudo_poses(poses) # count and delta not stated
# 3. Start one Gaussian per SfM point [app. Initialization]
gaussians = init_gaussians(sfm_points)
for iteration in range(10000): # [§4.1]
# 4. Photometric and depth loss on a training view [eq. 8]
view = sample_training_view()
rgb, depth = render(gaussians, view)
loss = 0.8 * l1(rgb, view.image) # lambda_1 [§3.4]
loss += 0.2 * dssim(rgb, view.image) # lambda_2 [§3.4]
prior = depth_priors[view.index]
loss += 0.05 * depth_corr(depth, prior) # sign: see Pitfalls
# 5. Pseudo-view depth term, after 2000 iterations [§4.1] [alg. 1]
if iteration > 2000:
pseudo = sample(pseudo_poses)
rgb_p, depth_p = render(gaussians, pseudo)
# The prior is computed from the rendered pseudo image [alg. 1]
prior_p = depth_model(rgb_p)
# lambda_4 is not stated in the paper
loss += lambda_4 * depth_corr(depth_p, prior_p)
# 6. Densify, prune and unpool every 100 steps after step 500 [§4.1]
if iteration > 500 and iteration % 100 == 0:
# Remove Gaussians by the rule as written in alg. 1 (see Pitfalls)
prune(gaussians)
# Standard 3D-GS gradient-based densification [§3.1] [alg. 1]
densify(gaussians)
# Score = mean distance to 3 nearest neighbours [§3.2] [eq. 4]
scores = mean_knn_distance(gaussians.means, k=3)
for source in gaussians where scores > 10: # t_prox = 10 [§4.1]
for destination in neighbours(source):
# New Gaussian at the edge midpoint [§3.2]
new = midpoint(source, destination)
new.scale = destination.scale
new.opacity = destination.opacity
new.rotation = 0 # convention not stated
new.sh = 0
add(gaussians, new)
# 7. Opacity reset to 0.05 at 2000, 5000 and 7000 [app. Training]
if iteration in (2000, 5000, 7000):
gaussians.opacity = 0.05
# 8. SH degree rises from 0 to 4, one step every 500 steps [app.]
gaussians.sh_degree = min(iteration // 500, 4)
update(gaussians, loss) # per-parameter learning rates [app. Training]
return gaussiansLearning rates from app. Training: position 0.00016, SH 0.0025, opacity 0.05, scale 0.005, rotation 0.001. The paper states no learning-rate decay.
Hyperparameters
| Name | Value | Where | Notes |
|---|---|---|---|
| Training steps | 10,000 | §4.1 | About 9.5 min on LLFF, about 24 min on Mip-NeRF360 §4.1 |
| Densification interval | every 100 steps | §4.1 | Starts after step 500 §4.1 |
| Proximity neighbours K | 3 | §3.2 | |
| Proximity threshold t_\text{prox} | 10 | §4.1 | Units not stated; depends on scene scale |
| Pseudo views start | iteration 2,000 | §4.1 alg. 1 | |
| Loss weights \lambda_1, \lambda_2, \lambda_3 | 0.8, 0.2, 0.05 | §3.4 | Grid search; no sensitivity study |
| Loss weight \lambda_4 (pseudo views) | not stated | alg. 1 | |
| Opacity init | 0.1 | app. Initialization | |
| Opacity reset | 0.05 at 2,000, 5,000, 7,000 | app. Training | |
| SH degree schedule | 0 to 4, +1 every 500 steps | app. Training | |
| Learning rate: position | 0.00016 | app. Training | No decay stated |
| Learning rate: SH | 0.0025 | app. Training | |
| Learning rate: opacity | 0.05 | app. Training | |
| Learning rate: scale | 0.005 | app. Training | |
| Learning rate: rotation | 0.001 | app. Training | |
| Gradient threshold t_\text{pos} | not stated | alg. 1 | |
| Prune threshold \varepsilon | not stated | alg. 1 | |
| Pseudo-view noise \delta | not stated | eq. 5 | |
| Pseudo views per step | not stated | alg. 1 | One sampled per step in alg. 1 |
| Depth model | DPT (main text); MiDaS v2 link (appendix) | §3.3 app. Training | See Pitfalls |
| Input resolution, LLFF | 1/8 (504×378) and 1/4 (1008×756) | §4.1 tab. 1 | See Pitfalls on order |
| Input resolution, Blender | 400×400 | §4.1 | |
| Training views | 3 (LLFF, Shiny); 8 (Blender); 24 (Mip-NeRF360) | §4.1 | Mip-NeRF360 is not few-shot |
| Hardware | NVIDIA A6000 | §4.1 | Stated for all results |
Results
Author-reported. Setup in §4.1. Test images on LLFF and Mip-NeRF360 are every eighth image. FPS was measured on the A6000 for all methods §4.1.
LLFF, 3 training views tab. 1
| Method | 1/8 FPS | 1/8 PSNR | 1/8 SSIM | 1/8 LPIPS | 1/4 FPS | 1/4 PSNR | 1/4 SSIM | 1/4 LPIPS |
|---|---|---|---|---|---|---|---|---|
| Mip-NeRF | 0.21 | 16.11 | 0.401 | 0.460 | 0.14 | 15.22 | 0.351 | 0.540 |
| 3D-GS | 385 | 17.43 | 0.522 | 0.321 | 312 | 16.94 | 0.488 | 0.402 |
| DietNeRF | 0.14 | 14.94 | 0.370 | 0.496 | 0.08 | 13.86 | 0.305 | 0.578 |
| RegNeRF | 0.21 | 19.08 | 0.587 | 0.336 | 0.14 | 18.06 | 0.535 | 0.411 |
| FreeNeRF | 0.21 | 19.63 | 0.612 | 0.308 | 0.14 | 18.73 | 0.562 | 0.384 |
| SparseNeRF | 0.21 | 19.86 | 0.624 | 0.328 | 0.14 | 19.07 | 0.564 | 0.401 |
| FSGS (this paper) | 458 | 20.31 | 0.652 | 0.288 | 351 | 19.88 | 0.612 | 0.340 |
Gaussian counts on LLFF: 57,513 for FSGS against 63,219 for 3D-GS §4.2.
Mip-NeRF360, 24 training views tab. 2
| Method | 1/8 FPS | 1/8 PSNR | 1/8 SSIM | 1/8 LPIPS | 1/4 FPS | 1/4 PSNR | 1/4 SSIM | 1/4 LPIPS |
|---|---|---|---|---|---|---|---|---|
| Mip-NeRF360 | 0.12 | 21.23 | 0.613 | 0.351 | 0.07 | 19.78 | 0.530 | 0.431 |
| 3D-GS | 223 | 20.89 | 0.633 | 0.317 | 145 | 19.93 | 0.588 | 0.401 |
| DietNeRF | 0.05 | 20.21 | 0.557 | 0.387 | 0.03 | 19.11 | 0.482 | 0.452 |
| RegNeRF | 0.07 | 22.19 | 0.643 | 0.335 | 0.04 | 20.55 | 0.546 | 0.398 |
| FreeNeRF | 0.07 | 22.78 | 0.689 | 0.323 | 0.04 | 21.04 | 0.587 | 0.377 |
| SparseNeRF | 0.07 | 22.85 | 0.693 | 0.315 | 0.04 | 21.13 | 0.600 | 0.389 |
| FSGS (this paper) | 290 | 23.70 | 0.745 | 0.220 | 203 | 22.82 | 0.693 | 0.293 |
Blender (8 views, 400×400) and Shiny (3 views) tab. 3
| Method | Blender FPS | Blender PSNR | Blender SSIM | Blender LPIPS | Shiny FPS | Shiny PSNR | Shiny SSIM | Shiny LPIPS |
|---|---|---|---|---|---|---|---|---|
| Mip-NeRF | 0.22 | 20.89 | 0.830 | 0.168 | 0.19 | 17.37 | 0.525 | 0.432 |
| 3D-GS | 332 | 21.56 | 0.847 | 0.130 | 316 | 17.83 | 0.547 | 0.385 |
| DietNeRF | 0.14 | 22.50 | 0.823 | 0.124 | 0.16 | 17.67 | 0.546 | 0.403 |
| RegNeRF | 0.22 | 23.86 | 0.852 | 0.105 | 0.19 | 18.10 | 0.574 | 0.378 |
| FreeNeRF | 0.22 | 24.26 | 0.883 | 0.098 | 0.19 | 18.65 | 0.586 | 0.360 |
| SparseNeRF | 0.22 | 24.04 | 0.876 | 0.113 | 0.19 | 18.81 | 0.591 | 0.354 |
| FSGS (this paper) | 467 | 24.64 | 0.895 | 0.095 | 341 | 19.63 | 0.612 | 0.327 |
Training views, LLFF at 1/8 resolution app. Effects of Training Views, tab. 6. PSNR / SSIM / LPIPS at 3, 6 and 9 views:
| Method | PSNR 3 / 6 / 9 | SSIM 3 / 6 / 9 | LPIPS 3 / 6 / 9 |
|---|---|---|---|
| Mip-NeRF | 16.10 / 22.91 / 24.88 | 0.401 / 0.756 / 0.826 | 0.460 / 0.213 / 0.160 |
| 3D-GS | 17.43 / 22.87 / 24.65 | 0.522 / 0.732 / 0.813 | 0.321 / 0.204 / 0.169 |
| DietNeRF | 14.94 / 21.75 / 24.28 | 0.370 / 0.717 / 0.801 | 0.496 / 0.248 / 0.183 |
| RegNeRF | 19.08 / 23.10 / 24.86 | 0.587 / 0.760 / 0.820 | 0.336 / 0.206 / 0.161 |
| FreeNeRF | 19.63 / 23.73 / 25.13 | 0.612 / 0.779 / 0.827 | 0.308 / 0.195 / 0.160 |
| SparseNeRF | 19.86 / 23.64 / 24.97 | 0.624 / 0.784 / 0.834 | 0.328 / 0.202 / 0.158 |
| FSGS (this paper) | 20.31 / 24.55 / 25.89 | 0.652 / 0.795 / 0.845 | 0.288 / 0.177 / 0.143 |
Mobile-phone captures (iPhone 15 Pro, 3 training views, 3 scenes) app. FSGS on Mobile Phones Data, tab. 7. 1/8 FPS, PSNR, SSIM, LPIPS | 1/4 FPS, PSNR, SSIM, LPIPS:
| Method | 1/8 FPS | 1/8 PSNR | 1/8 SSIM | 1/8 LPIPS | 1/4 FPS | 1/4 PSNR | 1/4 SSIM | 1/4 LPIPS |
|---|---|---|---|---|---|---|---|---|
| Mip-NeRF | 0.07 | 14.74 | 0.337 | 0.602 | 0.14 | 13.84 | 0.284 | 0.631 |
| 3D-GS | 225 | 16.29 | 0.408 | 0.439 | 127 | 15.83 | 0.413 | 0.530 |
| DietNeRF | 0.05 | 13.62 | 0.263 | 0.598 | 0.08 | 12.57 | 0.228 | 0.722 |
| RegNeRF | 0.07 | 17.41 | 0.517 | 0.440 | 0.14 | 16.44 | 0.443 | 0.504 |
| FreeNeRF | 0.07 | 18.07 | 0.497 | 0.426 | 0.14 | 17.14 | 0.462 | 0.509 |
| SparseNeRF | 0.07 | 18.79 | 0.539 | 0.441 | 0.04 | 17.82 | 0.472 | 0.524 |
| FSGS (this paper) | 263 | 19.54 | 0.539 | 0.403 | 190 | 18.41 | 0.493 | 0.471 |
Ablation on LLFF, 3 views, 1/8 resolution tab. 4
| Gaussian unpooling | Depth regularisation | Pseudo views | PSNR | SSIM | LPIPS |
|---|---|---|---|---|---|
| no | no | no | 17.43 | 0.522 | 0.321 |
| yes | no | no | 18.64 | 0.580 | 0.311 |
| yes | yes | no | 19.83 | 0.634 | 0.297 |
| yes | yes | yes | 20.31 | 0.652 | 0.288 |
Depth estimator on LLFF, 1/8 resolution tab. 5. The table header says "FPS ↓", but the text reads higher FPS as better; the values below are as printed.
| Method | FPS | PSNR | SSIM | LPIPS |
|---|---|---|---|---|
| SparseNeRF | 0.21 | 19.86 | 0.624 | 0.328 |
| 3D-GS | 385 | 17.43 | 0.522 | 0.321 |
| FSGS, MiDaS small | 446 | 20.17 | 0.647 | 0.294 |
| FSGS, DPT Hybrid | 460 | 20.29 | 0.652 | 0.290 |
| FSGS, DPT Large | 458 | 20.31 | 0.652 | 0.288 |
| FSGS, DepthAnything | 468 | 20.37 | 0.654 | 0.289 |
Headline numbers: 2,180× the FPS of SparseNeRF on LLFF at 1/8 §4.2, tab. 1; 4,142× on Mip-NeRF360 at 1/8 tab. 2; a 0.85 dB PSNR gain over SparseNeRF on Mip-NeRF360 §4.2.
Paper vs code
No code comparison was made. The authors' repository is VITA-Group/FSGS, linked from the project page; its LICENSE.md is the Gaussian-Splatting License, which permits research use only and prohibits commercial use without the licensors' consent. wikillm publishes with advertising, so this card uses the paper alone and does not read or cite the repository's code. Only the repository's LICENSE.md was read, and the code was not used. The paper's own appendix conflicts on the depth model (see Pitfalls), and the released code could settle that question for someone with a research licence.
Pitfalls
- Resolution order is inverted in the text. §4.1 says 4× and 8× downsampling give "504×378 and 1008×756 respectively". The table captions and columns put 504×378 at "1/8 Resolution" tab. 1, which is the correct reading for a 4032×3024 original. Use 1/8 = 504×378 and 1/4 = 1008×756.
- Mip-NeRF360 is not few-shot. It uses 24 training views §4.1. The "203 FPS" real-time figure in the introduction §1 is the Mip-NeRF360 1/4-resolution number tab. 2.
- The regularisation term's sign is unclear. Eq. 8 writes \lambda_3 \lVert \mathrm{Corr} \rVert_1. As written, minimising this drives the correlation to zero, the opposite of the stated aim of aligning depth structure §3.3. The paper never states the sign or whether a term such as 1 - \mathrm{Corr} was meant. Choose a form that is maximised at correlation 1, and test it on a short run.
- \lambda_4 is missing. Algorithm 1 uses a pseudo-view weight but the paper never gives its value alg. 1.
- The pseudo-view depth term uses the rendered image. Algorithm 1 computes the prior for a pseudo view from the rendered pseudo RGB image, not from ground truth alg. 1. The algorithm's pseudo-view Pearson term also indexes the rendered depth with i where the pseudo view is j. Read it as comparing the depth of the pseudo view with the prior of that same view.
- Depth model is stated two ways. The main text names DPT §3.3, §4.1. The appendix links the PyTorch Hub MiDaS v2 model and says the estimator outputs inverse depth app. Training. The DPT Large row in tab. 5 matches the main-table numbers, which suggests DPT Large was used. Inverse depth is compared with rendered z-depth eq. 7 with no conversion stated.
- Pruning rule as printed looks reversed. Algorithm 1 removes a Gaussian when \alpha_i > \varepsilon alg. 1. Removing high-opacity Gaussians is unusual, and the appendix's opacity resets suggest low-opacity removal was meant. \varepsilon is not given.
IsTooLargeandNoProximityare not defined, and the proximity-triggered unpooling is namedNoProximityin the algorithm but described by threshold in §3.2. - Proximity threshold has no units. t_\text{prox} = 10 §4.1 is in scene units. It depends on the COLMAP scale of each scene, which the paper does not normalise. Expect to re-tune it when the scene scale changes.
- "Rotation set to 0" is not a valid quaternion. §3.2 and app. Initialization set rotation to 0. A zero quaternion is not a rotation. The convention (identity, (1,0,0,0), or a different ordering) is not stated.
- Opacity is reset to 0.05 but initialised at 0.1. app. Initialization and app. Training give different values. The paper does not say whether the new unpooled Gaussians keep the destination's opacity after resets.
- Gradient threshold and density controls are not given. The value t_\text{pos} for densification alg. 1 and the densify/split rule are not stated beyond "standard 3D-GS densification" §3.1. The paper gives no learning-rate decay.
- Pseudo-view count and noise are not given. The number of pseudo views and the variance \delta in eq. 5 are not stated §3.3.
- Blender depth loss masks pixels. Correlation is computed only where depth is greater than 0 app. Training. The paper does not say how the mask affects the other datasets.
- Baseline training recipe. For 3D-GS, the paper follows its original dense-view recipe §4.1. Whether the 3-view 3D-GS numbers used that recipe unchanged is not stated.
- Small text mismatches. §4.2 says 0.40 dB over FreeNeRF on Blender, but tab. 3 gives 24.64 − 24.26 = 0.38 dB. The mobile-phone claim of "over 0.75" dB app. equals 0.75 exactly.
- Rebuttal material is not in the paper. The LaTeX source includes a rebuttal file with full-resolution and 1/2-resolution LLFF results and extra baselines. It is not part of the published paper, so its numbers are not used here.
- Cost. Each step renders the pseudo view and runs the depth model on it, so pseudo views add a depth-model call per step after iteration 2,000. The paper reports the wall-clock times above but not the share of this cost.
Lineage
- Builds on: 3D Gaussian Splatting, arXiv:2308.04079 §3.1. FSGS keeps its rasteriser and its densification as a base, and adds unpooling, pseudo views and the depth term §3.
- Depth prior: DPT, Ranftl et al. 2021, arXiv:2103.13413 §3.3.
- Compared against: Mip-NeRF, DietNeRF, RegNeRF, FreeNeRF and SparseNeRF §4.2. Concurrent work SparseGS and DRGS is described only in the rebuttal and is not in the paper.
- Superseded by: not stated in this wiki.
Source and license
Adapted from “FSGS: Real-Time Few-shot View Synthesis using Gaussian Splatting” by Zehao Zhu, Zhiwen Fan, Yifan Jiang, Zhangyang Wang (arXiv:2312.00451), 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/VITA-Group/FSGS at commit a536a64c5b36 (Gaussian-Splatting License (non-commercial research only)). Any quoted code is shown under that license.